From 572e71d76ab644b524baf8bec3c9c0b84d64ebc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8E=E6=B9=9B?= Date: Fri, 7 Jan 2022 17:10:21 +0800 Subject: [PATCH] add CookieOptions add UnitTest --- net/ghttp/ghttp_server_config.go | 4 +- net/ghttp/ghttp_server_cookie.go | 43 +++++------ net/ghttp/ghttp_z_unit_feature_config_test.go | 12 ++-- net/ghttp/ghttp_z_unit_feature_cookie_test.go | 71 +++++++++++++++++-- 4 files changed, 94 insertions(+), 36 deletions(-) diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 85370b71a..edb955c40 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -147,7 +147,7 @@ type ServerConfig struct { // CookieSameSite specifies cookie SameSite property. // It also affects the default storage for session id. - CookieSameSite string `json:"sameSite"` + CookieSameSite string `json:"cookieSameSite"` // CookieSameSite specifies cookie Secure property. // It also affects the default storage for session id. @@ -155,7 +155,7 @@ type ServerConfig struct { // CookieSameSite specifies cookie HttpOnly property. // It also affects the default storage for session id. - CookieHttpOnly bool `json:"CookieHttpOnly"` + CookieHttpOnly bool `json:"cookieHttpOnly"` // ====================================================================================================== // Session. diff --git a/net/ghttp/ghttp_server_cookie.go b/net/ghttp/ghttp_server_cookie.go index d9b7ac440..b37492a4c 100644 --- a/net/ghttp/ghttp_server_cookie.go +++ b/net/ghttp/ghttp_server_cookie.go @@ -7,7 +7,6 @@ package ghttp import ( - "github.com/gogf/gf/v2/util/gconv" "net/http" "time" @@ -22,6 +21,13 @@ type Cookie struct { response *Response // Belonged HTTP response. } +// CookieOptions provides security config for cookies +type CookieOptions struct { + sameSite http.SameSite // cookie SameSite property + secure bool // cookie Secure property + httpOnly bool // cookie HttpOnly property +} + // cookieItem is the item stored in Cookie. type cookieItem struct { *http.Cookie // Underlying cookie items. @@ -89,10 +95,10 @@ func (c *Cookie) Set(key, value string) { c.request.Server.GetCookieDomain(), c.request.Server.GetCookiePath(), c.request.Server.GetCookieMaxAge(), - map[string]interface{}{ - "sameSite": c.request.Server.GetCookieSameSite(), - "secure": c.request.Server.GetCookieSecure(), - "httpOnly": c.request.Server.GetCookieHttpOnly(), + CookieOptions{ + sameSite: c.request.Server.GetCookieSameSite(), + secure: c.request.Server.GetCookieSecure(), + httpOnly: c.request.Server.GetCookieHttpOnly(), }, ) } @@ -100,25 +106,20 @@ func (c *Cookie) Set(key, value string) { // SetCookie sets cookie item with given domain, path and expiration age. // The optional parameter `httpOnly` specifies if the cookie item is only available in HTTP, // which is usually empty. -func (c *Cookie) SetCookie(key, value, domain, path string, maxAge time.Duration, extra ...map[string]interface{}) { +func (c *Cookie) SetCookie(key, value, domain, path string, maxAge time.Duration, options ...CookieOptions) { c.init() - isHttpOnly := false - sameSite := http.SameSiteDefaultMode - secure := false - if len(extra) > 0 { - config := extra[0] - isHttpOnly = gconv.Bool(config["httpOnly"]) - sameSite = http.SameSite(gconv.Int(config["sameSite"])) - secure = gconv.Bool(config["secure"]) + config := CookieOptions{} + if len(options) > 0 { + config = options[0] } httpCookie := &http.Cookie{ Name: key, Value: value, Path: path, Domain: domain, - HttpOnly: isHttpOnly, - SameSite: sameSite, - Secure: secure, + HttpOnly: config.httpOnly, + SameSite: config.sameSite, + Secure: config.secure, } if maxAge != 0 { httpCookie.Expires = time.Now().Add(maxAge) @@ -149,10 +150,10 @@ func (c *Cookie) SetSessionId(id string) { c.request.Server.GetCookieDomain(), c.request.Server.GetCookiePath(), c.server.GetSessionCookieMaxAge(), - map[string]interface{}{ - "sameSite": c.request.Server.GetCookieSameSite(), - "secure": c.request.Server.GetCookieSecure(), - "httpOnly": c.request.Server.GetCookieHttpOnly(), + CookieOptions{ + sameSite: c.request.Server.GetCookieSameSite(), + secure: c.request.Server.GetCookieSecure(), + httpOnly: c.request.Server.GetCookieHttpOnly(), }, ) } diff --git a/net/ghttp/ghttp_z_unit_feature_config_test.go b/net/ghttp/ghttp_z_unit_feature_config_test.go index 271225de2..78bd844e3 100644 --- a/net/ghttp/ghttp_z_unit_feature_config_test.go +++ b/net/ghttp/ghttp_z_unit_feature_config_test.go @@ -28,9 +28,9 @@ func Test_ConfigFromMap(t *testing.T) { "indexFiles": g.Slice{"index.php", "main.php"}, "errorLogEnabled": true, "cookieMaxAge": "1y", - "sameSite": "lax", + "cookieSameSite": "lax", "cookieSecure": true, - "CookieHttpOnly": true, + "cookieHttpOnly": true, } config, err := ghttp.ConfigFromMap(m) t.Assert(err, nil) @@ -41,9 +41,9 @@ func Test_ConfigFromMap(t *testing.T) { t.Assert(config.CookieMaxAge, d2) t.Assert(config.IndexFiles, m["indexFiles"]) t.Assert(config.ErrorLogEnabled, m["errorLogEnabled"]) - t.Assert(config.CookieSameSite, m["sameSite"]) + t.Assert(config.CookieSameSite, m["cookieSameSite"]) t.Assert(config.CookieSecure, m["cookieSecure"]) - t.Assert(config.CookieHttpOnly, m["CookieHttpOnly"]) + t.Assert(config.CookieHttpOnly, m["cookieHttpOnly"]) }) } @@ -60,9 +60,9 @@ func Test_SetConfigWithMap(t *testing.T) { "SessionIdName": "MySessionId", "SessionPath": "/tmp/MySessionStoragePath", "SessionMaxAge": 24 * time.Hour, - "sameSite": "lax", + "cookieSameSite": "lax", "cookieSecure": true, - "CookieHttpOnly": true, + "cookieHttpOnly": true, } s := g.Server() err := s.SetConfigWithMap(m) diff --git a/net/ghttp/ghttp_z_unit_feature_cookie_test.go b/net/ghttp/ghttp_z_unit_feature_cookie_test.go index e087ca88e..e9d30ff9c 100644 --- a/net/ghttp/ghttp_z_unit_feature_cookie_test.go +++ b/net/ghttp/ghttp_z_unit_feature_cookie_test.go @@ -9,6 +9,7 @@ package ghttp_test import ( "fmt" "net/http" + "strings" "testing" "time" @@ -105,14 +106,70 @@ func Test_SetHttpCookie(t *testing.T) { }) } -func Test_CookieSameSite(t *testing.T) { - // todo: 补充测试 +func Test_CookieOptionsDefault(t *testing.T) { + p, _ := ports.PopRand() + s := g.Server(p) + s.BindHandler("/test", func(r *ghttp.Request) { + r.Cookie.Set(r.Get("k").String(), r.Get("v").String()) + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + client := g.Client() + client.SetBrowserMode(true) + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + r1, e1 := client.Get(ctx, "/test?k=key1&v=100") + if r1 != nil { + defer r1.Close() + } + + t.Assert(e1, nil) + t.Assert(r1.ReadAllString(), "") + + parts := strings.Split(r1.Header.Get("Set-Cookie"), "; ") + + t.AssertEQ(len(parts), 3) + }) } -func Test_CookieSecure(t *testing.T) { - // todo: 补充测试 -} +func Test_CookieOptions(t *testing.T) { + p, _ := ports.PopRand() + s := g.Server(p) + s.SetConfigWithMap(g.Map{ + "cookieSameSite": "lax", + "cookieSecure": true, + "cookieHttpOnly": true, + }) + s.BindHandler("/test", func(r *ghttp.Request) { + r.Cookie.Set(r.Get("k").String(), r.Get("v").String()) + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() -func Test_CookieHttpOnly(t *testing.T) { - // todo: 补充测试 + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + client := g.Client() + client.SetBrowserMode(true) + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + r1, e1 := client.Get(ctx, "/test?k=key1&v=100") + if r1 != nil { + defer r1.Close() + } + + t.Assert(e1, nil) + t.Assert(r1.ReadAllString(), "") + + parts := strings.Split(r1.Header.Get("Set-Cookie"), "; ") + + t.AssertEQ(len(parts), 6) + t.Assert(parts[3], "HttpOnly") + t.Assert(parts[4], "Secure") + t.Assert(parts[5], "SameSite=Lax") + }) }