diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh index 095104dc4e..1f089c97b6 100755 --- a/.clusterfuzzlite/build.sh +++ b/.clusterfuzzlite/build.sh @@ -29,4 +29,4 @@ compile_native_go_fuzzer github.com/stacklok/minder/internal/engine/eval/rego Fu compile_native_go_fuzzer github.com/stacklok/minder/internal/controlplane FuzzGitHubEventParsers FuzzGitHubEventParsers compile_native_go_fuzzer github.com/stacklok/minder/internal/engine/ingester/diff FuzzDiffParse FuzzDiffParse compile_native_go_fuzzer github.com/stacklok/minder/internal/crypto FuzzEncryptDecrypt FuzzEncryptDecrypt -compile_native_go_fuzzer github.com/stacklok/minder/internal/auth FuzzParseAndValidate FuzzParseAndValidate +compile_native_go_fuzzer github.com/stacklok/minder/internal/auth/jwt FuzzParseAndValidate FuzzParseAndValidate diff --git a/cmd/dev/app/testserver/testserver.go b/cmd/dev/app/testserver/testserver.go index d4b2bcf336..a17801da73 100644 --- a/cmd/dev/app/testserver/testserver.go +++ b/cmd/dev/app/testserver/testserver.go @@ -30,7 +30,7 @@ import ( "github.com/spf13/viper" "github.com/stacklok/minder/internal/auth" - noopauth "github.com/stacklok/minder/internal/auth/noop" + noopauth "github.com/stacklok/minder/internal/auth/jwt/noop" mockauthz "github.com/stacklok/minder/internal/authz/mock" "github.com/stacklok/minder/internal/config" serverconfig "github.com/stacklok/minder/internal/config/server" diff --git a/cmd/server/app/serve.go b/cmd/server/app/serve.go index c311dcdb85..a364687b7a 100644 --- a/cmd/server/app/serve.go +++ b/cmd/server/app/serve.go @@ -28,6 +28,7 @@ import ( "github.com/spf13/viper" "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" "github.com/stacklok/minder/internal/auth/keycloak" "github.com/stacklok/minder/internal/authz" "github.com/stacklok/minder/internal/config" @@ -100,7 +101,7 @@ var serveCmd = &cobra.Command{ if err != nil { return fmt.Errorf("failed to create issuer URL: %w\n", err) } - jwt, err := auth.NewJwtValidator(ctx, jwksUrl.String(), issUrl.String(), cfg.Identity.Server.Audience) + jwt, err := jwt.NewJwtValidator(ctx, jwksUrl.String(), issUrl.String(), cfg.Identity.Server.Audience) if err != nil { return fmt.Errorf("failed to fetch and cache identity provider JWKS: %w\n", err) } diff --git a/internal/auth/github.go b/internal/auth/github.go index fbd86ddc64..0a091bff67 100644 --- a/internal/auth/github.go +++ b/internal/auth/github.go @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Package auth contains the authentication logic for the control plane package auth import ( diff --git a/internal/auth/fuzz_test.go b/internal/auth/jwt/fuzz_test.go similarity index 96% rename from internal/auth/fuzz_test.go rename to internal/auth/jwt/fuzz_test.go index 322c6c273e..5afc80d436 100644 --- a/internal/auth/fuzz_test.go +++ b/internal/auth/jwt/fuzz_test.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package auth +package jwt import ( "crypto/rand" @@ -24,7 +24,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwk" "go.uber.org/mock/gomock" - mockjwt "github.com/stacklok/minder/internal/auth/mock" + mockjwt "github.com/stacklok/minder/internal/auth/jwt/mock" ) var ( diff --git a/internal/auth/jwauth_test.go b/internal/auth/jwt/jwauth_test.go similarity index 97% rename from internal/auth/jwauth_test.go rename to internal/auth/jwt/jwauth_test.go index b51f6f484a..e544717065 100644 --- a/internal/auth/jwauth_test.go +++ b/internal/auth/jwt/jwauth_test.go @@ -13,8 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// package auth contains the authentication logic for the control plane -package auth +package jwt import ( crand "crypto/rand" @@ -29,7 +28,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" - mockjwt "github.com/stacklok/minder/internal/auth/mock" + mockjwt "github.com/stacklok/minder/internal/auth/jwt/mock" ) func TestParseAndValidate(t *testing.T) { diff --git a/internal/auth/jwtauth.go b/internal/auth/jwt/jwtauth.go similarity index 95% rename from internal/auth/jwtauth.go rename to internal/auth/jwt/jwtauth.go index 4e7332c175..08689dcdcd 100644 --- a/internal/auth/jwtauth.go +++ b/internal/auth/jwt/jwtauth.go @@ -13,7 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -package auth +// Package jwt provides the logic for reading and validating JWT tokens +package jwt import ( "context" @@ -26,8 +27,8 @@ import ( //go:generate go run go.uber.org/mock/mockgen -package mock_$GOPACKAGE -destination=./mock/$GOFILE -source=./$GOFILE -// JwtValidator provides the functions to validate a JWT -type JwtValidator interface { +// Validator provides the functions to validate a JWT +type Validator interface { ParseAndValidate(tokenString string) (openid.Token, error) } @@ -86,7 +87,7 @@ func (j *JwkSetJwtValidator) ParseAndValidate(tokenString string) (openid.Token, } // NewJwtValidator creates a new JWT validator that uses a JWK set URL to validate the tokens -func NewJwtValidator(ctx context.Context, jwksUrl string, issUrl string, aud string) (JwtValidator, error) { +func NewJwtValidator(ctx context.Context, jwksUrl string, issUrl string, aud string) (Validator, error) { // Cache the JWK set // The cache will refresh every 15 minutes by default jwks := jwk.NewCache(ctx) diff --git a/internal/auth/mock/jwtauth.go b/internal/auth/jwt/mock/jwtauth.go similarity index 67% rename from internal/auth/mock/jwtauth.go rename to internal/auth/jwt/mock/jwtauth.go index 3c3554f3fa..fc078d7592 100644 --- a/internal/auth/mock/jwtauth.go +++ b/internal/auth/jwt/mock/jwtauth.go @@ -3,11 +3,11 @@ // // Generated by this command: // -// mockgen -package mock_auth -destination=./mock/jwtauth.go -source=./jwtauth.go +// mockgen -package mock_jwt -destination=./mock/jwtauth.go -source=./jwtauth.go // -// Package mock_auth is a generated GoMock package. -package mock_auth +// Package mock_jwt is a generated GoMock package. +package mock_jwt import ( reflect "reflect" @@ -17,31 +17,31 @@ import ( gomock "go.uber.org/mock/gomock" ) -// MockJwtValidator is a mock of JwtValidator interface. -type MockJwtValidator struct { +// MockValidator is a mock of Validator interface. +type MockValidator struct { ctrl *gomock.Controller - recorder *MockJwtValidatorMockRecorder + recorder *MockValidatorMockRecorder } -// MockJwtValidatorMockRecorder is the mock recorder for MockJwtValidator. -type MockJwtValidatorMockRecorder struct { - mock *MockJwtValidator +// MockValidatorMockRecorder is the mock recorder for MockValidator. +type MockValidatorMockRecorder struct { + mock *MockValidator } -// NewMockJwtValidator creates a new mock instance. -func NewMockJwtValidator(ctrl *gomock.Controller) *MockJwtValidator { - mock := &MockJwtValidator{ctrl: ctrl} - mock.recorder = &MockJwtValidatorMockRecorder{mock} +// NewMockValidator creates a new mock instance. +func NewMockValidator(ctrl *gomock.Controller) *MockValidator { + mock := &MockValidator{ctrl: ctrl} + mock.recorder = &MockValidatorMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockJwtValidator) EXPECT() *MockJwtValidatorMockRecorder { +func (m *MockValidator) EXPECT() *MockValidatorMockRecorder { return m.recorder } // ParseAndValidate mocks base method. -func (m *MockJwtValidator) ParseAndValidate(tokenString string) (openid.Token, error) { +func (m *MockValidator) ParseAndValidate(tokenString string) (openid.Token, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ParseAndValidate", tokenString) ret0, _ := ret[0].(openid.Token) @@ -50,9 +50,9 @@ func (m *MockJwtValidator) ParseAndValidate(tokenString string) (openid.Token, e } // ParseAndValidate indicates an expected call of ParseAndValidate. -func (mr *MockJwtValidatorMockRecorder) ParseAndValidate(tokenString any) *gomock.Call { +func (mr *MockValidatorMockRecorder) ParseAndValidate(tokenString any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParseAndValidate", reflect.TypeOf((*MockJwtValidator)(nil).ParseAndValidate), tokenString) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ParseAndValidate", reflect.TypeOf((*MockValidator)(nil).ParseAndValidate), tokenString) } // MockKeySetFetcher is a mock of KeySetFetcher interface. diff --git a/internal/auth/noop/jwtauth.go b/internal/auth/jwt/noop/jwtauth.go similarity index 87% rename from internal/auth/noop/jwtauth.go rename to internal/auth/jwt/noop/jwtauth.go index fc55c21939..a379059997 100644 --- a/internal/auth/noop/jwtauth.go +++ b/internal/auth/jwt/noop/jwtauth.go @@ -13,13 +13,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package noop provides a no-op implementation of the JwtValidator interface +// Package noop provides a no-op implementation of the Validator interface package noop import ( "github.com/lestrrat-go/jwx/v2/jwt/openid" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" ) type noopJwtValidator struct { @@ -28,7 +28,7 @@ type noopJwtValidator struct { } // NewJwtValidator returns a new instance of the no-op JWT validator -func NewJwtValidator(subject string) auth.JwtValidator { +func NewJwtValidator(subject string) jwt.Validator { return &noopJwtValidator{ Subject: subject, } diff --git a/internal/authz/authz.go b/internal/authz/authz.go index b08ad9bdca..4a0458f84d 100644 --- a/internal/authz/authz.go +++ b/internal/authz/authz.go @@ -32,7 +32,7 @@ import ( "github.com/rs/zerolog" "k8s.io/client-go/transport" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" srvconfig "github.com/stacklok/minder/internal/config/server" minderv1 "github.com/stacklok/minder/pkg/api/protobuf/go/minder/v1" ) @@ -249,7 +249,7 @@ func (a *ClientWrapper) Check(ctx context.Context, action string, project uuid.U // TODO: set ClientCheckOptions like in // https://openfga.dev/docs/getting-started/perform-check#02-calling-check-api options := fgaclient.ClientCheckOptions{} - userString := getUserForTuple(auth.GetUserSubjectFromContext(ctx)) + userString := getUserForTuple(jwt.GetUserSubjectFromContext(ctx)) body := fgaclient.ClientCheckRequest{ User: userString, Relation: action, diff --git a/internal/authz/authz_test.go b/internal/authz/authz_test.go index 6d6a571a83..e6870bf1ef 100644 --- a/internal/authz/authz_test.go +++ b/internal/authz/authz_test.go @@ -32,7 +32,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" "github.com/stacklok/minder/internal/authz" srvconfig "github.com/stacklok/minder/internal/config/server" ) @@ -105,7 +105,7 @@ func TestVerifyOneProject(t *testing.T) { userJWT := openid.New() assert.NoError(t, userJWT.Set("sub", "user-1")) - userctx := auth.WithAuthTokenContext(ctx, userJWT) + userctx := jwt.WithAuthTokenContext(ctx, userJWT) // verify the project assert.NoError(t, c.Check(userctx, "get", prj), "failed to check project") @@ -159,7 +159,7 @@ func TestVerifyMultipleProjects(t *testing.T) { user1JWT := openid.New() assert.NoError(t, user1JWT.Set("sub", "user-1")) - userctx := auth.WithAuthTokenContext(ctx, user1JWT) + userctx := jwt.WithAuthTokenContext(ctx, user1JWT) // verify the project assert.NoError(t, c.Check(userctx, "get", prj1), "failed to check project") @@ -178,7 +178,7 @@ func TestVerifyMultipleProjects(t *testing.T) { // verify the project user2JWT := openid.New() assert.NoError(t, user2JWT.Set("sub", "user-2")) - assert.NoError(t, c.Check(auth.WithAuthTokenContext(ctx, user2JWT), "get", prj3), "failed to check project") + assert.NoError(t, c.Check(jwt.WithAuthTokenContext(ctx, user2JWT), "get", prj3), "failed to check project") // verify user-1 cannot operate on project 3 assert.Error(t, c.Check(userctx, "get", prj3), "expected user-1 to not be able to operate on project 3") diff --git a/internal/authz/fuzz_test.go b/internal/authz/fuzz_test.go index 12567587d5..f13b57b8f7 100644 --- a/internal/authz/fuzz_test.go +++ b/internal/authz/fuzz_test.go @@ -22,7 +22,7 @@ import ( "github.com/google/uuid" "github.com/lestrrat-go/jwx/v2/jwt/openid" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" "github.com/stacklok/minder/internal/authz" ) @@ -52,7 +52,7 @@ func FuzzAllAuthzApis(f *testing.F) { return } - userctx := auth.WithAuthTokenContext(ctx, userJWT) + userctx := jwt.WithAuthTokenContext(ctx, userJWT) c.Check(userctx, str3, prj) diff --git a/internal/controlplane/handlers_authz.go b/internal/controlplane/handlers_authz.go index 73031864e1..41b642c150 100644 --- a/internal/controlplane/handlers_authz.go +++ b/internal/controlplane/handlers_authz.go @@ -29,7 +29,7 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" "github.com/stacklok/minder/internal/authz" "github.com/stacklok/minder/internal/db" "github.com/stacklok/minder/internal/email" @@ -111,7 +111,7 @@ func ProjectAuthorizationInterceptor(ctx context.Context, req interface{}, info zerolog.Ctx(ctx).Error().Err(err).Msg("authorization check failed") return nil, util.UserVisibleError( codes.PermissionDenied, "user %q is not authorized to perform this operation on project %q", - auth.GetUserSubjectFromContext(ctx), entityCtx.Project.ID) + jwt.GetUserSubjectFromContext(ctx), entityCtx.Project.ID) } return handler(ctx, req) @@ -183,7 +183,7 @@ func getDefaultProjectID( store db.Store, authzClient authz.Client, ) (uuid.UUID, error) { - subject := auth.GetUserSubjectFromContext(ctx) + subject := jwt.GetUserSubjectFromContext(ctx) userInfo, err := store.GetUserBySubject(ctx, subject) if err != nil { @@ -348,7 +348,7 @@ func (s *Server) inviteUser( ) (*minder.AssignRoleResponse, error) { var userInvite db.UserInvite // Get the sponsor's user information (current user) - currentUser, err := s.store.GetUserBySubject(ctx, auth.GetUserSubjectFromContext(ctx)) + currentUser, err := s.store.GetUserBySubject(ctx, jwt.GetUserSubjectFromContext(ctx)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get user: %s", err) } @@ -692,7 +692,7 @@ func (s *Server) updateInvite( ) (*minder.UpdateRoleResponse, error) { var userInvite db.UserInvite // Get the sponsor's user information (current user) - currentUser, err := s.store.GetUserBySubject(ctx, auth.GetUserSubjectFromContext(ctx)) + currentUser, err := s.store.GetUserBySubject(ctx, jwt.GetUserSubjectFromContext(ctx)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get user: %s", err) } @@ -847,12 +847,12 @@ func (s *Server) updateRole( // isUserSelfUpdating is used to prevent if the user is trying to update their own role func isUserSelfUpdating(ctx context.Context, subject, inviteeEmail string) error { if subject != "" { - if auth.GetUserSubjectFromContext(ctx) == subject { + if jwt.GetUserSubjectFromContext(ctx) == subject { return util.UserVisibleError(codes.InvalidArgument, "cannot update your own role") } } if inviteeEmail != "" { - tokenEmail, err := auth.GetUserEmailFromContext(ctx) + tokenEmail, err := jwt.GetUserEmailFromContext(ctx) if err != nil { return util.UserVisibleError(codes.Internal, "error getting user email from token: %v", err) } diff --git a/internal/controlplane/handlers_authz_test.go b/internal/controlplane/handlers_authz_test.go index d3c041ed75..e7e5bec939 100644 --- a/internal/controlplane/handlers_authz_test.go +++ b/internal/controlplane/handlers_authz_test.go @@ -39,7 +39,8 @@ import ( mockdb "github.com/stacklok/minder/database/mock" "github.com/stacklok/minder/internal/auth" - "github.com/stacklok/minder/internal/auth/noop" + authjwt "github.com/stacklok/minder/internal/auth/jwt" + "github.com/stacklok/minder/internal/auth/jwt/noop" "github.com/stacklok/minder/internal/authz" "github.com/stacklok/minder/internal/authz/mock" "github.com/stacklok/minder/internal/db" @@ -187,7 +188,7 @@ func TestEntityContextProjectInterceptor(t *testing.T) { if tc.buildStubs != nil { tc.buildStubs(t, mockStore) } - ctx := auth.WithAuthTokenContext(withRpcOptions(context.Background(), rpcOptions), userJWT) + ctx := authjwt.WithAuthTokenContext(withRpcOptions(context.Background(), rpcOptions), userJWT) authzClient := &mock.SimpleClient{} @@ -281,7 +282,7 @@ func TestProjectAuthorizationInterceptor(t *testing.T) { } ctx := withRpcOptions(context.Background(), rpcOptions) ctx = engcontext.WithEntityContext(ctx, tc.entityCtx) - ctx = auth.WithAuthTokenContext(ctx, userJWT) + ctx = authjwt.WithAuthTokenContext(ctx, userJWT) _, err := ProjectAuthorizationInterceptor(ctx, request{}, &grpc.UnaryServerInfo{ Server: &server, }, unaryHandler) @@ -491,7 +492,7 @@ func TestRoleManagement(t *testing.T) { } ctx := context.Background() - ctx = auth.WithAuthTokenContext(ctx, user) + ctx = authjwt.WithAuthTokenContext(ctx, user) ctx = engcontext.WithEntityContext(ctx, &engcontext.EntityContext{ Project: engcontext.Project{ ID: project, diff --git a/internal/controlplane/handlers_oauth.go b/internal/controlplane/handlers_oauth.go index 25d98de159..58e4ba62d7 100644 --- a/internal/controlplane/handlers_oauth.go +++ b/internal/controlplane/handlers_oauth.go @@ -37,6 +37,7 @@ import ( "google.golang.org/grpc/status" "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" mcrypto "github.com/stacklok/minder/internal/crypto" "github.com/stacklok/minder/internal/db" "github.com/stacklok/minder/internal/engine/engcontext" @@ -93,7 +94,7 @@ func (s *Server) GetAuthorizationURL(ctx context.Context, span.SetAttributes(attribute.Key("provider").String(providerName)) defer span.End() - user, _ := auth.GetUserClaimFromContext[string](ctx, "gh_id") + user, _ := jwt.GetUserClaimFromContext[string](ctx, "gh_id") // If the user's token doesn't have gh_id set yet, we'll pass it through for now. s.mt.AddTokenOpCount(ctx, "issued", user != "") diff --git a/internal/controlplane/handlers_oauth_test.go b/internal/controlplane/handlers_oauth_test.go index 54346ca61c..da29188374 100644 --- a/internal/controlplane/handlers_oauth_test.go +++ b/internal/controlplane/handlers_oauth_test.go @@ -42,8 +42,8 @@ import ( "google.golang.org/grpc/codes" mockdb "github.com/stacklok/minder/database/mock" - "github.com/stacklok/minder/internal/auth" - mockjwt "github.com/stacklok/minder/internal/auth/mock" + "github.com/stacklok/minder/internal/auth/jwt" + mockjwt "github.com/stacklok/minder/internal/auth/jwt/mock" serverconfig "github.com/stacklok/minder/internal/config/server" "github.com/stacklok/minder/internal/controlplane/metrics" "github.com/stacklok/minder/internal/crypto" @@ -375,7 +375,7 @@ func TestGetAuthorizationURL(t *testing.T) { if tc.getToken != nil { token = tc.getToken(token) } - ctx := auth.WithAuthTokenContext(basengcontext, token) + ctx := jwt.WithAuthTokenContext(basengcontext, token) ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -412,7 +412,7 @@ func TestGetAuthorizationURL(t *testing.T) { }, }, } - mockJwt := mockjwt.NewMockJwtValidator(ctrl) + mockJwt := mockjwt.NewMockValidator(ctrl) mockAuthManager := mockmanager.NewMockAuthManager(ctrl) mockAuthManager.EXPECT().NewOAuthConfig(gomock.Any(), gomock.Any()).Return(&oauth2.Config{}, nil).AnyTimes() diff --git a/internal/controlplane/handlers_projects.go b/internal/controlplane/handlers_projects.go index 81f89adbed..a6e1332345 100644 --- a/internal/controlplane/handlers_projects.go +++ b/internal/controlplane/handlers_projects.go @@ -26,7 +26,7 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" "github.com/stacklok/minder/internal/db" "github.com/stacklok/minder/internal/engine/engcontext" "github.com/stacklok/minder/internal/projects" @@ -40,7 +40,7 @@ func (s *Server) ListProjects( ctx context.Context, _ *minderv1.ListProjectsRequest, ) (*minderv1.ListProjectsResponse, error) { - userInfo, err := s.store.GetUserBySubject(ctx, auth.GetUserSubjectFromContext(ctx)) + userInfo, err := s.store.GetUserBySubject(ctx, jwt.GetUserSubjectFromContext(ctx)) if err != nil { return nil, status.Errorf(codes.Internal, "error getting user: %v", err) } diff --git a/internal/controlplane/handlers_projects_test.go b/internal/controlplane/handlers_projects_test.go index 5c58ead171..59a9064e49 100644 --- a/internal/controlplane/handlers_projects_test.go +++ b/internal/controlplane/handlers_projects_test.go @@ -25,7 +25,7 @@ import ( "go.uber.org/mock/gomock" mockdb "github.com/stacklok/minder/database/mock" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" "github.com/stacklok/minder/internal/authz/mock" "github.com/stacklok/minder/internal/db" minder "github.com/stacklok/minder/pkg/api/protobuf/go/minder/v1" @@ -55,7 +55,7 @@ func TestListProjects(t *testing.T) { } ctx := context.Background() - ctx = auth.WithAuthTokenContext(ctx, user) + ctx = jwt.WithAuthTokenContext(ctx, user) resp, err := server.ListProjects(ctx, &minder.ListProjectsRequest{}) assert.NoError(t, err) @@ -92,7 +92,7 @@ func TestListProjectsWithOneDeletedWhileIterating(t *testing.T) { } ctx := context.Background() - ctx = auth.WithAuthTokenContext(ctx, user) + ctx = jwt.WithAuthTokenContext(ctx, user) resp, err := server.ListProjects(ctx, &minder.ListProjectsRequest{}) assert.NoError(t, err) diff --git a/internal/controlplane/handlers_providers_test.go b/internal/controlplane/handlers_providers_test.go index 7b692dee4e..7172d55e81 100644 --- a/internal/controlplane/handlers_providers_test.go +++ b/internal/controlplane/handlers_providers_test.go @@ -36,7 +36,7 @@ import ( "google.golang.org/protobuf/types/known/structpb" mockdb "github.com/stacklok/minder/database/mock" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" "github.com/stacklok/minder/internal/authz/mock" serverconfig "github.com/stacklok/minder/internal/config/server" "github.com/stacklok/minder/internal/crypto" @@ -242,7 +242,7 @@ func TestCreateProvider(t *testing.T) { assert.NoError(t, user.Set("sub", "testuser")) ctx := context.Background() - ctx = auth.WithAuthTokenContext(ctx, user) + ctx = jwt.WithAuthTokenContext(ctx, user) ctx = engcontext.WithEntityContext(ctx, &engcontext.EntityContext{ Project: engcontext.Project{ID: projectID}, Provider: engcontext.Provider{Name: scenario.name}, @@ -354,7 +354,7 @@ func TestCreateProviderFailures(t *testing.T) { assert.NoError(t, user.Set("sub", "testuser")) ctx := context.Background() - ctx = auth.WithAuthTokenContext(ctx, user) + ctx = jwt.WithAuthTokenContext(ctx, user) ctx = engcontext.WithEntityContext(ctx, &engcontext.EntityContext{ Project: engcontext.Project{ID: projectID}, Provider: engcontext.Provider{Name: providerName}, @@ -591,7 +591,7 @@ func TestDeleteProvider(t *testing.T) { } ctx := context.Background() - ctx = auth.WithAuthTokenContext(ctx, user) + ctx = jwt.WithAuthTokenContext(ctx, user) ctx = engcontext.WithEntityContext(ctx, &engcontext.EntityContext{ Project: engcontext.Project{ID: projectID}, Provider: engcontext.Provider{Name: providerName}, @@ -704,7 +704,7 @@ func TestDeleteProviderByID(t *testing.T) { } ctx := context.Background() - ctx = auth.WithAuthTokenContext(ctx, user) + ctx = jwt.WithAuthTokenContext(ctx, user) ctx = engcontext.WithEntityContext(ctx, &engcontext.EntityContext{ Project: engcontext.Project{ID: projectID}, }) diff --git a/internal/controlplane/handlers_token.go b/internal/controlplane/handlers_token.go index 37969858ab..76c19ae7c6 100644 --- a/internal/controlplane/handlers_token.go +++ b/internal/controlplane/handlers_token.go @@ -30,7 +30,7 @@ import ( "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" "github.com/stacklok/minder/internal/logger" "github.com/stacklok/minder/internal/util" minder "github.com/stacklok/minder/pkg/api/protobuf/go/minder/v1" @@ -77,7 +77,7 @@ func TokenValidationInterceptor(ctx context.Context, req interface{}, info *grpc return nil, status.Errorf(codes.Unauthenticated, "invalid auth token: %v", err) } - ctx = auth.WithAuthTokenContext(ctx, parsedToken) + ctx = jwt.WithAuthTokenContext(ctx, parsedToken) // Attach the login sha for telemetry usage (hash of the user subject from the JWT) loginSHA := sha256.Sum256([]byte(parsedToken.Subject())) diff --git a/internal/controlplane/handlers_user.go b/internal/controlplane/handlers_user.go index bbfb217432..a29e5b2ec2 100644 --- a/internal/controlplane/handlers_user.go +++ b/internal/controlplane/handlers_user.go @@ -29,7 +29,7 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" "github.com/stacklok/minder/internal/authz" "github.com/stacklok/minder/internal/db" "github.com/stacklok/minder/internal/flags" @@ -118,7 +118,7 @@ func (s *Server) CreateUser(ctx context.Context, } func (s *Server) claimGitHubInstalls(ctx context.Context, qtx db.Querier) []*db.Project { - ghId, ok := auth.GetUserClaimFromContext[string](ctx, "gh_id") + ghId, ok := jwt.GetUserClaimFromContext[string](ctx, "gh_id") if !ok || ghId == "" { return nil } @@ -316,7 +316,7 @@ func (s *Server) ListInvitations(ctx context.Context, _ *pb.ListInvitationsReque invitations := make([]*pb.Invitation, 0) // Extracts the user email from the token - tokenEmail, err := auth.GetUserEmailFromContext(ctx) + tokenEmail, err := jwt.GetUserEmailFromContext(ctx) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get user email: %s", err) } @@ -421,7 +421,7 @@ func (s *Server) acceptInvitation(ctx context.Context, userInvite db.GetInvitati } // Loop through all role assignments for the project and check if this user already has a role for _, existing := range as { - if existing.Subject == auth.GetUserSubjectFromContext(ctx) { + if existing.Subject == jwt.GetUserSubjectFromContext(ctx) { // User already has the same role in the project if existing.Role == userInvite.Role { return util.UserVisibleError(codes.AlreadyExists, "user already has the same role in the project") @@ -434,7 +434,7 @@ func (s *Server) acceptInvitation(ctx context.Context, userInvite db.GetInvitati // Delete the role assignment if err := s.authzClient.Delete( ctx, - auth.GetUserSubjectFromContext(ctx), + jwt.GetUserSubjectFromContext(ctx), existingRole, uuid.MustParse(*existing.Project), ); err != nil { @@ -448,7 +448,7 @@ func (s *Server) acceptInvitation(ctx context.Context, userInvite db.GetInvitati return status.Errorf(codes.Internal, "failed to parse invitation role: %s", err) } // Add the user to the project - if err := s.authzClient.Write(ctx, auth.GetUserSubjectFromContext(ctx), authzRole, userInvite.Project); err != nil { + if err := s.authzClient.Write(ctx, jwt.GetUserSubjectFromContext(ctx), authzRole, userInvite.Project); err != nil { return status.Errorf(codes.Internal, "error writing role assignment: %v", err) } return nil @@ -457,7 +457,7 @@ func (s *Server) acceptInvitation(ctx context.Context, userInvite db.GetInvitati // isUserSelfResolving is used to prevent if the user is trying to resolve an invitation they created func isUserSelfResolving(ctx context.Context, store db.Store, i db.GetInvitationByCodeRow) error { // Get current user data - currentUser, err := store.GetUserBySubject(ctx, auth.GetUserSubjectFromContext(ctx)) + currentUser, err := store.GetUserBySubject(ctx, jwt.GetUserSubjectFromContext(ctx)) if err != nil { return status.Errorf(codes.Internal, "failed to get user: %s", err) } diff --git a/internal/controlplane/handlers_user_test.go b/internal/controlplane/handlers_user_test.go index c8be77869e..0dc908994f 100644 --- a/internal/controlplane/handlers_user_test.go +++ b/internal/controlplane/handlers_user_test.go @@ -33,8 +33,8 @@ import ( "google.golang.org/grpc/metadata" mockdb "github.com/stacklok/minder/database/mock" - "github.com/stacklok/minder/internal/auth" - mockjwt "github.com/stacklok/minder/internal/auth/mock" + "github.com/stacklok/minder/internal/auth/jwt" + mockjwt "github.com/stacklok/minder/internal/auth/jwt/mock" "github.com/stacklok/minder/internal/authz/mock" serverconfig "github.com/stacklok/minder/internal/config/server" mockcrypto "github.com/stacklok/minder/internal/crypto/mock" @@ -62,7 +62,7 @@ func TestCreateUser_gRPC(t *testing.T) { testCases := []struct { name string req *pb.CreateUserRequest - buildStubs func(ctx context.Context, store *mockdb.MockStore, jwt *mockjwt.MockJwtValidator, + buildStubs func(ctx context.Context, store *mockdb.MockStore, validator *mockjwt.MockValidator, prov *mockprov.MockGitHubProviderService) context.Context checkResponse func(t *testing.T, res *pb.CreateUserResponse, err error) expectedStatusCode codes.Code @@ -70,7 +70,7 @@ func TestCreateUser_gRPC(t *testing.T) { { name: "Success", req: &pb.CreateUserRequest{}, - buildStubs: func(ctx context.Context, store *mockdb.MockStore, jwt *mockjwt.MockJwtValidator, + buildStubs: func(ctx context.Context, store *mockdb.MockStore, jwt *mockjwt.MockValidator, _ *mockprov.MockGitHubProviderService) context.Context { tx := sql.Tx{} store.EXPECT().BeginTransaction().Return(&tx, nil) @@ -114,9 +114,9 @@ func TestCreateUser_gRPC(t *testing.T) { { name: "Success with pending App", req: &pb.CreateUserRequest{}, - buildStubs: func(ctx context.Context, store *mockdb.MockStore, jwt *mockjwt.MockJwtValidator, + buildStubs: func(ctx context.Context, store *mockdb.MockStore, validator *mockjwt.MockValidator, prov *mockprov.MockGitHubProviderService) context.Context { - ctx = auth.WithAuthTokenContext(ctx, keyCloakUserToken) + ctx = jwt.WithAuthTokenContext(ctx, keyCloakUserToken) tx := sql.Tx{} store.EXPECT().BeginTransaction().Return(&tx, nil) @@ -152,7 +152,7 @@ func TestCreateUser_gRPC(t *testing.T) { store.EXPECT().Commit(gomock.Any()) store.EXPECT().Rollback(gomock.Any()) tokenResult, _ := openid.NewBuilder().GivenName("Foo").FamilyName("Bar").Email("test@stacklok.com").Subject("subject1").Build() - jwt.EXPECT().ParseAndValidate(gomock.Any()).Return(tokenResult, nil) + validator.EXPECT().ParseAndValidate(gomock.Any()).Return(tokenResult, nil) return ctx }, @@ -171,9 +171,9 @@ func TestCreateUser_gRPC(t *testing.T) { { name: "Success with two pending Apps", req: &pb.CreateUserRequest{}, - buildStubs: func(ctx context.Context, store *mockdb.MockStore, jwt *mockjwt.MockJwtValidator, + buildStubs: func(ctx context.Context, store *mockdb.MockStore, validator *mockjwt.MockValidator, prov *mockprov.MockGitHubProviderService) context.Context { - ctx = auth.WithAuthTokenContext(ctx, keyCloakUserToken) + ctx = jwt.WithAuthTokenContext(ctx, keyCloakUserToken) tx := sql.Tx{} store.EXPECT().BeginTransaction().Return(&tx, nil) @@ -220,7 +220,7 @@ func TestCreateUser_gRPC(t *testing.T) { store.EXPECT().Commit(gomock.Any()) store.EXPECT().Rollback(gomock.Any()) tokenResult, _ := openid.NewBuilder().GivenName("Foo").FamilyName("Bar").Email("test@stacklok.com").Subject("subject1").Build() - jwt.EXPECT().ParseAndValidate(gomock.Any()).Return(tokenResult, nil) + validator.EXPECT().ParseAndValidate(gomock.Any()).Return(tokenResult, nil) return ctx }, @@ -254,7 +254,7 @@ func TestCreateUser_gRPC(t *testing.T) { defer ctrl.Finish() mockStore := mockdb.NewMockStore(ctrl) - mockJwtValidator := mockjwt.NewMockJwtValidator(ctrl) + mockJwtValidator := mockjwt.NewMockValidator(ctrl) mockProviders := mockprov.NewMockGitHubProviderService(ctrl) reqCtx := tc.buildStubs(ctx, mockStore, mockJwtValidator, mockProviders) crypeng := mockcrypto.NewMockEngine(ctrl) @@ -294,7 +294,7 @@ func TestDeleteUserDBMock(t *testing.T) { defer ctrl.Finish() mockStore := mockdb.NewMockStore(ctrl) - mockJwtValidator := mockjwt.NewMockJwtValidator(ctrl) + mockJwtValidator := mockjwt.NewMockValidator(ctrl) request := &pb.DeleteUserRequest{} @@ -372,14 +372,14 @@ func TestDeleteUser_gRPC(t *testing.T) { testCases := []struct { name string req *pb.DeleteUserRequest - buildStubs func(store *mockdb.MockStore, jwt *mockjwt.MockJwtValidator) + buildStubs func(store *mockdb.MockStore, jwt *mockjwt.MockValidator) checkResponse func(t *testing.T, res *pb.DeleteUserResponse, err error) expectedStatusCode codes.Code }{ { name: "Success", req: &pb.DeleteUserRequest{}, - buildStubs: func(store *mockdb.MockStore, jwt *mockjwt.MockJwtValidator) { + buildStubs: func(store *mockdb.MockStore, jwt *mockjwt.MockValidator) { tokenResult, _ := openid.NewBuilder().Subject("subject1").Build() jwt.EXPECT().ParseAndValidate(gomock.Any()).Return(tokenResult, nil) @@ -425,7 +425,7 @@ func TestDeleteUser_gRPC(t *testing.T) { defer ctrl.Finish() mockStore := mockdb.NewMockStore(ctrl) - mockJwtValidator := mockjwt.NewMockJwtValidator(ctrl) + mockJwtValidator := mockjwt.NewMockValidator(ctrl) tc.buildStubs(mockStore, mockJwtValidator) testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/controlplane/server.go b/internal/controlplane/server.go index 27390020a9..8563713be3 100644 --- a/internal/controlplane/server.go +++ b/internal/controlplane/server.go @@ -50,6 +50,7 @@ import ( "github.com/stacklok/minder/internal/assets" "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" "github.com/stacklok/minder/internal/authz" serverconfig "github.com/stacklok/minder/internal/config/server" "github.com/stacklok/minder/internal/controlplane/metrics" @@ -87,7 +88,7 @@ type Server struct { evt events.Publisher mt metrics.Metrics grpcServer *grpc.Server - jwt auth.JwtValidator + jwt jwt.Validator authzClient authz.Client idClient auth.Resolver cryptoEngine crypto.Engine @@ -127,7 +128,7 @@ func NewServer( evt events.Publisher, cfg *serverconfig.Config, serverMetrics metrics.Metrics, - jwt auth.JwtValidator, + jwtValidator jwt.Validator, cryptoEngine crypto.Engine, authzClient authz.Client, idClient auth.Resolver, @@ -148,7 +149,7 @@ func NewServer( cfg: cfg, evt: evt, cryptoEngine: cryptoEngine, - jwt: jwt, + jwt: jwtValidator, mt: serverMetrics, profiles: profileService, ruleTypes: ruleService, diff --git a/internal/controlplane/server_test.go b/internal/controlplane/server_test.go index 699ab71f8b..18c259f4f1 100644 --- a/internal/controlplane/server_test.go +++ b/internal/controlplane/server_test.go @@ -35,7 +35,7 @@ import ( mockdb "github.com/stacklok/minder/database/mock" "github.com/stacklok/minder/internal/auth" - mockjwt "github.com/stacklok/minder/internal/auth/mock" + mockjwt "github.com/stacklok/minder/internal/auth/jwt/mock" mockauthz "github.com/stacklok/minder/internal/authz/mock" serverconfig "github.com/stacklok/minder/internal/config/server" "github.com/stacklok/minder/internal/controlplane/metrics" @@ -95,7 +95,7 @@ func newDefaultServer( } ctrl := gomock.NewController(t) defer ctrl.Finish() - mockJwt := mockjwt.NewMockJwtValidator(ctrl) + mockJwt := mockjwt.NewMockValidator(ctrl) // Needed to keep these tests working as-is. // In future, beef up unit test coverage in the dependencies diff --git a/internal/flags/flags.go b/internal/flags/flags.go index ddab910298..38d609b08d 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -25,7 +25,7 @@ import ( gofeature "github.com/thomaspoignant/go-feature-flag" "github.com/thomaspoignant/go-feature-flag/retriever/fileretriever" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" config "github.com/stacklok/minder/internal/config/server" "github.com/stacklok/minder/internal/engine/engcontext" ) @@ -39,7 +39,7 @@ func fromContext(ctx context.Context) openfeature.EvaluationContext { // Note: engine.EntityFromContext is best-effort, so these values may be zero. ec := engcontext.EntityFromContext(ctx) return openfeature.NewEvaluationContext( - auth.GetUserSubjectFromContext(ctx), + jwt.GetUserSubjectFromContext(ctx), map[string]interface{}{ "project": ec.Project.ID.String(), "provider": ec.Provider.Name, diff --git a/internal/flags/flags_test.go b/internal/flags/flags_test.go index 94033e8384..f45c4468ad 100644 --- a/internal/flags/flags_test.go +++ b/internal/flags/flags_test.go @@ -27,7 +27,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwt/openid" "github.com/open-feature/go-sdk/openfeature" - "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" config "github.com/stacklok/minder/internal/config/server" "github.com/stacklok/minder/internal/engine/engcontext" ) @@ -93,7 +93,7 @@ test_flag: if err := userJWT.Set("sub", "user-1"); err != nil { t.Fatalf("failed to set sub claim: %v", err) } - ctx = auth.WithAuthTokenContext(ctx, userJWT) + ctx = jwt.WithAuthTokenContext(ctx, userJWT) ctx = engcontext.WithEntityContext(ctx, &engcontext.EntityContext{ Project: engcontext.Project{ID: uuid.New()}, Provider: engcontext.Provider{Name: "testing"}, diff --git a/internal/service/service.go b/internal/service/service.go index aa58dcf863..34e59fe71b 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -25,6 +25,7 @@ import ( "golang.org/x/sync/errgroup" "github.com/stacklok/minder/internal/auth" + "github.com/stacklok/minder/internal/auth/jwt" "github.com/stacklok/minder/internal/authz" serverconfig "github.com/stacklok/minder/internal/config/server" "github.com/stacklok/minder/internal/controlplane" @@ -64,7 +65,7 @@ func AllInOneServerService( ctx context.Context, cfg *serverconfig.Config, store db.Store, - jwt auth.JwtValidator, + jwtValidator jwt.Validator, restClientCache ratecache.RestClientCache, authzClient authz.Client, idClient auth.Resolver, @@ -144,7 +145,7 @@ func AllInOneServerService( evt, cfg, serverMetrics, - jwt, + jwtValidator, cryptoEngine, authzClient, idClient,