summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-05-28 14:26:19 -0600
committermo khan <mo@mokhan.ca>2025-05-28 14:26:19 -0600
commite9546b40c8befabda26c1598c124a6ee2a8d2b8f (patch)
treec7b09c0c1c821b516e56b5ac3637dc07dc97d039
parent1de6a34a55c2e8b7d50945984acb45e7809f6a37 (diff)
refactor: always provide a user in the request context
-rw-r--r--app/controllers/dashboard/controller.go6
-rw-r--r--app/controllers/dashboard/controller_test.go3
-rw-r--r--app/controllers/sparkles/controller_test.go3
-rw-r--r--app/domain/entity.go6
-rw-r--r--app/domain/user.go6
-rw-r--r--app/middleware/init.go15
-rw-r--r--app/middleware/is_logged_in.go3
-rw-r--r--app/middleware/request_parser.go9
-rw-r--r--app/middleware/require_user_test.go3
-rw-r--r--app/middleware/user.go11
-rw-r--r--app/middleware/user_test.go14
-rwxr-xr-xbin/envoy.sh2
-rw-r--r--etc/envoy/envoy.yaml23
-rw-r--r--pkg/authz/check_service.go1
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
}