11//
2- // Copyright 2024 The Chainloop Authors.
2+ // Copyright 2024-2025 The Chainloop Authors.
33//
44// Licensed under the Apache License, Version 2.0 (the "License");
55// you may not use this file except in compliance with the License.
@@ -23,7 +23,9 @@ import (
2323 "github.com/go-kratos/kratos/v2/log"
2424 "github.com/golang-jwt/jwt/v4"
2525
26+ "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/attjwtmiddleware"
2627 "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext/entities"
28+ "github.com/chainloop-dev/chainloop/app/controlplane/pkg/authz"
2729 "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz"
2830 "github.com/chainloop-dev/chainloop/app/controlplane/pkg/jwt/user"
2931 "github.com/go-kratos/kratos/v2/middleware"
@@ -82,3 +84,60 @@ func setCurrentUser(ctx context.Context, userUC biz.UserOrgFinder, userID string
8284
8385 return entities .WithCurrentUser (ctx , & entities.User {Email : u .Email , ID : u .ID , CreatedAt : u .CreatedAt }), nil
8486}
87+
88+ // Middleware that injects the current user + organization to the context during the attestation process
89+ // it leverages the existing middlewares to set the current user and organization
90+ // but with a skipping behavior since that's the one required by the attMiddleware multi-selector
91+ func WithAttestationContextFromUser (userUC * biz.UserUseCase , logger * log.Helper ) middleware.Middleware {
92+ return func (handler middleware.Handler ) middleware.Handler {
93+ return func (ctx context.Context , req interface {}) (interface {}, error ) {
94+ // If the token is not an user token, we don't need to do anything
95+ // note that this middleware is called by a multi-middleware that works in cascade mode
96+ authInfo , ok := attjwtmiddleware .FromJWTAuthContext (ctx )
97+ // If not found means that there is no currentUser set in the context
98+ if ! ok {
99+ logger .Warn ("couldn't extract org/user, JWT parser middleware not running before this one?" )
100+ return nil , errors .New ("can't extract JWT info from the context" )
101+ }
102+
103+ if authInfo .ProviderKey != attjwtmiddleware .UserTokenProviderKey {
104+ return handler (ctx , req )
105+ }
106+
107+ // set the raw claims in the default context field so the user middleware can understand it
108+ ctx = jwtMiddleware .NewContext (ctx , authInfo .Claims )
109+ // We received a user token during the attestation process
110+ // 1 - Set the current user from the user token
111+ // 2 - Set the current organization for the user token from the DB, header or default
112+ // 3 - Check if the user has permissions to perform attestations in the organization
113+ // 4 - Set the robot account
114+ // NOTE: we reuse the existing middlewares to set the current user and organization by wrapping the call
115+ // Now we can load the organization using the other middleware we have set
116+ return WithCurrentUserMiddleware (userUC , logger )(func (ctx context.Context , req any ) (any , error ) {
117+ return WithCurrentOrganizationMiddleware (userUC , logger )(func (ctx context.Context , req any ) (any , error ) {
118+ org := entities .CurrentOrg (ctx )
119+ if org == nil {
120+ return nil , errors .New ("organization not found" )
121+ }
122+
123+ subject := CurrentAuthzSubject (ctx )
124+ if subject == "" {
125+ return nil , errors .New ("missing authorization subject" )
126+ }
127+
128+ // Load the authorization subject from the context which might be related to a currentUser or an APItoken
129+ // TODO: move to authz middleware once we add support for all the tokens
130+ // for now in that middleware we are not mapping admins nor owners to a specific role
131+ if subject != string (authz .RoleAdmin ) && subject != string (authz .RoleOwner ) {
132+ return nil , fmt .Errorf ("your user doesn't have permissions to perform attestations in this organization, role=%s, orgID=%s" , subject , org .ID )
133+ }
134+
135+ ctx = withRobotAccount (ctx , & RobotAccount {OrgID : org .ID , ProviderKey : attjwtmiddleware .UserTokenProviderKey })
136+ logger .Infow ("msg" , "[authN] processed credentials" , "type" , attjwtmiddleware .UserTokenProviderKey )
137+
138+ return handler (ctx , req )
139+ })(ctx , req )
140+ })(ctx , req )
141+ }
142+ }
143+ }
0 commit comments