From e955a9fef0e284b545d4eddf3d3416843ebb3d5b Mon Sep 17 00:00:00 2001 From: wsalles Date: Thu, 6 Jan 2022 15:48:20 -0300 Subject: [PATCH 1/5] feat: implement allowlist to skip authentication closes [PLT-125] --- cmd/main.go | 5 ++-- go.mod | 2 +- go.sum | 4 ++-- internal/clients/oidc.go | 13 ++++++---- .../rest_controller_configuration.go | 1 + internal/controllers/rest_controller.go | 24 +++++++++++++++++-- 6 files changed, 38 insertions(+), 11 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 2440689..4b2386e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -43,8 +43,6 @@ func main() { logger.Info("Starting: Authentication Service") oidcClient := clients.NewOAuth2OIDCClient(logger, oidcClientConfiguration) - // Start OIDC Provider setup. - oidcClient.StartSetup() // Initializes a storage to save temporary sessions configured with TTL. sessionStorage := storages.NewSessionStorage() @@ -66,6 +64,9 @@ func main() { restController.Boot(httpServer) httpServer.Run(context.Background()) + // Start OIDC Provider setup. + oidcClient.StartSetup() + // HealthCheck httpServer.AddHealthz() httpServer.AddReadyz(func() bool { return true }) diff --git a/go.mod b/go.mod index ca1f961..c8d4a97 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.2.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/stretchr/testify v1.7.0 - github.com/ydataai/go-core v0.2.1 + github.com/ydataai/go-core v0.3.2 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 ) diff --git a/go.sum b/go.sum index 143dfe8..2863650 100644 --- a/go.sum +++ b/go.sum @@ -471,8 +471,8 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/ydataai/go-core v0.2.1 h1:ZQoL04MyUa+3Qsrzzfqbg2g2NpmIeiBjCElgr3bceu8= -github.com/ydataai/go-core v0.2.1/go.mod h1:tdL95U51Wzr0GJvYsVXk4oNXz7P0NiVxc574NnhcLq4= +github.com/ydataai/go-core v0.3.2 h1:7blaiWGeCk02i25L6QHCvf7ggb7i1KCD4eBzlfKDYfE= +github.com/ydataai/go-core v0.3.2/go.mod h1:zrMbN0hjPUPjs3O+z3yKwNtvsl9ioPhhn8aSBfcJ0Jw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/internal/clients/oidc.go b/internal/clients/oidc.go index bf4271d..c80d7c9 100644 --- a/internal/clients/oidc.go +++ b/internal/clients/oidc.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "time" "github.com/coreos/go-oidc/v3/oidc" "github.com/ydataai/authentication-service/internal/configurations" @@ -44,11 +45,15 @@ func (oc *OAuth2OIDCClient) StartSetup() { var err error ctx := context.Background() - oc.provider, err = oidc.NewProvider(ctx, oc.configuration.OIDProviderURL) - if err != nil { - oc.logger.Fatalf("✖️ OIDC Provider setup failed. Error: %v", err) + for { + oc.provider, err = oidc.NewProvider(ctx, oc.configuration.OIDProviderURL) + if err == nil { + oc.logger.Info("✔️ Connected to OIDC Provider") + break + } + oc.logger.Errorf("✖️ OIDC Provider setup failed. Error: %v", err) + time.Sleep(time.Second * 5) } - oc.logger.Info("✔️ Connected to OIDC Provider") // Configure an OpenID Connect aware OAuth2 client. oc.oauth2config = &oauth2.Config{ diff --git a/internal/configurations/rest_controller_configuration.go b/internal/configurations/rest_controller_configuration.go index 8c8f92b..0015081 100644 --- a/internal/configurations/rest_controller_configuration.go +++ b/internal/configurations/rest_controller_configuration.go @@ -14,6 +14,7 @@ type RESTControllerConfiguration struct { HTTPRequestTimeout time.Duration `envconfig:"HTTP_REQUEST_TIMEOUT" default:"30s"` UserIDHeader string `envconfig:"USER_ID_HEADER" default:"userid"` CookieMaxAge int `envconfig:"COOKIE_MAX_AGE" default:"86400"` + AllowlistURL []string `envconfig:"ALLOWLIST_URL" default:"/dex" split_words:"true"` } // LoadFromEnvVars reads all env vars. diff --git a/internal/controllers/rest_controller.go b/internal/controllers/rest_controller.go index 7299941..d5604ed 100644 --- a/internal/controllers/rest_controller.go +++ b/internal/controllers/rest_controller.go @@ -3,7 +3,9 @@ package controllers import ( "context" "encoding/json" + "fmt" "net/http" + "strings" "time" "github.com/gin-gonic/gin" @@ -42,9 +44,13 @@ func NewRESTController( // Boot initializes creating some routes. func (rc RESTController) Boot(s *server.Server) { - s.Router.GET(rc.configuration.AuthServiceURL, gin.WrapF(rc.CheckForAuthentication)) + s.Router.Use(rc.allowlistMiddleware()) + + s.Router.GET("/", gin.WrapF(rc.CheckForAuthentication)) + s.Router.GET("/:path", gin.WrapF(rc.CheckForAuthentication)) + s.Router.Any("/:path/*sources", gin.WrapF(rc.CheckForAuthentication)) s.Router.GET(rc.configuration.OIDCCallbackURL, gin.WrapF(rc.OIDCProviderCallback)) - s.Router.GET(rc.configuration.LogoutURL, gin.WrapF(rc.Logout)) + s.Router.POST(rc.configuration.LogoutURL, gin.WrapF(rc.Logout)) } // CheckForAuthentication is responsible for knowing if the user already has a valid credential or not. @@ -186,3 +192,17 @@ func (rc RESTController) forbiddenResponse(w http.ResponseWriter, err error) { } json.NewEncoder(w).Encode(jsonBody) } + +// allowlistMiddleware is a middleware that allows all requests that match the allowlist. +func (rc RESTController) allowlistMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + for _, allow := range rc.configuration.AllowlistURL { + pathPrefix := fmt.Sprintf("/%s", strings.TrimPrefix(allow, "/")) + if strings.HasPrefix(c.Request.URL.Path, pathPrefix) { + rc.logger.Infof("URL %s is allowlisted. Accepted without authorization.", c.Request.URL.Path) + c.AbortWithStatus(http.StatusOK) + } + } + c.Next() + } +} From e8fe82956ed82677e263542c3dda7c141e27aa37 Mon Sep 17 00:00:00 2001 From: wsalles Date: Tue, 11 Jan 2022 15:30:14 -0300 Subject: [PATCH 2/5] refactoring: update go-core v0.4.0 --- cmd/main.go | 5 ++--- go.mod | 2 +- go.sum | 4 ++-- internal/clients/oidc.go | 13 ++++--------- internal/controllers/rest_controller.go | 7 ++++--- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 4b2386e..e66557f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -62,10 +62,9 @@ func main() { httpServer := server.NewServer(logger, serverConfiguration) restController.Boot(httpServer) - httpServer.Run(context.Background()) - // Start OIDC Provider setup. - oidcClient.StartSetup() + // Run HTTP Server and start setup the OIDC Provider. + httpServer.Run(context.Background(), oidcClient.StartSetup) // HealthCheck httpServer.AddHealthz() diff --git a/go.mod b/go.mod index c8d4a97..aa27fcc 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.2.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/stretchr/testify v1.7.0 - github.com/ydataai/go-core v0.3.2 + github.com/ydataai/go-core v0.4.0 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 ) diff --git a/go.sum b/go.sum index 2863650..680dd60 100644 --- a/go.sum +++ b/go.sum @@ -471,8 +471,8 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/ydataai/go-core v0.3.2 h1:7blaiWGeCk02i25L6QHCvf7ggb7i1KCD4eBzlfKDYfE= -github.com/ydataai/go-core v0.3.2/go.mod h1:zrMbN0hjPUPjs3O+z3yKwNtvsl9ioPhhn8aSBfcJ0Jw= +github.com/ydataai/go-core v0.4.0 h1:ZkI09itxvi+q5ctboTAvQWALS7lWjdedM29a+k4Vu0c= +github.com/ydataai/go-core v0.4.0/go.mod h1:zrMbN0hjPUPjs3O+z3yKwNtvsl9ioPhhn8aSBfcJ0Jw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/internal/clients/oidc.go b/internal/clients/oidc.go index c80d7c9..bf4271d 100644 --- a/internal/clients/oidc.go +++ b/internal/clients/oidc.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "time" "github.com/coreos/go-oidc/v3/oidc" "github.com/ydataai/authentication-service/internal/configurations" @@ -45,15 +44,11 @@ func (oc *OAuth2OIDCClient) StartSetup() { var err error ctx := context.Background() - for { - oc.provider, err = oidc.NewProvider(ctx, oc.configuration.OIDProviderURL) - if err == nil { - oc.logger.Info("✔️ Connected to OIDC Provider") - break - } - oc.logger.Errorf("✖️ OIDC Provider setup failed. Error: %v", err) - time.Sleep(time.Second * 5) + oc.provider, err = oidc.NewProvider(ctx, oc.configuration.OIDProviderURL) + if err != nil { + oc.logger.Fatalf("✖️ OIDC Provider setup failed. Error: %v", err) } + oc.logger.Info("✔️ Connected to OIDC Provider") // Configure an OpenID Connect aware OAuth2 client. oc.oauth2config = &oauth2.Config{ diff --git a/internal/controllers/rest_controller.go b/internal/controllers/rest_controller.go index d5604ed..7c97186 100644 --- a/internal/controllers/rest_controller.go +++ b/internal/controllers/rest_controller.go @@ -46,11 +46,12 @@ func NewRESTController( func (rc RESTController) Boot(s *server.Server) { s.Router.Use(rc.allowlistMiddleware()) - s.Router.GET("/", gin.WrapF(rc.CheckForAuthentication)) - s.Router.GET("/:path", gin.WrapF(rc.CheckForAuthentication)) - s.Router.Any("/:path/*sources", gin.WrapF(rc.CheckForAuthentication)) + s.Router.GET(rc.configuration.AuthServiceURL, gin.WrapF(rc.CheckForAuthentication)) s.Router.GET(rc.configuration.OIDCCallbackURL, gin.WrapF(rc.OIDCProviderCallback)) s.Router.POST(rc.configuration.LogoutURL, gin.WrapF(rc.Logout)) + + s.Router.Any("/:forward", gin.WrapF(rc.CheckForAuthentication)) + s.Router.Any("/:forward/*any", gin.WrapF(rc.CheckForAuthentication)) } // CheckForAuthentication is responsible for knowing if the user already has a valid credential or not. From 851f36566df4daf00ddca3d04b014df1befb3fce Mon Sep 17 00:00:00 2001 From: wsalles Date: Wed, 12 Jan 2022 11:01:18 -0300 Subject: [PATCH 3/5] bug fix --- internal/controllers/rest_controller.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/controllers/rest_controller.go b/internal/controllers/rest_controller.go index 7c97186..1e5f027 100644 --- a/internal/controllers/rest_controller.go +++ b/internal/controllers/rest_controller.go @@ -3,7 +3,6 @@ package controllers import ( "context" "encoding/json" - "fmt" "net/http" "strings" "time" @@ -198,10 +197,10 @@ func (rc RESTController) forbiddenResponse(w http.ResponseWriter, err error) { func (rc RESTController) allowlistMiddleware() gin.HandlerFunc { return func(c *gin.Context) { for _, allow := range rc.configuration.AllowlistURL { - pathPrefix := fmt.Sprintf("/%s", strings.TrimPrefix(allow, "/")) - if strings.HasPrefix(c.Request.URL.Path, pathPrefix) { + if strings.HasPrefix(c.Request.URL.Path, allow) { rc.logger.Infof("URL %s is allowlisted. Accepted without authorization.", c.Request.URL.Path) - c.AbortWithStatus(http.StatusOK) + c.Abort() + return } } c.Next() From b7fa877dc5587f219aee562b03f9365411cea7b0 Mon Sep 17 00:00:00 2001 From: wsalles Date: Wed, 12 Jan 2022 12:06:20 -0300 Subject: [PATCH 4/5] renamed: from allowlist to skip --- .../configurations/rest_controller_configuration.go | 2 +- internal/controllers/rest_controller.go | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/configurations/rest_controller_configuration.go b/internal/configurations/rest_controller_configuration.go index 0015081..cedd865 100644 --- a/internal/configurations/rest_controller_configuration.go +++ b/internal/configurations/rest_controller_configuration.go @@ -14,7 +14,7 @@ type RESTControllerConfiguration struct { HTTPRequestTimeout time.Duration `envconfig:"HTTP_REQUEST_TIMEOUT" default:"30s"` UserIDHeader string `envconfig:"USER_ID_HEADER" default:"userid"` CookieMaxAge int `envconfig:"COOKIE_MAX_AGE" default:"86400"` - AllowlistURL []string `envconfig:"ALLOWLIST_URL" default:"/dex" split_words:"true"` + SkipURL []string `envconfig:"SKIP_URL" default:"/dex" split_words:"true"` } // LoadFromEnvVars reads all env vars. diff --git a/internal/controllers/rest_controller.go b/internal/controllers/rest_controller.go index 1e5f027..c5356eb 100644 --- a/internal/controllers/rest_controller.go +++ b/internal/controllers/rest_controller.go @@ -43,7 +43,7 @@ func NewRESTController( // Boot initializes creating some routes. func (rc RESTController) Boot(s *server.Server) { - s.Router.Use(rc.allowlistMiddleware()) + s.Router.Use(rc.skipURLMiddleware()) s.Router.GET(rc.configuration.AuthServiceURL, gin.WrapF(rc.CheckForAuthentication)) s.Router.GET(rc.configuration.OIDCCallbackURL, gin.WrapF(rc.OIDCProviderCallback)) @@ -193,12 +193,12 @@ func (rc RESTController) forbiddenResponse(w http.ResponseWriter, err error) { json.NewEncoder(w).Encode(jsonBody) } -// allowlistMiddleware is a middleware that allows all requests that match the allowlist. -func (rc RESTController) allowlistMiddleware() gin.HandlerFunc { +// skipURLMiddleware is a middleware that skips all requests configured in SKIP_URL. +func (rc RESTController) skipURLMiddleware() gin.HandlerFunc { return func(c *gin.Context) { - for _, allow := range rc.configuration.AllowlistURL { - if strings.HasPrefix(c.Request.URL.Path, allow) { - rc.logger.Infof("URL %s is allowlisted. Accepted without authorization.", c.Request.URL.Path) + for _, skip := range rc.configuration.SkipURL { + if strings.HasPrefix(c.Request.URL.Path, skip) { + rc.logger.Infof("URL %s was skipped. Accepted without authorization.", c.Request.URL.Path) c.Abort() return } From 73cff958772295965e8b75f14a722099333d533f Mon Sep 17 00:00:00 2001 From: wsalles Date: Wed, 12 Jan 2022 12:23:40 -0300 Subject: [PATCH 5/5] fix typo --- .../configurations/rest_controller_configuration.go | 2 +- internal/controllers/rest_controller.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/configurations/rest_controller_configuration.go b/internal/configurations/rest_controller_configuration.go index cedd865..d70563a 100644 --- a/internal/configurations/rest_controller_configuration.go +++ b/internal/configurations/rest_controller_configuration.go @@ -14,7 +14,7 @@ type RESTControllerConfiguration struct { HTTPRequestTimeout time.Duration `envconfig:"HTTP_REQUEST_TIMEOUT" default:"30s"` UserIDHeader string `envconfig:"USER_ID_HEADER" default:"userid"` CookieMaxAge int `envconfig:"COOKIE_MAX_AGE" default:"86400"` - SkipURL []string `envconfig:"SKIP_URL" default:"/dex" split_words:"true"` + SkipURLs []string `envconfig:"SKIP_URLS" default:"/dex" split_words:"true"` } // LoadFromEnvVars reads all env vars. diff --git a/internal/controllers/rest_controller.go b/internal/controllers/rest_controller.go index c5356eb..ecd688f 100644 --- a/internal/controllers/rest_controller.go +++ b/internal/controllers/rest_controller.go @@ -43,7 +43,7 @@ func NewRESTController( // Boot initializes creating some routes. func (rc RESTController) Boot(s *server.Server) { - s.Router.Use(rc.skipURLMiddleware()) + s.Router.Use(rc.skipURLsMiddleware()) s.Router.GET(rc.configuration.AuthServiceURL, gin.WrapF(rc.CheckForAuthentication)) s.Router.GET(rc.configuration.OIDCCallbackURL, gin.WrapF(rc.OIDCProviderCallback)) @@ -193,11 +193,11 @@ func (rc RESTController) forbiddenResponse(w http.ResponseWriter, err error) { json.NewEncoder(w).Encode(jsonBody) } -// skipURLMiddleware is a middleware that skips all requests configured in SKIP_URL. -func (rc RESTController) skipURLMiddleware() gin.HandlerFunc { +// skipURLsMiddleware is a middleware that skips all requests configured in SKIP_URL. +func (rc RESTController) skipURLsMiddleware() gin.HandlerFunc { return func(c *gin.Context) { - for _, skip := range rc.configuration.SkipURL { - if strings.HasPrefix(c.Request.URL.Path, skip) { + for _, skipURL := range rc.configuration.SkipURLs { + if strings.HasPrefix(c.Request.URL.Path, skipURL) { rc.logger.Infof("URL %s was skipped. Accepted without authorization.", c.Request.URL.Path) c.Abort() return