From 0ae415821bfe47bee82c476dcbe771c41f452320 Mon Sep 17 00:00:00 2001 From: nxtcoder17 Date: Thu, 14 Mar 2024 10:56:06 +0530 Subject: [PATCH 1/2] fix: fixes auth logout flow - deletes session, and cookie when logout happens --- .tools/nvim/__http__/auth/auth.graphql.yml | 9 +- .tools/nvim/__http__/console/apps.graphql.yml | 2 +- .../console/logs-and-metrics.rest.yml | 9 +- apps/auth/Taskfile.yml | 3 - apps/auth/internal/app/app.go | 48 +- apps/auth/internal/app/gqlgen.yml | 26 + .../internal/app/graph/entity.resolvers.go | 10 +- apps/auth/internal/app/graph/functions.go | 17 +- .../internal/app/graph/generated/generated.go | 479 ++++++++++++------ .../auth/internal/app/graph/resolver-utils.go | 16 + apps/auth/internal/app/graph/schema.graphqls | 31 +- .../internal/app/graph/schema.resolvers.go | 200 ++++---- apps/auth/internal/app/grpc-server.go | 3 +- apps/auth/internal/domain/domain.go | 16 +- apps/auth/internal/domain/impl.go | 69 +-- .../internal/{domain => entities}/entities.go | 34 +- apps/auth/main.go | 3 +- .../internal/domain/repository.go | 2 - common/session.go | 2 +- pkg/http-server/http-session.go | 19 +- pkg/kv/nats-kv-repo.go | 7 +- 21 files changed, 629 insertions(+), 376 deletions(-) create mode 100644 apps/auth/internal/app/graph/resolver-utils.go rename apps/auth/internal/{domain => entities}/entities.go (69%) diff --git a/.tools/nvim/__http__/auth/auth.graphql.yml b/.tools/nvim/__http__/auth/auth.graphql.yml index d4f1a46e4..9eeca4f49 100644 --- a/.tools/nvim/__http__/auth/auth.graphql.yml +++ b/.tools/nvim/__http__/auth/auth.graphql.yml @@ -36,6 +36,7 @@ query: | auth_login(email: $email, password: $password) { id userId + userId userEmail loginMethod userVerified @@ -245,11 +246,9 @@ variables: password: "{{.password}}" --- +label: Auth Logout query: |+ - query ListOAuthProviders { - auth_listOAuthProviders { - provider - enabled - } + mutation AuthLogout { + auth_logout } --- diff --git a/.tools/nvim/__http__/console/apps.graphql.yml b/.tools/nvim/__http__/console/apps.graphql.yml index d00f1e2d3..408ed8e36 100644 --- a/.tools/nvim/__http__/console/apps.graphql.yml +++ b/.tools/nvim/__http__/console/apps.graphql.yml @@ -38,7 +38,7 @@ query: |+ #graphql } variables: projectName: "{{.projectName}}" - envName: "{{.envName}}" + envName: "{{.environmentName}}" --- label: Get App diff --git a/.tools/nvim/__http__/console/logs-and-metrics.rest.yml b/.tools/nvim/__http__/console/logs-and-metrics.rest.yml index 96d75c0cc..e9f2fd057 100644 --- a/.tools/nvim/__http__/console/logs-and-metrics.rest.yml +++ b/.tools/nvim/__http__/console/logs-and-metrics.rest.yml @@ -1,12 +1,5 @@ --- global: - namespace: "sample-nxtcoder17" - accountName: kloudlite-dev - workspaceName: sample - - clusterName: "nova-303453" - clusterNamespace: "kl-account-nova" - endTime: '1707205625' startTime: '1707119214' --- @@ -27,7 +20,7 @@ query: label: Get Memory Metrics for App query: method: GET - url: 'http://console-api.kloudlite.svc.cluster.local:9100/observability/metrics/memory?cluster_name=ab-cluster-3&tracking_id=app-3ez2fpr-3oc8gqjib-ii5-pbat6d&step=5m' + url: 'http://console-api.kloudlite.svc.cluster.local:9100/observability/metrics/memory?cluster_name={{.clusterName}}&tracking_id=app-hg2hmvokiiuxih9jsq-ibqquumbl&step=5m' --- diff --git a/apps/auth/Taskfile.yml b/apps/auth/Taskfile.yml index 18ab1c535..e2121a5e9 100644 --- a/apps/auth/Taskfile.yml +++ b/apps/auth/Taskfile.yml @@ -13,9 +13,6 @@ tasks: - go run main.go --dev gql: - sources: - - graph/schema.graphqls - - gqlgen.yml dir: internal/app cmds: - go run github.com/99designs/gqlgen generate diff --git a/apps/auth/internal/app/app.go b/apps/auth/internal/app/app.go index 3dfacc7c8..8bb3b8282 100644 --- a/apps/auth/internal/app/app.go +++ b/apps/auth/internal/app/app.go @@ -2,6 +2,10 @@ package app import ( "context" + "github.com/kloudlite/api/apps/auth/internal/entities" + + "github.com/99designs/gqlgen/graphql" + "github.com/gofiber/fiber/v2" "github.com/kloudlite/api/pkg/nats" "go.uber.org/fx" "google.golang.org/grpc" @@ -23,19 +27,19 @@ type CommsClientConnection *grpc.ClientConn var Module = fx.Module( "app", - repos.NewFxMongoRepo[*domain.User]("users", "usr", domain.UserIndexes), - repos.NewFxMongoRepo[*domain.AccessToken]("access_tokens", "tkn", domain.AccessTokenIndexes), - repos.NewFxMongoRepo[*domain.RemoteLogin]("remote_logins", "rlgn", domain.RemoteTokenIndexes), + repos.NewFxMongoRepo[*entities.User]("users", "usr", entities.UserIndexes), + repos.NewFxMongoRepo[*entities.AccessToken]("access_tokens", "tkn", entities.AccessTokenIndexes), + repos.NewFxMongoRepo[*entities.RemoteLogin]("remote_logins", "rlgn", entities.RemoteTokenIndexes), fx.Provide( - func(ev *env.Env, jc *nats.JetstreamClient) (kv.Repo[*domain.VerifyToken], error) { + func(ev *env.Env, jc *nats.JetstreamClient) (kv.Repo[*entities.VerifyToken], error) { cxt := context.TODO() - return kv.NewNatsKVRepo[*domain.VerifyToken](cxt, ev.VerifyTokenKVBucket, jc) + return kv.NewNatsKVRepo[*entities.VerifyToken](cxt, ev.VerifyTokenKVBucket, jc) }, ), fx.Provide( - func(ev *env.Env, jc *nats.JetstreamClient) (kv.Repo[*domain.ResetPasswordToken], error) { + func(ev *env.Env, jc *nats.JetstreamClient) (kv.Repo[*entities.ResetPasswordToken], error) { cxt := context.TODO() - return kv.NewNatsKVRepo[*domain.ResetPasswordToken](cxt, ev.ResetPasswordTokenKVBucket, jc) + return kv.NewNatsKVRepo[*entities.ResetPasswordToken](cxt, ev.ResetPasswordTokenKVBucket, jc) }, ), @@ -63,9 +67,33 @@ var Module = fx.Module( ev *env.Env, repo kv.Repo[*common.AuthSession], ) { - schema := generated.NewExecutableSchema( - generated.Config{Resolvers: graph.NewResolver(d, ev)}, - ) + gqlConfig := generated.Config{Resolvers: graph.NewResolver(d, ev)} + gqlConfig.Directives.IsLoggedIn = func(ctx context.Context, obj any, next graphql.Resolver) (res interface{}, err error) { + sess := httpServer.GetSession[*common.AuthSession](ctx) + if sess == nil { + return nil, fiber.ErrUnauthorized + } + + return next(context.WithValue(ctx, "user-session", sess)) + } + + gqlConfig.Directives.IsLoggedInAndVerified = func(ctx context.Context, obj any, next graphql.Resolver) (res interface{}, err error) { + sess := httpServer.GetSession[*common.AuthSession](ctx) + if sess == nil { + return nil, fiber.ErrUnauthorized + } + + if !sess.UserVerified { + return nil, &fiber.Error{ + Code: fiber.StatusForbidden, + Message: "user's email is not verified", + } + } + + return next(context.WithValue(ctx, "user-session", sess)) + } + + schema := generated.NewExecutableSchema(gqlConfig) server.SetupGraphqlServer( schema, diff --git a/apps/auth/internal/app/gqlgen.yml b/apps/auth/internal/app/gqlgen.yml index 5c4e6b82e..d9642f9c2 100644 --- a/apps/auth/internal/app/gqlgen.yml +++ b/apps/auth/internal/app/gqlgen.yml @@ -1,6 +1,7 @@ # Where are all the schema files located? globs are supported eg src/**/*.graphqls schema: - graph/*.graphqls + - graph/struct-to-graphql/*.graphqls # Where should the generated server code go? exec: @@ -56,6 +57,31 @@ models: ID: model: - github.com/kloudlite/api/pkg/repos.ID + + # AuthSession: + # model: + # - github.com/kloudlite/api/common.AuthSession + + # User: + # model: + # - github.com/kloudlite/api/apps/auth/internal/entities.User + + Github__com___kloudlite___api___apps___auth___internal___entities__ProviderDetail: + model: + - github.com/kloudlite/api/apps/auth/internal/entities.ProviderDetail + + Github__com___kloudlite___api___apps___auth___internal___entities__ProviderDetailIn: + model: + - github.com/kloudlite/api/apps/auth/internal/entities.ProviderDetail + + Github__com___kloudlite___api___apps___auth___internal___entities__InvitationStatus: + model: + - github.com/kloudlite/api/apps/auth/internal/entities.InvitationStatus + + Github__com___kloudlite___api___apps___auth___internal___entities__UserMetadata: + model: + - github.com/kloudlite/api/apps/auth/internal/entities.UserMetadata + Int: model: - github.com/99designs/gqlgen/graphql.Int diff --git a/apps/auth/internal/app/graph/entity.resolvers.go b/apps/auth/internal/app/graph/entity.resolvers.go index dc112b483..49fba4915 100644 --- a/apps/auth/internal/app/graph/entity.resolvers.go +++ b/apps/auth/internal/app/graph/entity.resolvers.go @@ -6,16 +6,20 @@ package graph import ( "context" + "github.com/kloudlite/api/apps/auth/internal/app/graph/generated" "github.com/kloudlite/api/apps/auth/internal/app/graph/model" - "github.com/kloudlite/api/pkg/errors" "github.com/kloudlite/api/pkg/repos" ) // FindUserByID is the resolver for the findUserByID field. func (r *entityResolver) FindUserByID(ctx context.Context, id repos.ID) (*model.User, error) { - userEntity, err := r.d.GetUserById(ctx, id) - return userModelFromEntity(userEntity), errors.NewE(err) + u, err := r.d.GetUserById(ctx, id) + if err != nil { + return nil, err + } + + return userModelFromEntity(u), nil } // Entity returns generated.EntityResolver implementation. diff --git a/apps/auth/internal/app/graph/functions.go b/apps/auth/internal/app/graph/functions.go index 21b5c5a69..40579115b 100644 --- a/apps/auth/internal/app/graph/functions.go +++ b/apps/auth/internal/app/graph/functions.go @@ -2,11 +2,10 @@ package graph import ( "github.com/kloudlite/api/apps/auth/internal/app/graph/model" - "github.com/kloudlite/api/apps/auth/internal/domain" - "github.com/kloudlite/api/common" + "github.com/kloudlite/api/apps/auth/internal/entities" ) -func mapFromProviderDetail(detail *domain.ProviderDetail) map[string]any { +func mapFromProviderDetail(detail *entities.ProviderDetail) map[string]any { if detail == nil { return nil } @@ -16,17 +15,7 @@ func mapFromProviderDetail(detail *domain.ProviderDetail) map[string]any { } } -func sessionModelFromAuthSession(session *common.AuthSession) *model.Session { - return &model.Session{ - ID: session.Id, - UserID: session.UserId, - UserEmail: session.UserEmail, - LoginMethod: session.LoginMethod, - UserVerified: session.UserVerified, - } -} - -func userModelFromEntity(userEntity *domain.User) *model.User { +func userModelFromEntity(userEntity *entities.User) *model.User { return &model.User{ ID: userEntity.Id, Name: userEntity.Name, diff --git a/apps/auth/internal/app/graph/generated/generated.go b/apps/auth/internal/app/graph/generated/generated.go index 4c7878b36..71f7f8fb8 100644 --- a/apps/auth/internal/app/graph/generated/generated.go +++ b/apps/auth/internal/app/graph/generated/generated.go @@ -44,6 +44,8 @@ type ResolverRoot interface { } type DirectiveRoot struct { + IsLoggedIn func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) + IsLoggedInAndVerified func(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) } type ComplexityRoot struct { @@ -124,6 +126,8 @@ type MutationResolver interface { AuthCreateRemoteLogin(ctx context.Context, secret *string) (string, error) AuthLogin(ctx context.Context, email string, password string) (*model.Session, error) AuthSignup(ctx context.Context, name string, email string, password string) (*model.Session, error) + OAuthLogin(ctx context.Context, provider string, code string, state *string) (*model.Session, error) + OAuthAddLogin(ctx context.Context, provider string, state string, code string) (bool, error) AuthLogout(ctx context.Context) (bool, error) AuthSetMetadata(ctx context.Context, values map[string]interface{}) (*model.User, error) AuthClearMetadata(ctx context.Context) (*model.User, error) @@ -133,8 +137,6 @@ type MutationResolver interface { AuthChangeEmail(ctx context.Context, email string) (bool, error) AuthResendVerificationEmail(ctx context.Context) (bool, error) AuthChangePassword(ctx context.Context, currentPassword string, newPassword string) (bool, error) - OAuthLogin(ctx context.Context, provider string, code string, state *string) (*model.Session, error) - OAuthAddLogin(ctx context.Context, provider string, state string, code string) (bool, error) } type QueryResolver interface { AuthMe(ctx context.Context) (*model.User, error) @@ -619,15 +621,18 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er } var sources = []*ast.Source{ - {Name: "../schema.graphqls", Input: `scalar Date + {Name: "../schema.graphqls", Input: `directive @isLoggedIn on FIELD_DEFINITION +directive @isLoggedInAndVerified on FIELD_DEFINITION + +scalar Date scalar Json scalar ProviderDetail scalar URL scalar Any type RemoteLogin { - status: String! - authHeader: String + status: String! + authHeader: String } type OAuthProviderStatus { @@ -636,7 +641,7 @@ type OAuthProviderStatus { } type Query { - auth_me: User # public-access + auth_me: User @isLoggedIn auth_findByEmail(email: String!): User # public-access # Deprecate oAuth_requestLogin(provider: String!, state: String): URL! # public-access auth_getRemoteLogin(loginId: String!, secret: String!): RemoteLogin # public-access @@ -649,17 +654,21 @@ type Mutation { auth_login(email: String!, password: String!): Session # public-access auth_signup(name: String!, email: String!, password: String!): Session # public-access - auth_logout: Boolean! # user-access - auth_setMetadata(values: Json!): User! # user-access # Deprecate - auth_clearMetadata: User! # user-access # Deprecate - auth_verifyEmail(token: String!): Session! # public-access # TBD - auth_resetPassword(token: String!, password: String!): Boolean! # user-access - auth_requestResetPassword(email: String!): Boolean! # user-access - auth_changeEmail(email: String!): Boolean! # user-access #Done - auth_resendVerificationEmail: Boolean! # user-access #Done - auth_changePassword(currentPassword: String!, newPassword: String!): Boolean! # user-access #Done + oAuth_login(provider: String!, code: String!, state: String): Session! # public-access oAuth_addLogin(provider: String!, state: String!, code: String!): Boolean! # public-access + + auth_logout: Boolean! @isLoggedIn + + auth_setMetadata(values: Json!): User! @isLoggedInAndVerified + auth_clearMetadata: User! @isLoggedInAndVerified + + auth_verifyEmail(token: String!): Session! + auth_resetPassword(token: String!, password: String!): Boolean! + auth_requestResetPassword(email: String!): Boolean! + auth_changeEmail(email: String!): Boolean! @isLoggedInAndVerified + auth_resendVerificationEmail: Boolean! @isLoggedIn + auth_changePassword(currentPassword: String!, newPassword: String!): Boolean! @isLoggedInAndVerified } type Session { @@ -1453,6 +1462,128 @@ func (ec *executionContext) fieldContext_Mutation_auth_signup(ctx context.Contex return fc, nil } +func (ec *executionContext) _Mutation_oAuth_login(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_oAuth_login(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().OAuthLogin(rctx, fc.Args["provider"].(string), fc.Args["code"].(string), fc.Args["state"].(*string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.Session) + fc.Result = res + return ec.marshalNSession2ᚖgithubᚗcomᚋkloudliteᚋapiᚋappsᚋauthᚋinternalᚋappᚋgraphᚋmodelᚐSession(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_oAuth_login(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Session_id(ctx, field) + case "userId": + return ec.fieldContext_Session_userId(ctx, field) + case "userEmail": + return ec.fieldContext_Session_userEmail(ctx, field) + case "loginMethod": + return ec.fieldContext_Session_loginMethod(ctx, field) + case "userVerified": + return ec.fieldContext_Session_userVerified(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Session", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_oAuth_login_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + +func (ec *executionContext) _Mutation_oAuth_addLogin(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_oAuth_addLogin(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().OAuthAddLogin(rctx, fc.Args["provider"].(string), fc.Args["state"].(string), fc.Args["code"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_oAuth_addLogin(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_oAuth_addLogin_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + func (ec *executionContext) _Mutation_auth_logout(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_auth_logout(ctx, field) if err != nil { @@ -1466,8 +1597,28 @@ func (ec *executionContext) _Mutation_auth_logout(ctx context.Context, field gra } }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().AuthLogout(rctx) + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().AuthLogout(rctx) + } + directive1 := func(ctx context.Context) (interface{}, error) { + if ec.directives.IsLoggedIn == nil { + return nil, errors.New("directive isLoggedIn is not implemented") + } + return ec.directives.IsLoggedIn(ctx, nil, directive0) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(bool); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be bool`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -1510,8 +1661,28 @@ func (ec *executionContext) _Mutation_auth_setMetadata(ctx context.Context, fiel } }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().AuthSetMetadata(rctx, fc.Args["values"].(map[string]interface{})) + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().AuthSetMetadata(rctx, fc.Args["values"].(map[string]interface{})) + } + directive1 := func(ctx context.Context) (interface{}, error) { + if ec.directives.IsLoggedInAndVerified == nil { + return nil, errors.New("directive isLoggedInAndVerified is not implemented") + } + return ec.directives.IsLoggedInAndVerified(ctx, nil, directive0) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*model.User); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/kloudlite/api/apps/auth/internal/app/graph/model.User`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -1589,8 +1760,28 @@ func (ec *executionContext) _Mutation_auth_clearMetadata(ctx context.Context, fi } }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().AuthClearMetadata(rctx) + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().AuthClearMetadata(rctx) + } + directive1 := func(ctx context.Context) (interface{}, error) { + if ec.directives.IsLoggedInAndVerified == nil { + return nil, errors.New("directive isLoggedInAndVerified is not implemented") + } + return ec.directives.IsLoggedInAndVerified(ctx, nil, directive0) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*model.User); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/kloudlite/api/apps/auth/internal/app/graph/model.User`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -1834,8 +2025,28 @@ func (ec *executionContext) _Mutation_auth_changeEmail(ctx context.Context, fiel } }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().AuthChangeEmail(rctx, fc.Args["email"].(string)) + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().AuthChangeEmail(rctx, fc.Args["email"].(string)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + if ec.directives.IsLoggedInAndVerified == nil { + return nil, errors.New("directive isLoggedInAndVerified is not implemented") + } + return ec.directives.IsLoggedInAndVerified(ctx, nil, directive0) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(bool); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be bool`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -1889,8 +2100,28 @@ func (ec *executionContext) _Mutation_auth_resendVerificationEmail(ctx context.C } }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().AuthResendVerificationEmail(rctx) + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().AuthResendVerificationEmail(rctx) + } + directive1 := func(ctx context.Context) (interface{}, error) { + if ec.directives.IsLoggedIn == nil { + return nil, errors.New("directive isLoggedIn is not implemented") + } + return ec.directives.IsLoggedIn(ctx, nil, directive0) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(bool); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be bool`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -1933,130 +2164,28 @@ func (ec *executionContext) _Mutation_auth_changePassword(ctx context.Context, f } }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().AuthChangePassword(rctx, fc.Args["currentPassword"].(string), fc.Args["newPassword"].(string)) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().AuthChangePassword(rctx, fc.Args["currentPassword"].(string), fc.Args["newPassword"].(string)) } - return graphql.Null - } - res := resTmp.(bool) - fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Mutation_auth_changePassword(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Mutation", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) + directive1 := func(ctx context.Context) (interface{}, error) { + if ec.directives.IsLoggedInAndVerified == nil { + return nil, errors.New("directive isLoggedInAndVerified is not implemented") + } + return ec.directives.IsLoggedInAndVerified(ctx, nil, directive0) } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_auth_changePassword_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return - } - return fc, nil -} -func (ec *executionContext) _Mutation_oAuth_login(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Mutation_oAuth_login(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().OAuthLogin(rctx, fc.Args["provider"].(string), fc.Args["code"].(string), fc.Args["state"].(*string)) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) } - return graphql.Null - } - res := resTmp.(*model.Session) - fc.Result = res - return ec.marshalNSession2ᚖgithubᚗcomᚋkloudliteᚋapiᚋappsᚋauthᚋinternalᚋappᚋgraphᚋmodelᚐSession(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Mutation_oAuth_login(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Mutation", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_Session_id(ctx, field) - case "userId": - return ec.fieldContext_Session_userId(ctx, field) - case "userEmail": - return ec.fieldContext_Session_userEmail(ctx, field) - case "loginMethod": - return ec.fieldContext_Session_loginMethod(ctx, field) - case "userVerified": - return ec.fieldContext_Session_userVerified(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Session", field.Name) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) + if tmp == nil { + return nil, nil } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_oAuth_login_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return - } - return fc, nil -} - -func (ec *executionContext) _Mutation_oAuth_addLogin(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Mutation_oAuth_addLogin(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null + if data, ok := tmp.(bool); ok { + return data, nil } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().OAuthAddLogin(rctx, fc.Args["provider"].(string), fc.Args["state"].(string), fc.Args["code"].(string)) + return nil, fmt.Errorf(`unexpected type %T from directive, should be bool`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -2073,7 +2202,7 @@ func (ec *executionContext) _Mutation_oAuth_addLogin(ctx context.Context, field return ec.marshalNBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Mutation_oAuth_addLogin(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_auth_changePassword(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, @@ -2090,7 +2219,7 @@ func (ec *executionContext) fieldContext_Mutation_oAuth_addLogin(ctx context.Con } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_oAuth_addLogin_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_auth_changePassword_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return } @@ -2198,8 +2327,28 @@ func (ec *executionContext) _Query_auth_me(ctx context.Context, field graphql.Co } }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AuthMe(rctx) + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().AuthMe(rctx) + } + directive1 := func(ctx context.Context) (interface{}, error) { + if ec.directives.IsLoggedIn == nil { + return nil, errors.New("directive isLoggedIn is not implemented") + } + return ec.directives.IsLoggedIn(ctx, nil, directive0) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*model.User); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/kloudlite/api/apps/auth/internal/app/graph/model.User`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -5432,6 +5581,24 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return ec._Mutation_auth_signup(ctx, field) }) + case "oAuth_login": + + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_oAuth_login(ctx, field) + }) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "oAuth_addLogin": + + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_oAuth_addLogin(ctx, field) + }) + + if out.Values[i] == graphql.Null { + invalids++ + } case "auth_logout": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { @@ -5510,24 +5677,6 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return ec._Mutation_auth_changePassword(ctx, field) }) - if out.Values[i] == graphql.Null { - invalids++ - } - case "oAuth_login": - - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Mutation_oAuth_login(ctx, field) - }) - - if out.Values[i] == graphql.Null { - invalids++ - } - case "oAuth_addLogin": - - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Mutation_oAuth_addLogin(ctx, field) - }) - if out.Values[i] == graphql.Null { invalids++ } diff --git a/apps/auth/internal/app/graph/resolver-utils.go b/apps/auth/internal/app/graph/resolver-utils.go new file mode 100644 index 000000000..df231cdfe --- /dev/null +++ b/apps/auth/internal/app/graph/resolver-utils.go @@ -0,0 +1,16 @@ +package graph + +import ( + "context" + "fmt" + + "github.com/kloudlite/api/common" +) + +func GetUserSession(ctx context.Context) (*common.AuthSession, error) { + session, ok := ctx.Value("user-session").(*common.AuthSession) + if !ok { + return nil, fmt.Errorf(`request context is missing 'user-session' key`) + } + return session, nil +} diff --git a/apps/auth/internal/app/graph/schema.graphqls b/apps/auth/internal/app/graph/schema.graphqls index 1989ded4a..70af7ae00 100644 --- a/apps/auth/internal/app/graph/schema.graphqls +++ b/apps/auth/internal/app/graph/schema.graphqls @@ -1,3 +1,6 @@ +directive @isLoggedIn on FIELD_DEFINITION +directive @isLoggedInAndVerified on FIELD_DEFINITION + scalar Date scalar Json scalar ProviderDetail @@ -5,8 +8,8 @@ scalar URL scalar Any type RemoteLogin { - status: String! - authHeader: String + status: String! + authHeader: String } type OAuthProviderStatus { @@ -15,7 +18,7 @@ type OAuthProviderStatus { } type Query { - auth_me: User # public-access + auth_me: User @isLoggedIn auth_findByEmail(email: String!): User # public-access # Deprecate oAuth_requestLogin(provider: String!, state: String): URL! # public-access auth_getRemoteLogin(loginId: String!, secret: String!): RemoteLogin # public-access @@ -28,17 +31,21 @@ type Mutation { auth_login(email: String!, password: String!): Session # public-access auth_signup(name: String!, email: String!, password: String!): Session # public-access - auth_logout: Boolean! # user-access - auth_setMetadata(values: Json!): User! # user-access # Deprecate - auth_clearMetadata: User! # user-access # Deprecate - auth_verifyEmail(token: String!): Session! # public-access # TBD - auth_resetPassword(token: String!, password: String!): Boolean! # user-access - auth_requestResetPassword(email: String!): Boolean! # user-access - auth_changeEmail(email: String!): Boolean! # user-access #Done - auth_resendVerificationEmail: Boolean! # user-access #Done - auth_changePassword(currentPassword: String!, newPassword: String!): Boolean! # user-access #Done + oAuth_login(provider: String!, code: String!, state: String): Session! # public-access oAuth_addLogin(provider: String!, state: String!, code: String!): Boolean! # public-access + + auth_logout: Boolean! @isLoggedIn + + auth_setMetadata(values: Json!): User! @isLoggedInAndVerified + auth_clearMetadata: User! @isLoggedInAndVerified + + auth_verifyEmail(token: String!): Session! + auth_resetPassword(token: String!, password: String!): Boolean! + auth_requestResetPassword(email: String!): Boolean! + auth_changeEmail(email: String!): Boolean! @isLoggedInAndVerified + auth_resendVerificationEmail: Boolean! @isLoggedIn + auth_changePassword(currentPassword: String!, newPassword: String!): Boolean! @isLoggedInAndVerified } type Session { diff --git a/apps/auth/internal/app/graph/schema.resolvers.go b/apps/auth/internal/app/graph/schema.resolvers.go index 36eca004a..1750dc9fc 100644 --- a/apps/auth/internal/app/graph/schema.resolvers.go +++ b/apps/auth/internal/app/graph/schema.resolvers.go @@ -6,12 +6,11 @@ package graph import ( "context" - "github.com/kloudlite/api/apps/auth/internal/app/graph/generated" "github.com/kloudlite/api/apps/auth/internal/app/graph/model" "github.com/kloudlite/api/common" "github.com/kloudlite/api/pkg/errors" - klErrors "github.com/kloudlite/api/pkg/errors" + fn "github.com/kloudlite/api/pkg/functions" httpServer "github.com/kloudlite/api/pkg/http-server" "github.com/kloudlite/api/pkg/repos" ) @@ -19,79 +18,129 @@ import ( // AuthSetRemoteAuthHeader is the resolver for the auth_setRemoteAuthHeader field. func (r *mutationResolver) AuthSetRemoteAuthHeader(ctx context.Context, loginID string, authHeader *string) (bool, error) { err := r.d.SetRemoteLoginAuthHeader(ctx, repos.ID(loginID), *authHeader) - return err == nil, klErrors.NewE(err) + return err == nil, errors.NewE(err) } // AuthCreateRemoteLogin is the resolver for the auth_createRemoteLogin field. func (r *mutationResolver) AuthCreateRemoteLogin(ctx context.Context, secret *string) (string, error) { login, err := r.d.CreateRemoteLogin(ctx, *secret) if err != nil { - return "", klErrors.NewE(err) + return "", errors.NewE(err) } return string(login), nil } // AuthLogin is the resolver for the auth_login field. func (r *mutationResolver) AuthLogin(ctx context.Context, email string, password string) (*model.Session, error) { - sessionEntity, err := r.d.Login(ctx, email, password) + sess, err := r.d.Login(ctx, email, password) if err != nil { - return nil, klErrors.NewE(err) - } - - if !sessionEntity.UserVerified { - return nil, errors.New("user email not verified") + return nil, errors.NewE(err) } - httpServer.SetSession(ctx, sessionEntity) - return sessionModelFromAuthSession(sessionEntity), err + httpServer.SetSession(ctx, sess) + return &model.Session{ + ID: sess.Id, + UserID: sess.UserId, + UserEmail: sess.UserEmail, + LoginMethod: sess.LoginMethod, + UserVerified: sess.UserVerified, + }, nil } // AuthSignup is the resolver for the auth_signup field. func (r *mutationResolver) AuthSignup(ctx context.Context, name string, email string, password string) (*model.Session, error) { - sessionEntity, err := r.d.SignUp(ctx, name, email, password) + sess, err := r.d.SignUp(ctx, name, email, password) if err != nil { - return nil, klErrors.NewE(err) + return nil, errors.NewE(err) } - httpServer.SetSession(ctx, sessionEntity) - session := sessionModelFromAuthSession(sessionEntity) - return session, err + httpServer.SetSession(ctx, sess) + return &model.Session{ + ID: sess.Id, + UserID: sess.UserId, + UserEmail: sess.UserEmail, + LoginMethod: sess.LoginMethod, + UserVerified: sess.UserVerified, + }, nil } -// AuthLogout is the resolver for the auth_logout field. -func (r *mutationResolver) AuthLogout(ctx context.Context) (bool, error) { +// OAuthLogin is the resolver for the oAuth_login field. +func (r *mutationResolver) OAuthLogin(ctx context.Context, provider string, code string, state *string) (*model.Session, error) { + st := "" + if state != nil { + st = *state + } + sess, err := r.d.OauthLogin(ctx, provider, st, code) + if err != nil { + return nil, errors.NewEf(err, "could not create session") + } + httpServer.SetSession(ctx, sess) + return &model.Session{ + ID: sess.Id, + UserID: sess.UserId, + UserEmail: sess.UserEmail, + LoginMethod: sess.LoginMethod, + UserVerified: sess.UserVerified, + }, nil +} + +// OAuthAddLogin is the resolver for the oAuth_addLogin field. +func (r *mutationResolver) OAuthAddLogin(ctx context.Context, provider string, state string, code string) (bool, error) { session := httpServer.GetSession[*common.AuthSession](ctx) if session == nil { - return true, nil + return false, errors.New("user is not logged in") + } + return r.d.OauthAddLogin(ctx, session.UserId, provider, state, code) +} + +// AuthLogout is the resolver for the auth_logout field. +func (r *mutationResolver) AuthLogout(ctx context.Context) (bool, error) { + _, err := GetUserSession(ctx) + if err != nil { + return false, errors.NewEf(err, "getting user session") } + httpServer.DeleteSession(ctx) return true, nil } // AuthSetMetadata is the resolver for the auth_setMetadata field. func (r *mutationResolver) AuthSetMetadata(ctx context.Context, values map[string]interface{}) (*model.User, error) { - session := httpServer.GetSession[*common.AuthSession](ctx) - if session == nil { - return nil, errors.New("user not logged in") + sess, err := GetUserSession(ctx) + if err != nil { + return nil, errors.NewEf(err, "while getting user session") + } + + u, err := r.d.SetUserMetadata(ctx, sess.UserId, values) + if err != nil { + return nil, err } - userEntity, err := r.d.SetUserMetadata(ctx, session.UserId, values) - return userModelFromEntity(userEntity), klErrors.NewE(err) + return userModelFromEntity(u), nil } // AuthClearMetadata is the resolver for the auth_clearMetadata field. func (r *mutationResolver) AuthClearMetadata(ctx context.Context) (*model.User, error) { - session := httpServer.GetSession[*common.AuthSession](ctx) - if session == nil { - return nil, errors.New("user not logged in") + sess, err := GetUserSession(ctx) + if err != nil { + return nil, errors.NewEf(err, "while getting user session") + } + u, err := r.d.ClearUserMetadata(ctx, sess.UserId) + if err != nil { + return nil, err } - userEntity, err := r.d.ClearUserMetadata(ctx, session.UserId) - return userModelFromEntity(userEntity), klErrors.NewE(err) + return userModelFromEntity(u), nil } // AuthVerifyEmail is the resolver for the auth_verifyEmail field. func (r *mutationResolver) AuthVerifyEmail(ctx context.Context, token string) (*model.Session, error) { - sessionEntity, err := r.d.VerifyEmail(ctx, token) - httpServer.SetSession(ctx, sessionEntity) - return sessionModelFromAuthSession(sessionEntity), klErrors.NewE(err) + sess, err := r.d.VerifyEmail(ctx, token) + httpServer.SetSession(ctx, sess) + return &model.Session{ + ID: sess.Id, + UserID: sess.UserId, + UserEmail: sess.UserEmail, + LoginMethod: sess.LoginMethod, + UserVerified: sess.UserVerified, + }, err } // AuthResetPassword is the resolver for the auth_resetPassword field. @@ -106,85 +155,66 @@ func (r *mutationResolver) AuthRequestResetPassword(ctx context.Context, email s // AuthChangeEmail is the resolver for the auth_changeEmail field. func (r *mutationResolver) AuthChangeEmail(ctx context.Context, email string) (bool, error) { - session := httpServer.GetSession[*common.AuthSession](ctx) - if session == nil { - return false, errors.New("user is not logged in") + sess, err := GetUserSession(ctx) + if err != nil { + return false, errors.NewEf(err, "while getting user session") } - return r.d.ChangeEmail(ctx, session.UserId, email) + return r.d.ChangeEmail(ctx, sess.UserId, email) } // AuthResendVerificationEmail is the resolver for the auth_resendVerificationEmail field. func (r *mutationResolver) AuthResendVerificationEmail(ctx context.Context) (bool, error) { - session := httpServer.GetSession[*common.AuthSession](ctx) - if session == nil { - return false, errors.New("user is not logged in") + sess, err := GetUserSession(ctx) + if err != nil { + return false, errors.NewEf(err, "while getting user session") } - return r.d.ResendVerificationEmail(ctx, session.UserId) + + return r.d.ResendVerificationEmail(ctx, sess.UserId) } // AuthChangePassword is the resolver for the auth_changePassword field. func (r *mutationResolver) AuthChangePassword(ctx context.Context, currentPassword string, newPassword string) (bool, error) { - session := httpServer.GetSession[*common.AuthSession](ctx) - if session == nil { - return false, errors.New("user is not logged in") - } - return r.d.ChangePassword(ctx, session.UserId, currentPassword, newPassword) -} - -// OAuthLogin is the resolver for the oAuth_login field. -func (r *mutationResolver) OAuthLogin(ctx context.Context, provider string, code string, state *string) (*model.Session, error) { - st := "" - if state != nil { - st = *state - } - sessionEntity, err := r.d.OauthLogin(ctx, provider, st, code) + sess, err := GetUserSession(ctx) if err != nil { - return nil, klErrors.NewEf(err, "could not create session") + return false, errors.NewEf(err, "while getting user session") } - httpServer.SetSession(ctx, sessionEntity) - return sessionModelFromAuthSession(sessionEntity), klErrors.NewE(err) -} -// OAuthAddLogin is the resolver for the oAuth_addLogin field. -func (r *mutationResolver) OAuthAddLogin(ctx context.Context, provider string, state string, code string) (bool, error) { - session := httpServer.GetSession[*common.AuthSession](ctx) - if session == nil { - return false, errors.New("user is not logged in") - } - return r.d.OauthAddLogin(ctx, session.UserId, provider, state, code) + return r.d.ChangePassword(ctx, sess.UserId, currentPassword, newPassword) } // AuthMe is the resolver for the auth_me field. func (r *queryResolver) AuthMe(ctx context.Context) (*model.User, error) { - session := httpServer.GetSession[*common.AuthSession](ctx) - if session == nil { - return nil, errors.New("user not logged in") + sess, err := GetUserSession(ctx) + if err != nil { + return nil, errors.NewEf(err, "while getting user session") } - u, err := r.d.GetUserById(ctx, session.UserId) + + u, err := r.d.GetUserById(ctx, sess.UserId) if err != nil { - return nil, klErrors.NewE(err) + return nil, errors.NewE(err) } if u == nil { - return nil, klErrors.Newf("user(email=%s) does not exist in system", session.UserEmail) + return nil, errors.Newf("user(email=%s) does not exist in system", sess.UserEmail) } - return userModelFromEntity(u), klErrors.NewE(err) + return userModelFromEntity(u), nil } // AuthFindByEmail is the resolver for the auth_findByEmail field. func (r *queryResolver) AuthFindByEmail(ctx context.Context, email string) (*model.User, error) { - userEntity, err := r.d.GetUserByEmail(ctx, email) - return userModelFromEntity(userEntity), klErrors.NewE(err) + u, err := r.d.GetUserByEmail(ctx, email) + if err != nil { + return nil, err + } + + return userModelFromEntity(u), nil } // OAuthRequestLogin is the resolver for the oAuth_requestLogin field. func (r *queryResolver) OAuthRequestLogin(ctx context.Context, provider string, state *string) (string, error) { - _state := "" - if state != nil { - _state = *state - } - url, err := r.d.OauthRequestLogin(ctx, provider, _state) + pstate := fn.DefaultIfNil(state, "") + url, err := r.d.OauthRequestLogin(ctx, provider, pstate) if err != nil { - return "", klErrors.NewE(err) + return "", errors.NewE(err) } return url, nil } @@ -193,7 +223,7 @@ func (r *queryResolver) OAuthRequestLogin(ctx context.Context, provider string, func (r *queryResolver) AuthGetRemoteLogin(ctx context.Context, loginID string, secret string) (*model.RemoteLogin, error) { login, err := r.d.GetRemoteLogin(ctx, repos.ID(loginID), secret) if err != nil { - return nil, klErrors.NewE(err) + return nil, errors.NewE(err) } return &model.RemoteLogin{ Status: string(login.LoginStatus), @@ -228,7 +258,5 @@ func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResol // Query returns generated.QueryResolver implementation. func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } -type ( - mutationResolver struct{ *Resolver } - queryResolver struct{ *Resolver } -) +type mutationResolver struct{ *Resolver } +type queryResolver struct{ *Resolver } diff --git a/apps/auth/internal/app/grpc-server.go b/apps/auth/internal/app/grpc-server.go index 4fced8aeb..ac7b2b40c 100644 --- a/apps/auth/internal/app/grpc-server.go +++ b/apps/auth/internal/app/grpc-server.go @@ -2,6 +2,7 @@ package app import ( "context" + "github.com/kloudlite/api/apps/auth/internal/entities" "github.com/kloudlite/api/pkg/repos" "github.com/kloudlite/api/apps/auth/internal/domain" @@ -29,7 +30,7 @@ func (a *authGrpcServer) GetUser(ctx context.Context, in *auth.GetUserIn) (*auth }, nil } -func (a *authGrpcServer) FromAccToken(token domain.AccessToken) *auth.AccessTokenOut { +func (a *authGrpcServer) FromAccToken(token entities.AccessToken) *auth.AccessTokenOut { return &auth.AccessTokenOut{ Id: string(token.Id), UserId: string(token.UserId), diff --git a/apps/auth/internal/domain/domain.go b/apps/auth/internal/domain/domain.go index 22d5963b9..973c0a857 100644 --- a/apps/auth/internal/domain/domain.go +++ b/apps/auth/internal/domain/domain.go @@ -2,29 +2,31 @@ package domain import ( "context" + "github.com/kloudlite/api/apps/auth/internal/entities" + "github.com/kloudlite/api/common" "github.com/kloudlite/api/pkg/repos" ) type Domain interface { SetRemoteLoginAuthHeader(ctx context.Context, loginId repos.ID, authHeader string) error - GetRemoteLogin(ctx context.Context, loginId repos.ID, secret string) (*RemoteLogin, error) + GetRemoteLogin(ctx context.Context, loginId repos.ID, secret string) (*entities.RemoteLogin, error) CreateRemoteLogin(ctx context.Context, secret string) (repos.ID, error) Login(ctx context.Context, email string, password string) (*common.AuthSession, error) SignUp(ctx context.Context, name string, email string, password string) (*common.AuthSession, error) - EnsureUserByEmail(ctx context.Context, email string) (*User, error) - GetUserById(ctx context.Context, id repos.ID) (*User, error) - GetUserByEmail(ctx context.Context, email string) (*User, error) - SetUserMetadata(ctx context.Context, userId repos.ID, metadata UserMetadata) (*User, error) - ClearUserMetadata(ctx context.Context, id repos.ID) (*User, error) + EnsureUserByEmail(ctx context.Context, email string) (*entities.User, error) + GetUserById(ctx context.Context, id repos.ID) (*entities.User, error) + GetUserByEmail(ctx context.Context, email string) (*entities.User, error) + SetUserMetadata(ctx context.Context, userId repos.ID, metadata entities.UserMetadata) (*entities.User, error) + ClearUserMetadata(ctx context.Context, id repos.ID) (*entities.User, error) VerifyEmail(ctx context.Context, token string) (*common.AuthSession, error) ResetPassword(ctx context.Context, token string, password string) (bool, error) RequestResetPassword(ctx context.Context, email string) (bool, error) ChangeEmail(ctx context.Context, id repos.ID, email string) (bool, error) ResendVerificationEmail(ctx context.Context, userId repos.ID) (bool, error) ChangePassword(ctx context.Context, id repos.ID, currentPassword string, newPassword string) (bool, error) - GetAccessToken(ctx context.Context, provider string, userId string, tokenId string) (*AccessToken, error) + GetAccessToken(ctx context.Context, provider string, userId string, tokenId string) (*entities.AccessToken, error) GetLoginDetails(ctx context.Context, provider string, state *string) (string, error) InviteUser(ctx context.Context, email string, name string) (repos.ID, error) OauthRequestLogin(ctx context.Context, provider string, state string) (string, error) diff --git a/apps/auth/internal/domain/impl.go b/apps/auth/internal/domain/impl.go index 4f79c7af8..fa16ae73e 100644 --- a/apps/auth/internal/domain/impl.go +++ b/apps/auth/internal/domain/impl.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/kloudlite/api/apps/auth/internal/entities" "strings" "time" @@ -42,16 +43,16 @@ func newAuthSession(userId repos.ID, userEmail string, userName string, userVeri } type domainI struct { - userRepo repos.DbRepo[*User] - accessTokenRepo repos.DbRepo[*AccessToken] + userRepo repos.DbRepo[*entities.User] + accessTokenRepo repos.DbRepo[*entities.AccessToken] commsClient comms.CommsClient - verifyTokenRepo kv.Repo[*VerifyToken] - resetTokenRepo kv.Repo[*ResetPasswordToken] + verifyTokenRepo kv.Repo[*entities.VerifyToken] + resetTokenRepo kv.Repo[*entities.ResetPasswordToken] logger logging.Logger github Github gitlab Gitlab google Google - remoteLoginRepo repos.DbRepo[*RemoteLogin] + remoteLoginRepo repos.DbRepo[*entities.RemoteLogin] } func (d *domainI) SetRemoteLoginAuthHeader(ctx context.Context, loginId repos.ID, authHeader string) error { @@ -60,7 +61,7 @@ func (d *domainI) SetRemoteLoginAuthHeader(ctx context.Context, loginId repos.ID return errors.NewEf(err, "could not find remote login") } remoteLogin.AuthHeader = authHeader - remoteLogin.LoginStatus = LoginStatusSucceeded + remoteLogin.LoginStatus = entities.LoginStatusSucceeded _, err = d.remoteLoginRepo.UpdateById(ctx, loginId, remoteLogin) if err != nil { return errors.NewEf(err, "could not update remote login") @@ -68,7 +69,7 @@ func (d *domainI) SetRemoteLoginAuthHeader(ctx context.Context, loginId repos.ID return nil } -func (d *domainI) GetRemoteLogin(ctx context.Context, loginId repos.ID, secret string) (*RemoteLogin, error) { +func (d *domainI) GetRemoteLogin(ctx context.Context, loginId repos.ID, secret string) (*entities.RemoteLogin, error) { id, err := d.remoteLoginRepo.FindById(ctx, loginId) if err != nil { return nil, errors.NewEf(err, "could not find remote login") @@ -81,8 +82,8 @@ func (d *domainI) GetRemoteLogin(ctx context.Context, loginId repos.ID, secret s func (d *domainI) CreateRemoteLogin(ctx context.Context, secret string) (repos.ID, error) { create, err := d.remoteLoginRepo.Create( - ctx, &RemoteLogin{ - LoginStatus: LoginStatusPending, + ctx, &entities.RemoteLogin{ + LoginStatus: entities.LoginStatusPending, Secret: secret, }, ) @@ -92,9 +93,9 @@ func (d *domainI) CreateRemoteLogin(ctx context.Context, secret string) (repos.I return create.Id, nil } -func (d *domainI) EnsureUserByEmail(ctx context.Context, email string) (*User, error) { +func (d *domainI) EnsureUserByEmail(ctx context.Context, email string) (*entities.User, error) { u, err := d.userRepo.Create( - ctx, &User{ + ctx, &entities.User{ Email: email, }, ) @@ -144,11 +145,11 @@ func (d *domainI) OauthAddLogin(ctx context.Context, userId repos.ID, provider s } -func (d *domainI) GetUserById(ctx context.Context, id repos.ID) (*User, error) { +func (d *domainI) GetUserById(ctx context.Context, id repos.ID) (*entities.User, error) { return d.userRepo.FindById(ctx, id) } -func (d *domainI) GetUserByEmail(ctx context.Context, email string) (*User, error) { +func (d *domainI) GetUserByEmail(ctx context.Context, email string) (*entities.User, error) { return d.userRepo.FindOne(ctx, repos.Filter{"email": email}) } @@ -190,7 +191,7 @@ func (d *domainI) SignUp(ctx context.Context, name string, email string, passwor salt := generateId("salt") sum := md5.Sum([]byte(password + salt)) user, err := d.userRepo.Create( - ctx, &User{ + ctx, &entities.User{ Name: name, Email: email, Password: hex.EncodeToString(sum[:]), @@ -223,7 +224,7 @@ func (d *domainI) InviteUser(ctx context.Context, email string, name string) (re panic("implement me") } -func (d *domainI) SetUserMetadata(ctx context.Context, userId repos.ID, metadata UserMetadata) (*User, error) { +func (d *domainI) SetUserMetadata(ctx context.Context, userId repos.ID, metadata entities.UserMetadata) (*entities.User, error) { user, err := d.userRepo.FindById(ctx, userId) if err != nil { return nil, errors.NewE(err) @@ -236,7 +237,7 @@ func (d *domainI) SetUserMetadata(ctx context.Context, userId repos.ID, metadata return updated, nil } -func (d *domainI) ClearUserMetadata(ctx context.Context, userId repos.ID) (*User, error) { +func (d *domainI) ClearUserMetadata(ctx context.Context, userId repos.ID) (*entities.User, error) { user, err := d.userRepo.FindById(ctx, userId) if err != nil { return nil, errors.NewE(err) @@ -315,7 +316,7 @@ func (d *domainI) RequestResetPassword(ctx context.Context, email string) (bool, err = d.resetTokenRepo.SetWithExpiry( ctx, resetToken, - &ResetPasswordToken{Token: resetToken, UserId: one.Id}, + &entities.ResetPasswordToken{Token: resetToken, UserId: one.Id}, time.Second*24*60*60, ) if err != nil { @@ -394,7 +395,7 @@ func (d *domainI) OauthRequestLogin(ctx context.Context, provider string, state return "", errors.Newf("Unsupported provider (%v)", provider) } -func (d *domainI) addOAuthLogin(ctx context.Context, provider string, token *oauth2.Token, u *User, avatarUrl *string) (*User, error) { +func (d *domainI) addOAuthLogin(ctx context.Context, provider string, token *oauth2.Token, u *entities.User, avatarUrl *string) (*entities.User, error) { user, err := d.userRepo.FindOne(ctx, repos.Filter{"email": u.Email}) if err != nil { return nil, errors.NewEf(err, "could not find user") @@ -416,7 +417,7 @@ func (d *domainI) addOAuthLogin(ctx context.Context, provider string, token *oau } } t, err := d.accessTokenRepo.Upsert( - ctx, repos.Filter{"email": user.Email, "provider": provider}, &AccessToken{ + ctx, repos.Filter{"email": user.Email, "provider": provider}, &entities.AccessToken{ UserId: user.Id, Email: user.Email, Provider: provider, @@ -428,7 +429,7 @@ func (d *domainI) addOAuthLogin(ctx context.Context, provider string, token *oau return nil, errors.NewEf(err, "could not store access token in repo") } - p := &ProviderDetail{TokenId: t.Id, Avatar: avatarUrl} + p := &entities.ProviderDetail{TokenId: t.Id, Avatar: avatarUrl} if provider == constants.ProviderGithub { user.ProviderGithub = p @@ -450,7 +451,7 @@ func (d *domainI) addOAuthLogin(ctx context.Context, provider string, token *oau return user, nil } -func (d *domainI) afterOAuthLogin(ctx context.Context, provider string, token *oauth2.Token, newUser *User, avatarUrl *string) (*common.AuthSession, error) { +func (d *domainI) afterOAuthLogin(ctx context.Context, provider string, token *oauth2.Token, newUser *entities.User, avatarUrl *string) (*common.AuthSession, error) { user, err := d.addOAuthLogin(ctx, provider, token, newUser, avatarUrl) if err != nil { return nil, errors.NewE(err) @@ -490,7 +491,7 @@ func (d *domainI) OauthLogin(ctx context.Context, provider string, state string, return u.GetLogin() }() - user := &User{ + user := &entities.User{ Name: name, Avatar: u.AvatarURL, Email: email, @@ -505,7 +506,7 @@ func (d *domainI) OauthLogin(ctx context.Context, provider string, state string, return nil, errors.NewEf(err, "could not login to gitlab") } - user := &User{ + user := &entities.User{ Name: u.Name, Avatar: &u.AvatarURL, Email: u.Email, @@ -521,7 +522,7 @@ func (d *domainI) OauthLogin(ctx context.Context, provider string, state string, return nil, errors.NewEf(err, "could not login to google") } - user := &User{ + user := &entities.User{ Name: u.Name, Avatar: u.AvatarURL, Email: u.Email, @@ -544,7 +545,7 @@ func (gl *domainI) Hash(t *oauth2.Token) (string, error) { return b64.StdEncoding.EncodeToString(b), nil } -func (d *domainI) GetAccessToken(ctx context.Context, provider string, userId string, tokenId string) (*AccessToken, error) { +func (d *domainI) GetAccessToken(ctx context.Context, provider string, userId string, tokenId string) (*entities.AccessToken, error) { if tokenId == "" && (provider == "" || userId == "") { return nil, errors.Newf("bad params: [tokenId, (provider, userId)]") } @@ -601,7 +602,7 @@ func (d *domainI) GetAccessToken(ctx context.Context, provider string, userId st return accToken, nil } -func (d *domainI) sendResetPasswordEmail(ctx context.Context, token string, user *User) error { +func (d *domainI) sendResetPasswordEmail(ctx context.Context, token string, user *entities.User) error { _, err := d.commsClient.SendPasswordResetEmail( ctx, &comms.PasswordResetEmailInput{ Email: user.Email, @@ -615,7 +616,7 @@ func (d *domainI) sendResetPasswordEmail(ctx context.Context, token string, user return nil } -func (d *domainI) sendVerificationEmail(ctx context.Context, token string, user *User) error { +func (d *domainI) sendVerificationEmail(ctx context.Context, token string, user *entities.User) error { _, err := d.commsClient.SendVerificationEmail( ctx, &comms.VerificationEmailInput{ Email: user.Email, @@ -629,10 +630,10 @@ func (d *domainI) sendVerificationEmail(ctx context.Context, token string, user return nil } -func (d *domainI) generateAndSendVerificationToken(ctx context.Context, user *User) error { +func (d *domainI) generateAndSendVerificationToken(ctx context.Context, user *entities.User) error { verificationToken := generateId("invite") err := d.verifyTokenRepo.SetWithExpiry( - ctx, verificationToken, &VerifyToken{ + ctx, verificationToken, &entities.VerifyToken{ Token: verificationToken, UserId: user.Id, }, time.Second*24*60*60, @@ -648,11 +649,11 @@ func (d *domainI) generateAndSendVerificationToken(ctx context.Context, user *Us } func fxDomain( - userRepo repos.DbRepo[*User], - accessTokenRepo repos.DbRepo[*AccessToken], - remoteLoginRepo repos.DbRepo[*RemoteLogin], - verifyTokenRepo kv.Repo[*VerifyToken], - resetTokenRepo kv.Repo[*ResetPasswordToken], + userRepo repos.DbRepo[*entities.User], + accessTokenRepo repos.DbRepo[*entities.AccessToken], + remoteLoginRepo repos.DbRepo[*entities.RemoteLogin], + verifyTokenRepo kv.Repo[*entities.VerifyToken], + resetTokenRepo kv.Repo[*entities.ResetPasswordToken], github Github, gitlab Gitlab, google Google, diff --git a/apps/auth/internal/domain/entities.go b/apps/auth/internal/entities/entities.go similarity index 69% rename from apps/auth/internal/domain/entities.go rename to apps/auth/internal/entities/entities.go index 017a6f278..719a1e700 100644 --- a/apps/auth/internal/domain/entities.go +++ b/apps/auth/internal/entities/entities.go @@ -1,4 +1,4 @@ -package domain +package entities import ( "time" @@ -10,10 +10,10 @@ import ( type InvitationStatus string const ( - InvitationAccepted = InvitationStatus("accepted") - InvitationRejected = InvitationStatus("rejected") - InvitationNone = InvitationStatus("none") - InvitationSent = InvitationStatus("sent") + InvitationStatusAccepted InvitationStatus = "accepted" + InvitationStatusRejected InvitationStatus = "rejected" + InvitationStatusNone InvitationStatus = "none" + InvitationStatusSend InvitationStatus = "sent" ) type UserMetadata map[string]any @@ -32,19 +32,19 @@ type Session struct { } type User struct { - repos.BaseEntity `bson:",inline"` + repos.BaseEntity `json:",inline" graphql:"noinput"` Name string `json:"name"` Avatar *string `json:"avatar"` - ProviderGithub *ProviderDetail `json:"provider_github" bson:"provider_github"` - ProviderGitlab *ProviderDetail `json:"provider_gitlab" bson:"provider_gitlab"` - ProviderGoogle *ProviderDetail `json:"provider_google" bson:"provider_google"` + ProviderGithub *ProviderDetail `json:"provider_github"` + ProviderGitlab *ProviderDetail `json:"provider_gitlab"` + ProviderGoogle *ProviderDetail `json:"provider_google"` Email string `json:"email"` - Password string `json:"password"` + Password string `json:"password" graphql:"ignore"` InvitationStatus InvitationStatus `json:"invite"` - Verified bool `json:"verified"` + Verified bool `json:"verified" graphql:"noinput"` Metadata UserMetadata `json:"metadata"` Joined time.Time `json:"joined"` - PasswordSalt string `json:"password_salt"` + PasswordSalt string `json:"password_salt" graphql:"ignore"` } var UserIndexes = []repos.IndexField{ @@ -64,11 +64,11 @@ var UserIndexes = []repos.IndexField{ type AccessToken struct { repos.BaseEntity `bson:",inline"` - UserId repos.ID `json:"user_id" bson:"user_id"` - Email string `json:"email" bson:"email"` - Provider string `json:"provider" bson:"provider"` - Token *oauth2.Token `json:"token" bson:"token"` - Data map[string]any `json:"data" bson:"data"` + UserId repos.ID `json:"user_id"` + Email string `json:"email"` + Provider string `json:"provider"` + Token *oauth2.Token `json:"token"` + Data map[string]any `json:"data"` } var AccessTokenIndexes = []repos.IndexField{ diff --git a/apps/auth/main.go b/apps/auth/main.go index a79293e97..99f40d71f 100644 --- a/apps/auth/main.go +++ b/apps/auth/main.go @@ -3,9 +3,10 @@ package main import ( "context" "flag" - "github.com/kloudlite/api/pkg/errors" "time" + "github.com/kloudlite/api/pkg/errors" + "go.uber.org/fx" "github.com/kloudlite/api/apps/auth/internal/env" diff --git a/apps/container-registry/internal/domain/repository.go b/apps/container-registry/internal/domain/repository.go index 84138ae8b..2d9934dca 100644 --- a/apps/container-registry/internal/domain/repository.go +++ b/apps/container-registry/internal/domain/repository.go @@ -61,7 +61,6 @@ func (d *Impl) CreateRepository(ctx RegistryContext, repoName string) (*entities // DeleteRepository implements Domain. func (d *Impl) DeleteRepository(ctx RegistryContext, repoName string) error { - co, err := d.iamClient.Can(ctx, &iam.CanIn{ UserId: string(ctx.UserId), ResourceRefs: []string{ @@ -104,7 +103,6 @@ func (d *Impl) DeleteRepository(ctx RegistryContext, repoName string) error { } func (d *Impl) DeleteRepositoryDigest(ctx RegistryContext, repoName string, digest string) error { - co, err := d.iamClient.Can(ctx, &iam.CanIn{ UserId: string(ctx.UserId), ResourceRefs: []string{ diff --git a/common/session.go b/common/session.go index 3e73cc1b8..f925c8a76 100644 --- a/common/session.go +++ b/common/session.go @@ -13,4 +13,4 @@ type AuthSession struct { LoginMethod string `json:"login_method"` } -type Json map[string]any \ No newline at end of file +type Json map[string]any diff --git a/pkg/http-server/http-session.go b/pkg/http-server/http-session.go index a5cb775af..9edda20aa 100644 --- a/pkg/http-server/http-session.go +++ b/pkg/http-server/http-session.go @@ -36,9 +36,9 @@ func NewSessionMiddleware( var get any get, err := repo.Get(ctx.Context(), key) if err != nil { - if !repo.ErrKeyNotFound(err) { + if !repo.ErrKeyNotFound(err) { return errors.NewE(err) - } + } } if get != nil { @@ -79,6 +79,18 @@ func NewSessionMiddleware( fmt.Println("[ERROR]", err) } } + ctx.Cookie(&fiber.Cookie{ + Name: cookieName, + Value: "expired", + Path: "/", + Domain: fmt.Sprintf("%v", cookieDomain), + Expires: time.Now().Add(-1 * time.Minute), + MaxAge: 0, + Secure: true, + HTTPOnly: true, + // SameSite: http.SameSiteStrictMode, + SameSite: fiber.CookieSameSiteNoneMode, + }) }, ), ) @@ -126,7 +138,8 @@ func SetSession[T repos.Entity](ctx context.Context, session T) { } func DeleteSession(ctx context.Context) { - deleteSession, ok := ctx.Value("delete-session").(func()) + userContext := ctx.Value(userContextKey).(context.Context) + deleteSession, ok := userContext.Value("delete-session").(func()) if !ok { return } diff --git a/pkg/kv/nats-kv-repo.go b/pkg/kv/nats-kv-repo.go index 7e7990e47..db1d49d2b 100644 --- a/pkg/kv/nats-kv-repo.go +++ b/pkg/kv/nats-kv-repo.go @@ -4,11 +4,12 @@ import ( "context" "encoding/json" "fmt" - "github.com/kloudlite/api/pkg/nats" - "github.com/nats-io/nats.go/jetstream" "strings" "time" + "github.com/kloudlite/api/pkg/nats" + "github.com/nats-io/nats.go/jetstream" + "github.com/kloudlite/api/pkg/errors" ) @@ -87,7 +88,7 @@ func (r *natsKVRepo[T]) SetWithExpiry(c context.Context, _key string, value T, d } func (r *natsKVRepo[T]) Drop(c context.Context, key string) error { - return r.keyValue.Delete(c, key) + return r.keyValue.Delete(c, sanitiseKey(key)) } func (r *natsKVRepo[T]) ErrNoRecord(err error) bool { From 294dbcbb389fd59b58d8b02a7daebcfa6fb8dbae Mon Sep 17 00:00:00 2001 From: nxtcoder17 Date: Thu, 14 Mar 2024 15:09:49 +0530 Subject: [PATCH 2/2] feat(iam): account member role, can now do much of what admin, and owner can do --- apps/iam/internal/app/action-role-binding.go | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/iam/internal/app/action-role-binding.go b/apps/iam/internal/app/action-role-binding.go index 3b10b0437..c4064f03a 100644 --- a/apps/iam/internal/app/action-role-binding.go +++ b/apps/iam/internal/app/action-role-binding.go @@ -35,7 +35,7 @@ var roleBindings RoleBindingMap = RoleBindingMap{ // for clusters t.CreateCluster: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember}, t.DeleteCluster: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember}, - t.UpdateCluster: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin}, + t.UpdateCluster: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember}, t.ListClusters: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember, t.RoleProjectAdmin, t.RoleProjectMember}, t.GetCluster: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember, t.RoleProjectAdmin, t.RoleProjectMember}, @@ -68,11 +68,11 @@ var roleBindings RoleBindingMap = RoleBindingMap{ t.GetNodepool: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember, t.RoleProjectAdmin, t.RoleProjectMember}, // for cloud provider secrets - t.CreateCloudProviderSecret: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin}, - t.UpdateCloudProviderSecret: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin}, - t.DeleteCloudProviderSecret: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin}, - t.ListCloudProviderSecrets: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin}, - t.GetCloudProviderSecret: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin}, + t.CreateCloudProviderSecret: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember}, + t.UpdateCloudProviderSecret: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember}, + t.DeleteCloudProviderSecret: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember}, + t.ListCloudProviderSecrets: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember}, + t.GetCloudProviderSecret: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember}, // for projects t.CreateProject: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember}, @@ -82,11 +82,11 @@ var roleBindings RoleBindingMap = RoleBindingMap{ t.DeleteProject: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember, t.RoleProjectAdmin}, // for project invitations - t.InviteProjectAdmin: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin}, - t.InviteProjectMember: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleProjectAdmin}, - t.ListProjectInvitations: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleProjectAdmin}, - t.GetProjectInvitation: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleProjectAdmin}, - t.DeleteProjectInvitation: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleProjectAdmin}, + t.InviteProjectAdmin: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember}, + t.InviteProjectMember: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember, t.RoleProjectAdmin}, + t.ListProjectInvitations: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember, t.RoleProjectAdmin}, + t.GetProjectInvitation: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember, t.RoleProjectAdmin}, + t.DeleteProjectInvitation: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember, t.RoleProjectAdmin}, t.MutateResourcesInProject: []t.Role{t.RoleAccountOwner, t.RoleAccountAdmin, t.RoleAccountMember, t.RoleProjectAdmin, t.RoleProjectMember},