diff options
| author | mo khan <mo@mokhan.ca> | 2025-05-28 14:26:19 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-05-28 14:26:19 -0600 |
| commit | e9546b40c8befabda26c1598c124a6ee2a8d2b8f (patch) | |
| tree | c7b09c0c1c821b516e56b5ac3637dc07dc97d039 | |
| parent | 1de6a34a55c2e8b7d50945984acb45e7809f6a37 (diff) | |
refactor: always provide a user in the request context
| -rw-r--r-- | app/controllers/dashboard/controller.go | 6 | ||||
| -rw-r--r-- | app/controllers/dashboard/controller_test.go | 3 | ||||
| -rw-r--r-- | app/controllers/sparkles/controller_test.go | 3 | ||||
| -rw-r--r-- | app/domain/entity.go | 6 | ||||
| -rw-r--r-- | app/domain/user.go | 6 | ||||
| -rw-r--r-- | app/middleware/init.go | 15 | ||||
| -rw-r--r-- | app/middleware/is_logged_in.go | 3 | ||||
| -rw-r--r-- | app/middleware/request_parser.go | 9 | ||||
| -rw-r--r-- | app/middleware/require_user_test.go | 3 | ||||
| -rw-r--r-- | app/middleware/user.go | 11 | ||||
| -rw-r--r-- | app/middleware/user_test.go | 14 | ||||
| -rwxr-xr-x | bin/envoy.sh | 2 | ||||
| -rw-r--r-- | etc/envoy/envoy.yaml | 23 | ||||
| -rw-r--r-- | pkg/authz/check_service.go | 1 |
14 files changed, 44 insertions, 61 deletions
diff --git a/app/controllers/dashboard/controller.go b/app/controllers/dashboard/controller.go index 04a7ed1..d279930 100644 --- a/app/controllers/dashboard/controller.go +++ b/app/controllers/dashboard/controller.go @@ -41,14 +41,12 @@ func (c *Controller) Show(w http.ResponseWriter, r *http.Request) { } func (c *Controller) Navigation(w http.ResponseWriter, r *http.Request) { - currentUser := cfg.CurrentUser.From(r.Context()) - w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "text/html") dto := &NavigationDTO{ - CurrentUser: currentUser, - IsLoggedIn: currentUser != nil, + CurrentUser: cfg.CurrentUser.From(r.Context()), + IsLoggedIn: middleware.IsLoggedIn(r), } if err := views.Render(w, "dashboard/nav", dto); err != nil { pls.LogError(r.Context(), err) diff --git a/app/controllers/dashboard/controller_test.go b/app/controllers/dashboard/controller_test.go index c717a74..ddbfd34 100644 --- a/app/controllers/dashboard/controller_test.go +++ b/app/controllers/dashboard/controller_test.go @@ -28,7 +28,7 @@ func TestController(t *testing.T) { }) t.Run("when authenticated", func(t *testing.T) { - ctx := cfg.CurrentUser.With(t.Context(), &domain.User{}) + ctx := cfg.CurrentUser.With(t.Context(), &domain.User{ID: domain.ID("1")}) r, w := test.RequestResponse("GET", "/dashboard", test.WithContext(ctx)) mux.ServeHTTP(w, r) @@ -55,6 +55,7 @@ func TestController(t *testing.T) { t.Run("when authenticated", func(t *testing.T) { ctx := cfg.CurrentUser.With(t.Context(), &domain.User{ + ID: domain.ID("1"), Username: "root", }) r, w := test.RequestResponse("GET", "/dashboard/nav", test.WithContext(ctx)) diff --git a/app/controllers/sparkles/controller_test.go b/app/controllers/sparkles/controller_test.go index 8a1717d..0619b99 100644 --- a/app/controllers/sparkles/controller_test.go +++ b/app/controllers/sparkles/controller_test.go @@ -44,9 +44,10 @@ func TestSparkles(t *testing.T) { t.Run("POST /sparkles", func(t *testing.T) { t.Run("when a user is logged in", func(t *testing.T) { - currentUser := &domain.User{} + currentUser := domain.NewUser(domain.WithID[*domain.User](domain.ID("1"))) t.Run("saves a new sparkle", func(t *testing.T) { + repository := db.NewRepository[*domain.Sparkle]() mux := http.NewServeMux() controller := New(repository) diff --git a/app/domain/entity.go b/app/domain/entity.go index 0377c51..b2c2166 100644 --- a/app/domain/entity.go +++ b/app/domain/entity.go @@ -1,6 +1,12 @@ package domain +import "github.com/xlgmokha/x/pkg/x" + type Entity interface { Identifiable Validate() error } + +func New[T Entity](options ...x.Configure[T]) T { + return x.New[T](x.Map[x.Configure[T], x.Option[T]](options, x.With[T])...) +} diff --git a/app/domain/user.go b/app/domain/user.go index a6adfa8..52cd780 100644 --- a/app/domain/user.go +++ b/app/domain/user.go @@ -10,11 +10,7 @@ type User struct { } func NewUser(options ...x.Configure[*User]) *User { - user := &User{} - for _, option := range options { - option(user) - } - return user + return New[*User](options...) } func (u *User) GetID() ID { diff --git a/app/middleware/init.go b/app/middleware/init.go index 23c524d..4ff10c4 100644 --- a/app/middleware/init.go +++ b/app/middleware/init.go @@ -4,21 +4,16 @@ import ( "net/http" "github.com/xlgmokha/x/pkg/mapper" - "github.com/xlgmokha/x/pkg/x" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/domain" ) func init() { mapper.Register(func(h http.Header) *domain.User { - subject := h.Get("x-jwt-claim-sub") - if x.IsPresent(subject) { - return &domain.User{ - ID: domain.ID(h.Get("x-jwt-claim-sub")), - Username: h.Get("x-jwt-claim-username"), - ProfileURL: h.Get("x-jwt-claim-profile-url"), - Picture: h.Get("x-jwt-claim-picture-url"), - } + return &domain.User{ + ID: domain.ID(h.Get("x-id-jwt-claim-sub")), + Username: h.Get("x-id-jwt-claim-username"), + ProfileURL: h.Get("x-id-jwt-claim-profile-url"), + Picture: h.Get("x-id-jwt-claim-picture-url"), } - return nil }) } diff --git a/app/middleware/is_logged_in.go b/app/middleware/is_logged_in.go index e2f0445..f70a03b 100644 --- a/app/middleware/is_logged_in.go +++ b/app/middleware/is_logged_in.go @@ -8,5 +8,6 @@ import ( ) var IsLoggedIn x.Predicate[*http.Request] = x.Predicate[*http.Request](func(r *http.Request) bool { - return x.IsPresent(cfg.CurrentUser.From(r.Context())) + user := cfg.CurrentUser.From(r.Context()) + return x.IsPresent(user) && x.IsPresent(user.ID) }) diff --git a/app/middleware/request_parser.go b/app/middleware/request_parser.go deleted file mode 100644 index dfc5d3a..0000000 --- a/app/middleware/request_parser.go +++ /dev/null @@ -1,9 +0,0 @@ -package middleware - -import ( - "net/http" - - "github.com/xlgmokha/x/pkg/x" -) - -type RequestParser[T any] x.Mapper[*http.Request, T] diff --git a/app/middleware/require_user_test.go b/app/middleware/require_user_test.go index 07cbf92..20b5f94 100644 --- a/app/middleware/require_user_test.go +++ b/app/middleware/require_user_test.go @@ -28,7 +28,8 @@ func TestRequireUser(t *testing.T) { t.Run("when a user is logged in", func(t *testing.T) { t.Run("forwards the request", func(t *testing.T) { - r, w := test.RequestResponse("GET", "/example", test.WithContextKeyValue(t.Context(), cfg.CurrentUser, &domain.User{})) + user := &domain.User{ID: domain.ID("1")} + r, w := test.RequestResponse("GET", "/example", test.WithContextKeyValue(t.Context(), cfg.CurrentUser, user)) server := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusTeapot) diff --git a/app/middleware/user.go b/app/middleware/user.go index 2b2dd17..317671e 100644 --- a/app/middleware/user.go +++ b/app/middleware/user.go @@ -13,11 +13,12 @@ func User() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.WithFields(r.Context(), log.Fields{ - "payload": r.Header.Get("x-jwt-payload"), - "photo": r.Header.Get("x-jwt-claim-picture-url"), - "profile": r.Header.Get("x-jwt-claim-profile-url"), - "sub": r.Header.Get("x-jwt-claim-sub"), - "username": r.Header.Get("x-jwt-claim-username"), + "authorization": r.Header.Get("Authorization"), + "payload": r.Header.Get("x-id-jwt-payload"), + "photo": r.Header.Get("x-id-jwt-claim-picture-url"), + "profile": r.Header.Get("x-id-jwt-claim-profile-url"), + "sub": r.Header.Get("x-id-jwt-claim-sub"), + "username": r.Header.Get("x-id-jwt-claim-username"), }) next.ServeHTTP(w, r.WithContext(cfg.CurrentUser.With( diff --git a/app/middleware/user_test.go b/app/middleware/user_test.go index 66ca121..c778c98 100644 --- a/app/middleware/user_test.go +++ b/app/middleware/user_test.go @@ -14,9 +14,9 @@ import ( func TestUser(t *testing.T) { middleware := User() - t.Run("when x-jwt-claim-* headers are not provided", func(t *testing.T) { + t.Run("when x-id-jwt-claim-* headers are not provided", func(t *testing.T) { server := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - require.Nil(t, cfg.CurrentUser.From(r.Context())) + require.False(t, IsLoggedIn(r)) w.WriteHeader(http.StatusTeapot) })) @@ -27,7 +27,7 @@ func TestUser(t *testing.T) { assert.Equal(t, http.StatusTeapot, w.Code) }) - t.Run("when x-jwt-claim-* headers are provided", func(t *testing.T) { + t.Run("when x-id-jwt-claim-* headers are provided", func(t *testing.T) { server := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.True(t, IsLoggedIn(r)) @@ -43,10 +43,10 @@ func TestUser(t *testing.T) { })) r, w := test.RequestResponse("GET", "/", - test.WithRequestHeader("x-jwt-claim-sub", "1"), - test.WithRequestHeader("x-jwt-claim-username", "root"), - test.WithRequestHeader("x-jwt-claim-profile-url", "https://gitlab.com/tanuki"), - test.WithRequestHeader("x-jwt-claim-picture-url", "https://example.com/profile.png"), + test.WithRequestHeader("x-id-jwt-claim-sub", "1"), + test.WithRequestHeader("x-id-jwt-claim-username", "root"), + test.WithRequestHeader("x-id-jwt-claim-profile-url", "https://gitlab.com/tanuki"), + test.WithRequestHeader("x-id-jwt-claim-picture-url", "https://example.com/profile.png"), ) server.ServeHTTP(w, r) diff --git a/bin/envoy.sh b/bin/envoy.sh index 219228f..1af16aa 100755 --- a/bin/envoy.sh +++ b/bin/envoy.sh @@ -33,4 +33,4 @@ fi envoy \ --config-yaml "$yaml" \ --log-level warn \ - --component-log-level admin:warn,connection:warn,ext_authz:info,grpc:info,health_checker:warn,http:warn,http2:warn,jwt:trace,oauth2:warn,router:warn,secret:warn,upstream:warn + --component-log-level admin:warn,connection:warn,ext_authz:info,grpc:info,health_checker:warn,http:warn,http2:warn,jwt:trace,oauth2:trace,router:warn,secret:warn,upstream:warn diff --git a/etc/envoy/envoy.yaml b/etc/envoy/envoy.yaml index ef676fb..a7d20be 100644 --- a/etc/envoy/envoy.yaml +++ b/etc/envoy/envoy.yaml @@ -172,24 +172,23 @@ static_resources: typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication providers: - provider1: + id_token_provider: issuer: https://example.com audiences: - OAUTH_CLIENT_ID claim_to_headers: - claim_name: sub - header_name: x-jwt-claim-sub + header_name: x-id-jwt-claim-sub - claim_name: nickname - header_name: x-jwt-claim-username + header_name: x-id-jwt-claim-username - claim_name: profile - header_name: x-jwt-claim-profile-url + header_name: x-id-jwt-claim-profile-url - claim_name: picture - header_name: x-jwt-claim-picture-url + header_name: x-id-jwt-claim-picture-url forward: true - forward_payload_header: x-jwt-payload + forward_payload_header: x-id-jwt-payload from_cookies: - id_token - - bearer_token issuer: https://example.com remote_jwks: http_uri: @@ -198,20 +197,12 @@ static_resources: timeout: 5s rules: - match: - path: /health - - match: - prefix: /sparkles - - match: - prefix: /dashboard/nav - - match: safe_regex: regex: .*\\.(css|js|png|html|ico)$ - match: path: / - - match: - path: /dashboard requires: - provider_name: provider1 + provider_name: id_token_provider - name: envoy.filters.http.ext_authz typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz diff --git a/pkg/authz/check_service.go b/pkg/authz/check_service.go index 641ba92..9db32d0 100644 --- a/pkg/authz/check_service.go +++ b/pkg/authz/check_service.go @@ -40,6 +40,7 @@ func NewCheckService() *CheckService { } func (svc *CheckService) Check(ctx context.Context, request *auth.CheckRequest) (*auth.CheckResponse, error) { + log.WithFields(ctx, log.Fields{"headers": request.Attributes.Request.Http.Headers}) if svc.isAllowed(ctx, request) { return svc.OK(ctx), nil } |
