From 6429088b6d53de0c2fd59e1fa7d7d4793d011a70 Mon Sep 17 00:00:00 2001 From: Colin Alston Date: Thu, 11 Apr 2019 15:27:45 +0100 Subject: [PATCH 1/8] Add a method to authenticate without a config file Signed-off-by: Chris Halbert --- google/google.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/google/google.go b/google/google.go index d765ca5..7b7bcc5 100644 --- a/google/google.go +++ b/google/google.go @@ -74,6 +74,19 @@ func Setup(redirectURL, credFile string, scopes []string, secret []byte) { } } +// Setup the authorization path without a config file +func SetupFromString(redirectURL, clientID string, clientSecret string, scopes []string, secret []byte) { + store = sessions.NewCookieStore(secret) + + conf = &oauth2.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + RedirectURL: redirectURL, + Scopes: scopes, + Endpoint: google.Endpoint, + } +} + func Session(name string) gin.HandlerFunc { return sessions.Sessions(name, store) } From 3c2d74de0b5e2e14d31939a5bccea9c17608d2be Mon Sep 17 00:00:00 2001 From: Colin Alston Date: Thu, 11 Apr 2019 17:21:39 +0100 Subject: [PATCH 2/8] Redirect to login page on auth failure Signed-off-by: Chris Halbert --- google/google.go | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/google/google.go b/google/google.go index 7b7bcc5..7898bb1 100644 --- a/google/google.go +++ b/google/google.go @@ -10,6 +10,7 @@ import ( "encoding/gob" "encoding/json" "fmt" + "net/http" "os" @@ -44,6 +45,8 @@ func init() { gob.Register(goauth.Userinfo{}) } +var loginURL string + func randToken() string { b := make([]byte, 32) if _, err := rand.Read(b); err != nil { @@ -75,8 +78,9 @@ func Setup(redirectURL, credFile string, scopes []string, secret []byte) { } // Setup the authorization path without a config file -func SetupFromString(redirectURL, clientID string, clientSecret string, scopes []string, secret []byte) { - store = sessions.NewCookieStore(secret) +func SetupFromString(redirectURL, cLoginURL string, clientID string, clientSecret string, scopes []string, secret []byte) { + store = cookie.NewStore(secret) + loginURL = cLoginURL conf = &oauth2.Config{ ClientID: clientID, @@ -116,26 +120,26 @@ func GetLoginURL(state string) string { // Auth is the google authorization middleware. You can use them to protect a routergroup. // Example: // -// private.Use(google.Auth()) -// private.GET("/", UserInfoHandler) -// private.GET("/api", func(ctx *gin.Context) { -// ctx.JSON(200, gin.H{"message": "Hello from private for groups"}) -// }) +// private.Use(google.Auth()) +// private.GET("/", UserInfoHandler) +// private.GET("/api", func(ctx *gin.Context) { +// ctx.JSON(200, gin.H{"message": "Hello from private for groups"}) +// }) // -// // Requires google oauth pkg to be imported as `goauth "google.golang.org/api/oauth2/v2"` -// func UserInfoHandler(ctx *gin.Context) { -// var ( -// res goauth.Userinfo -// ok bool -// ) +// // Requires google oauth pkg to be imported as `goauth "google.golang.org/api/oauth2/v2"` +// func UserInfoHandler(ctx *gin.Context) { +// var ( +// res goauth.Userinfo +// ok bool +// ) // -// val := ctx.MustGet("user") -// if res, ok = val.(goauth.Userinfo); !ok { -// res = goauth.Userinfo{Name: "no user"} -// } +// val := ctx.MustGet("user") +// if res, ok = val.(goauth.Userinfo); !ok { +// res = goauth.Userinfo{Name: "no user"} +// } // -// ctx.JSON(http.StatusOK, gin.H{"Hello": "from private", "user": res.Email}) -// } +// ctx.JSON(http.StatusOK, gin.H{"Hello": "from private", "user": res.Email}) +// } func Auth() gin.HandlerFunc { return func(ctx *gin.Context) { // Handle the exchange code to initiate a transport. @@ -150,7 +154,11 @@ func Auth() gin.HandlerFunc { retrievedState := session.Get(stateKey) if retrievedState != ctx.Query(stateKey) { - ctx.AbortWithError(http.StatusUnauthorized, fmt.Errorf("invalid session state: %s", retrievedState)) + if loginURL != "" { + ctx.Redirect(302, loginURL) + } else { + ctx.AbortWithError(http.StatusUnauthorized, fmt.Errorf("invalid session state: %s", retrievedState)) + } return } From 78c98f3ea1a286a4400d067ecdcdd7b1b3393fcf Mon Sep 17 00:00:00 2001 From: Chris Halbert Date: Mon, 15 Jan 2024 20:00:34 -0500 Subject: [PATCH 3/8] Modify setup from stirng and include test. Signed-off-by: Chris Halbert --- google/google.go | 3 +-- google/google_test.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 google/google_test.go diff --git a/google/google.go b/google/google.go index 7898bb1..2fe71ec 100644 --- a/google/google.go +++ b/google/google.go @@ -78,9 +78,8 @@ func Setup(redirectURL, credFile string, scopes []string, secret []byte) { } // Setup the authorization path without a config file -func SetupFromString(redirectURL, cLoginURL string, clientID string, clientSecret string, scopes []string, secret []byte) { +func SetupFromString(redirectURL, clientID string, clientSecret string, scopes []string, secret []byte) { store = cookie.NewStore(secret) - loginURL = cLoginURL conf = &oauth2.Config{ ClientID: clientID, diff --git a/google/google_test.go b/google/google_test.go new file mode 100644 index 0000000..f0070d1 --- /dev/null +++ b/google/google_test.go @@ -0,0 +1,19 @@ +package google + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSetupFromString(t *testing.T) { + t.Run("should assign store and config accordingly", func(t *testing.T) { + store = nil + conf = nil + SetupFromString("http://fake.fake", "clientid", "clientsecret", []string{}, []byte("secret")) + assert.NotNil(t, conf) + assert.NotNil(t, store) + assert.Equal(t, conf.ClientID, "clientid") + assert.Equal(t, conf.ClientSecret, "clientsecret") + }) +} From 520614c8fbe4f8fc5f1e378edb443417a988cea2 Mon Sep 17 00:00:00 2001 From: Chris Halbert Date: Mon, 15 Jan 2024 20:10:14 -0500 Subject: [PATCH 4/8] Add with login url. Signed-off-by: Chris Halbert --- google/google.go | 4 ++++ google/google_test.go | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/google/google.go b/google/google.go index 2fe71ec..2ba0a26 100644 --- a/google/google.go +++ b/google/google.go @@ -116,6 +116,10 @@ func GetLoginURL(state string) string { return conf.AuthCodeURL(state) } +func WithLoginURL(url string) { + loginURL = url +} + // Auth is the google authorization middleware. You can use them to protect a routergroup. // Example: // diff --git a/google/google_test.go b/google/google_test.go index f0070d1..e9c2a90 100644 --- a/google/google_test.go +++ b/google/google_test.go @@ -17,3 +17,13 @@ func TestSetupFromString(t *testing.T) { assert.Equal(t, conf.ClientSecret, "clientsecret") }) } + +func TestWithLoginURL(t *testing.T) { + t.Run("should assign the login url", func(t *testing.T) { + loginURL = "" + url := "http://fake.fake" + WithLoginURL(url) + assert.NotEmpty(t, url) + assert.Equal(t, url, loginURL) + }) +} From 0d3b4b8b5c3cb1d2837e47bab95304f926be3307 Mon Sep 17 00:00:00 2001 From: Chris Halbert Date: Mon, 15 Jan 2024 21:26:10 -0500 Subject: [PATCH 5/8] Godocicize comment. Signed-off-by: Chris Halbert --- google/google.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/google.go b/google/google.go index 2ba0a26..430ec2e 100644 --- a/google/google.go +++ b/google/google.go @@ -77,7 +77,7 @@ func Setup(redirectURL, credFile string, scopes []string, secret []byte) { } } -// Setup the authorization path without a config file +// SetupFromString accepts string values for ouath2 Configs func SetupFromString(redirectURL, clientID string, clientSecret string, scopes []string, secret []byte) { store = cookie.NewStore(secret) From f83e7e1b6be6d2260d91ade8561cb1a17361b049 Mon Sep 17 00:00:00 2001 From: Chris Halbert Date: Fri, 19 Jan 2024 20:58:05 -0500 Subject: [PATCH 6/8] Add light url validation and sanitization with tests. Signed-off-by: Chris Halbert --- google/google.go | 13 ++++++++++--- google/google_test.go | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/google/google.go b/google/google.go index 430ec2e..4a43271 100644 --- a/google/google.go +++ b/google/google.go @@ -10,9 +10,10 @@ import ( "encoding/gob" "encoding/json" "fmt" - "net/http" + "net/url" "os" + "strings" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" @@ -116,8 +117,14 @@ func GetLoginURL(state string) string { return conf.AuthCodeURL(state) } -func WithLoginURL(url string) { - loginURL = url +func WithLoginURL(s string) error { + s = strings.TrimSpace(s) + url, err := url.ParseRequestURI(s) + if err != nil { + return err + } + loginURL = url.String() + return nil } // Auth is the google authorization middleware. You can use them to protect a routergroup. diff --git a/google/google_test.go b/google/google_test.go index e9c2a90..16aecb1 100644 --- a/google/google_test.go +++ b/google/google_test.go @@ -19,11 +19,38 @@ func TestSetupFromString(t *testing.T) { } func TestWithLoginURL(t *testing.T) { - t.Run("should assign the login url", func(t *testing.T) { - loginURL = "" - url := "http://fake.fake" - WithLoginURL(url) - assert.NotEmpty(t, url) - assert.Equal(t, url, loginURL) - }) + + var testCases = []struct { + description string + urlParm string + expectUrlLogin string + isErrNil bool + }{ + { + description: "should assign a valid url without error", + urlParm: "http://fake.fake", + expectUrlLogin: "http://fake.fake", + isErrNil: true, + }, + { + description: "should assign a sanitizable url without error", + urlParm: " http://fake.fake ", + expectUrlLogin: "http://fake.fake", + isErrNil: true, + }, + { + description: "should not assign an invalid url without error", + urlParm: "not a parseable url", + expectUrlLogin: "", + isErrNil: false, + }, + } + for _, testCase := range testCases { + t.Run(testCase.description, func(t *testing.T) { + loginURL = "" + err := WithLoginURL(testCase.urlParm) + assert.Equal(t, testCase.expectUrlLogin, loginURL) + assert.Equal(t, testCase.isErrNil, err == nil) + }) + } } From 653d40b98c0c7e648a5d2a8684f5e0c0eb3240b2 Mon Sep 17 00:00:00 2001 From: Chris Halbert Date: Fri, 19 Jan 2024 21:00:13 -0500 Subject: [PATCH 7/8] Fix description Signed-off-by: Chris Halbert --- google/google_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/google_test.go b/google/google_test.go index 16aecb1..7b220a0 100644 --- a/google/google_test.go +++ b/google/google_test.go @@ -39,7 +39,7 @@ func TestWithLoginURL(t *testing.T) { isErrNil: true, }, { - description: "should not assign an invalid url without error", + description: "should not assign an invalid url, and should return an error", urlParm: "not a parseable url", expectUrlLogin: "", isErrNil: false, From 634cbb0e3eee9e989f4b4952c4759d4ebd70a062 Mon Sep 17 00:00:00 2001 From: Chris Halbert Date: Fri, 19 Jan 2024 22:22:21 -0500 Subject: [PATCH 8/8] Fix linting issue. Signed-off-by: Chris Halbert --- google/google_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/google/google_test.go b/google/google_test.go index 7b220a0..cb887f2 100644 --- a/google/google_test.go +++ b/google/google_test.go @@ -23,25 +23,25 @@ func TestWithLoginURL(t *testing.T) { var testCases = []struct { description string urlParm string - expectUrlLogin string + expectURLLogin string isErrNil bool }{ { description: "should assign a valid url without error", urlParm: "http://fake.fake", - expectUrlLogin: "http://fake.fake", + expectURLLogin: "http://fake.fake", isErrNil: true, }, { description: "should assign a sanitizable url without error", urlParm: " http://fake.fake ", - expectUrlLogin: "http://fake.fake", + expectURLLogin: "http://fake.fake", isErrNil: true, }, { description: "should not assign an invalid url, and should return an error", urlParm: "not a parseable url", - expectUrlLogin: "", + expectURLLogin: "", isErrNil: false, }, } @@ -49,7 +49,7 @@ func TestWithLoginURL(t *testing.T) { t.Run(testCase.description, func(t *testing.T) { loginURL = "" err := WithLoginURL(testCase.urlParm) - assert.Equal(t, testCase.expectUrlLogin, loginURL) + assert.Equal(t, testCase.expectURLLogin, loginURL) assert.Equal(t, testCase.isErrNil, err == nil) }) }