diff --git a/internal/api/grpc/management/application.go b/internal/api/grpc/management/application.go index 71c6aa12f3..f7a6d35758 100644 --- a/internal/api/grpc/management/application.go +++ b/internal/api/grpc/management/application.go @@ -2,6 +2,7 @@ package management import ( "context" + "github.com/caos/zitadel/internal/api/authz" "github.com/golang/protobuf/ptypes/empty" @@ -25,49 +26,43 @@ func (s *Server) ApplicationByID(ctx context.Context, in *management.Application } func (s *Server) CreateOIDCApplication(ctx context.Context, in *management.OIDCApplicationCreate) (*management.Application, error) { - app, err := s.project.AddApplication(ctx, oidcAppCreateToModel(in)) + app, err := s.command.AddOIDCApplication(ctx, oidcAppCreateToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return appFromModel(app), nil + return oidcAppFromDomain(app), nil } func (s *Server) UpdateApplication(ctx context.Context, in *management.ApplicationUpdate) (*management.Application, error) { - app, err := s.project.ChangeApplication(ctx, appUpdateToModel(in)) + app, err := s.command.ChangeApplication(ctx, in.ProjectId, appUpdateToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return appFromModel(app), nil + return appFromDomain(app), nil } -func (s *Server) DeactivateApplication(ctx context.Context, in *management.ApplicationID) (*management.Application, error) { - app, err := s.project.DeactivateApplication(ctx, in.ProjectId, in.Id) - if err != nil { - return nil, err - } - return appFromModel(app), nil +func (s *Server) DeactivateApplication(ctx context.Context, in *management.ApplicationID) (*empty.Empty, error) { + err := s.command.DeactivateApplication(ctx, in.ProjectId, in.Id, authz.GetCtxData(ctx).OrgID) + return &empty.Empty{}, err } -func (s *Server) ReactivateApplication(ctx context.Context, in *management.ApplicationID) (*management.Application, error) { - app, err := s.project.ReactivateApplication(ctx, in.ProjectId, in.Id) - if err != nil { - return nil, err - } - return appFromModel(app), nil +func (s *Server) ReactivateApplication(ctx context.Context, in *management.ApplicationID) (*empty.Empty, error) { + err := s.command.ReactivateApplication(ctx, in.ProjectId, in.Id, authz.GetCtxData(ctx).OrgID) + return &empty.Empty{}, err } func (s *Server) RemoveApplication(ctx context.Context, in *management.ApplicationID) (*empty.Empty, error) { - err := s.project.RemoveApplication(ctx, in.ProjectId, in.Id) + err := s.command.RemoveApplication(ctx, in.ProjectId, in.Id, authz.GetCtxData(ctx).OrgID) return &empty.Empty{}, err } func (s *Server) UpdateApplicationOIDCConfig(ctx context.Context, in *management.OIDCConfigUpdate) (*management.OIDCConfig, error) { - config, err := s.project.ChangeOIDCConfig(ctx, oidcConfigUpdateToModel(in)) + config, err := s.command.ChangeOIDCApplication(ctx, oidcConfigUpdateToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return oidcConfigFromModel(config), nil + return oidcConfigFromDomain(config), nil } func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, in *management.ApplicationID) (*management.ClientSecret, error) { - config, err := s.project.ChangeOIDConfigSecret(ctx, in.ProjectId, in.Id) + config, err := s.command.ChangeOIDCApplicationSecret(ctx, in.ProjectId, in.Id, authz.GetCtxData(ctx).ResourceOwner) if err != nil { return nil, err } diff --git a/internal/api/grpc/management/application_converter.go b/internal/api/grpc/management/application_converter.go index dd0e12ab5d..639c270144 100644 --- a/internal/api/grpc/management/application_converter.go +++ b/internal/api/grpc/management/application_converter.go @@ -2,6 +2,8 @@ package management import ( "encoding/json" + "github.com/caos/zitadel/internal/v2/domain" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/caos/logging" "github.com/golang/protobuf/ptypes" @@ -16,44 +18,46 @@ import ( "github.com/caos/zitadel/pkg/grpc/message" ) -func appFromModel(app *proj_model.Application) *management.Application { - changeDate, err := ptypes.TimestampProto(app.ChangeDate) - logging.Log("GRPC-di7rw").OnError(err).Debug("unable to parse timestamp") +func appFromDomain(app domain.Application) *management.Application { + return &management.Application{ + Id: app.GetAppID(), + State: appStateFromDomain(app.GetState()), + Name: app.GetApplicationName(), + } +} +func oidcAppFromDomain(app *domain.OIDCApp) *management.Application { return &management.Application{ Id: app.AppID, - State: appStateFromModel(app.State), - ChangeDate: changeDate, - Name: app.Name, + State: appStateFromDomain(app.State), + ChangeDate: timestamppb.New(app.ChangeDate), + Name: app.AppName, Sequence: app.Sequence, - AppConfig: appConfigFromModel(app), + AppConfig: oidcAppConfigFromDomain(app), } } -func appConfigFromModel(app *proj_model.Application) management.AppConfig { - if app.Type == proj_model.AppTypeOIDC { - return &management.Application_OidcConfig{ - OidcConfig: oidcConfigFromModel(app.OIDCConfig), - } +func oidcAppConfigFromDomain(app *domain.OIDCApp) management.AppConfig { + return &management.Application_OidcConfig{ + OidcConfig: oidcConfigFromDomain(app), } - return nil } -func oidcConfigFromModel(config *proj_model.OIDCConfig) *management.OIDCConfig { +func oidcConfigFromDomain(config *domain.OIDCApp) *management.OIDCConfig { return &management.OIDCConfig{ RedirectUris: config.RedirectUris, - ResponseTypes: oidcResponseTypesFromModel(config.ResponseTypes), - GrantTypes: oidcGrantTypesFromModel(config.GrantTypes), - ApplicationType: oidcApplicationTypeFromModel(config.ApplicationType), + ResponseTypes: oidcResponseTypesFromDomain(config.ResponseTypes), + GrantTypes: oidcGrantTypesFromDomain(config.GrantTypes), + ApplicationType: oidcApplicationTypeFromDomain(config.ApplicationType), ClientId: config.ClientID, ClientSecret: config.ClientSecretString, - AuthMethodType: oidcAuthMethodTypeFromModel(config.AuthMethodType), + AuthMethodType: oidcAuthMethodTypeFromDomain(config.AuthMethodType), PostLogoutRedirectUris: config.PostLogoutRedirectUris, - Version: oidcVersionFromModel(config.OIDCVersion), + Version: oidcVersionFromDomain(config.OIDCVersion), NoneCompliant: config.Compliance.NoneCompliant, ComplianceProblems: complianceProblemsToLocalizedMessages(config.Compliance.Problems), DevMode: config.DevMode, - AccessTokenType: oidcTokenTypeFromModel(config.AccessTokenType), + AccessTokenType: oidcTokenTypeFromDomain(config.AccessTokenType), AccessTokenRoleAssertion: config.AccessTokenRoleAssertion, IdTokenRoleAssertion: config.IDTokenRoleAssertion, IdTokenUserinfoAssertion: config.IDTokenUserinfoAssertion, @@ -91,55 +95,49 @@ func complianceProblemsToLocalizedMessages(problems []string) []*message.Localiz } -func oidcAppCreateToModel(app *management.OIDCApplicationCreate) *proj_model.Application { - return &proj_model.Application{ +func oidcAppCreateToDomain(app *management.OIDCApplicationCreate) *domain.OIDCApp { + return &domain.OIDCApp{ ObjectRoot: models.ObjectRoot{ AggregateID: app.ProjectId, }, - Name: app.Name, - Type: proj_model.AppTypeOIDC, - OIDCConfig: &proj_model.OIDCConfig{ - OIDCVersion: oidcVersionToModel(app.Version), - RedirectUris: app.RedirectUris, - ResponseTypes: oidcResponseTypesToModel(app.ResponseTypes), - GrantTypes: oidcGrantTypesToModel(app.GrantTypes), - ApplicationType: oidcApplicationTypeToModel(app.ApplicationType), - AuthMethodType: oidcAuthMethodTypeToModel(app.AuthMethodType), - PostLogoutRedirectUris: app.PostLogoutRedirectUris, - DevMode: app.DevMode, - AccessTokenType: oidcTokenTypeToModel(app.AccessTokenType), - AccessTokenRoleAssertion: app.AccessTokenRoleAssertion, - IDTokenRoleAssertion: app.IdTokenRoleAssertion, - IDTokenUserinfoAssertion: app.IdTokenUserinfoAssertion, - ClockSkew: app.ClockSkew.AsDuration(), - }, + AppName: app.Name, + OIDCVersion: oidcVersionToDomain(app.Version), + RedirectUris: app.RedirectUris, + ResponseTypes: oidcResponseTypesToDomain(app.ResponseTypes), + GrantTypes: oidcGrantTypesToDomain(app.GrantTypes), + ApplicationType: oidcApplicationTypeToDomain(app.ApplicationType), + AuthMethodType: oidcAuthMethodTypeToDomain(app.AuthMethodType), + PostLogoutRedirectUris: app.PostLogoutRedirectUris, + DevMode: app.DevMode, + AccessTokenType: oidcTokenTypeToDomain(app.AccessTokenType), + AccessTokenRoleAssertion: app.AccessTokenRoleAssertion, + IDTokenRoleAssertion: app.IdTokenRoleAssertion, + IDTokenUserinfoAssertion: app.IdTokenUserinfoAssertion, + ClockSkew: app.ClockSkew.AsDuration(), } } -func appUpdateToModel(app *management.ApplicationUpdate) *proj_model.Application { - return &proj_model.Application{ - ObjectRoot: models.ObjectRoot{ - AggregateID: app.ProjectId, - }, - AppID: app.Id, - Name: app.Name, +func appUpdateToDomain(app *management.ApplicationUpdate) domain.Application { + return &domain.ChangeApp{ + AppID: app.Id, + AppName: app.Name, } } -func oidcConfigUpdateToModel(app *management.OIDCConfigUpdate) *proj_model.OIDCConfig { - return &proj_model.OIDCConfig{ +func oidcConfigUpdateToDomain(app *management.OIDCConfigUpdate) *domain.OIDCApp { + return &domain.OIDCApp{ ObjectRoot: models.ObjectRoot{ AggregateID: app.ProjectId, }, AppID: app.ApplicationId, RedirectUris: app.RedirectUris, - ResponseTypes: oidcResponseTypesToModel(app.ResponseTypes), - GrantTypes: oidcGrantTypesToModel(app.GrantTypes), - ApplicationType: oidcApplicationTypeToModel(app.ApplicationType), - AuthMethodType: oidcAuthMethodTypeToModel(app.AuthMethodType), + ResponseTypes: oidcResponseTypesToDomain(app.ResponseTypes), + GrantTypes: oidcGrantTypesToDomain(app.GrantTypes), + ApplicationType: oidcApplicationTypeToDomain(app.ApplicationType), + AuthMethodType: oidcAuthMethodTypeToDomain(app.AuthMethodType), PostLogoutRedirectUris: app.PostLogoutRedirectUris, DevMode: app.DevMode, - AccessTokenType: oidcTokenTypeToModel(app.AccessTokenType), + AccessTokenType: oidcTokenTypeToDomain(app.AccessTokenType), AccessTokenRoleAssertion: app.AccessTokenRoleAssertion, IDTokenRoleAssertion: app.IdTokenRoleAssertion, IDTokenUserinfoAssertion: app.IdTokenUserinfoAssertion, @@ -226,6 +224,17 @@ func applicationViewFromModel(application *proj_model.ApplicationView) *manageme return converted } +func appStateFromDomain(state domain.AppState) management.AppState { + switch state { + case domain.AppStateActive: + return management.AppState_APPSTATE_ACTIVE + case domain.AppStateInactive: + return management.AppState_APPSTATE_INACTIVE + default: + return management.AppState_APPSTATE_UNSPECIFIED + } +} + func appStateFromModel(state proj_model.AppState) management.AppState { switch state { case proj_model.AppStateActive: @@ -237,26 +246,42 @@ func appStateFromModel(state proj_model.AppState) management.AppState { } } -func oidcResponseTypesToModel(responseTypes []management.OIDCResponseType) []proj_model.OIDCResponseType { +func oidcResponseTypesToDomain(responseTypes []management.OIDCResponseType) []domain.OIDCResponseType { if responseTypes == nil || len(responseTypes) == 0 { - return []proj_model.OIDCResponseType{proj_model.OIDCResponseTypeCode} + return []domain.OIDCResponseType{domain.OIDCResponseTypeCode} } - oidcResponseTypes := make([]proj_model.OIDCResponseType, len(responseTypes)) + oidcResponseTypes := make([]domain.OIDCResponseType, len(responseTypes)) for i, responseType := range responseTypes { switch responseType { case management.OIDCResponseType_OIDCRESPONSETYPE_CODE: - oidcResponseTypes[i] = proj_model.OIDCResponseTypeCode + oidcResponseTypes[i] = domain.OIDCResponseTypeCode case management.OIDCResponseType_OIDCRESPONSETYPE_ID_TOKEN: - oidcResponseTypes[i] = proj_model.OIDCResponseTypeIDToken + oidcResponseTypes[i] = domain.OIDCResponseTypeIDToken case management.OIDCResponseType_OIDCRESPONSETYPE_ID_TOKEN_TOKEN: - oidcResponseTypes[i] = proj_model.OIDCResponseTypeIDTokenToken + oidcResponseTypes[i] = domain.OIDCResponseTypeIDTokenToken } } return oidcResponseTypes } +func oidcResponseTypesFromDomain(responseTypes []domain.OIDCResponseType) []management.OIDCResponseType { + oidcResponseTypes := make([]management.OIDCResponseType, len(responseTypes)) + + for i, responseType := range responseTypes { + switch responseType { + case domain.OIDCResponseTypeCode: + oidcResponseTypes[i] = management.OIDCResponseType_OIDCRESPONSETYPE_CODE + case domain.OIDCResponseTypeIDToken: + oidcResponseTypes[i] = management.OIDCResponseType_OIDCRESPONSETYPE_ID_TOKEN + case domain.OIDCResponseTypeIDTokenToken: + oidcResponseTypes[i] = management.OIDCResponseType_OIDCRESPONSETYPE_ID_TOKEN_TOKEN + } + } + + return oidcResponseTypes +} func oidcResponseTypesFromModel(responseTypes []proj_model.OIDCResponseType) []management.OIDCResponseType { oidcResponseTypes := make([]management.OIDCResponseType, len(responseTypes)) @@ -274,20 +299,36 @@ func oidcResponseTypesFromModel(responseTypes []proj_model.OIDCResponseType) []m return oidcResponseTypes } -func oidcGrantTypesToModel(grantTypes []management.OIDCGrantType) []proj_model.OIDCGrantType { +func oidcGrantTypesToDomain(grantTypes []management.OIDCGrantType) []domain.OIDCGrantType { if grantTypes == nil || len(grantTypes) == 0 { - return []proj_model.OIDCGrantType{proj_model.OIDCGrantTypeAuthorizationCode} + return []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode} } - oidcGrantTypes := make([]proj_model.OIDCGrantType, len(grantTypes)) + oidcGrantTypes := make([]domain.OIDCGrantType, len(grantTypes)) for i, grantType := range grantTypes { switch grantType { case management.OIDCGrantType_OIDCGRANTTYPE_AUTHORIZATION_CODE: - oidcGrantTypes[i] = proj_model.OIDCGrantTypeAuthorizationCode + oidcGrantTypes[i] = domain.OIDCGrantTypeAuthorizationCode case management.OIDCGrantType_OIDCGRANTTYPE_IMPLICIT: - oidcGrantTypes[i] = proj_model.OIDCGrantTypeImplicit + oidcGrantTypes[i] = domain.OIDCGrantTypeImplicit case management.OIDCGrantType_OIDCGRANTTYPE_REFRESH_TOKEN: - oidcGrantTypes[i] = proj_model.OIDCGrantTypeRefreshToken + oidcGrantTypes[i] = domain.OIDCGrantTypeRefreshToken + } + } + return oidcGrantTypes +} + +func oidcGrantTypesFromDomain(grantTypes []domain.OIDCGrantType) []management.OIDCGrantType { + oidcGrantTypes := make([]management.OIDCGrantType, len(grantTypes)) + + for i, grantType := range grantTypes { + switch grantType { + case domain.OIDCGrantTypeAuthorizationCode: + oidcGrantTypes[i] = management.OIDCGrantType_OIDCGRANTTYPE_AUTHORIZATION_CODE + case domain.OIDCGrantTypeImplicit: + oidcGrantTypes[i] = management.OIDCGrantType_OIDCGRANTTYPE_IMPLICIT + case domain.OIDCGrantTypeRefreshToken: + oidcGrantTypes[i] = management.OIDCGrantType_OIDCGRANTTYPE_REFRESH_TOKEN } } return oidcGrantTypes @@ -309,24 +350,37 @@ func oidcGrantTypesFromModel(grantTypes []proj_model.OIDCGrantType) []management return oidcGrantTypes } -func oidcApplicationTypeToModel(appType management.OIDCApplicationType) proj_model.OIDCApplicationType { +func oidcApplicationTypeToDomain(appType management.OIDCApplicationType) domain.OIDCApplicationType { switch appType { case management.OIDCApplicationType_OIDCAPPLICATIONTYPE_WEB: - return proj_model.OIDCApplicationTypeWeb + return domain.OIDCApplicationTypeWeb case management.OIDCApplicationType_OIDCAPPLICATIONTYPE_USER_AGENT: - return proj_model.OIDCApplicationTypeUserAgent + return domain.OIDCApplicationTypeUserAgent case management.OIDCApplicationType_OIDCAPPLICATIONTYPE_NATIVE: - return proj_model.OIDCApplicationTypeNative + return domain.OIDCApplicationTypeNative } - return proj_model.OIDCApplicationTypeWeb + return domain.OIDCApplicationTypeWeb } -func oidcVersionToModel(version management.OIDCVersion) proj_model.OIDCVersion { +func oidcVersionToDomain(version management.OIDCVersion) domain.OIDCVersion { switch version { case management.OIDCVersion_OIDCV1_0: - return proj_model.OIDCVersionV1 + return domain.OIDCVersionV1 + } + return domain.OIDCVersionV1 +} + +func oidcApplicationTypeFromDomain(appType domain.OIDCApplicationType) management.OIDCApplicationType { + switch appType { + case domain.OIDCApplicationTypeWeb: + return management.OIDCApplicationType_OIDCAPPLICATIONTYPE_WEB + case domain.OIDCApplicationTypeUserAgent: + return management.OIDCApplicationType_OIDCAPPLICATIONTYPE_USER_AGENT + case domain.OIDCApplicationTypeNative: + return management.OIDCApplicationType_OIDCAPPLICATIONTYPE_NATIVE + default: + return management.OIDCApplicationType_OIDCAPPLICATIONTYPE_WEB } - return proj_model.OIDCVersionV1 } func oidcApplicationTypeFromModel(appType proj_model.OIDCApplicationType) management.OIDCApplicationType { @@ -342,16 +396,29 @@ func oidcApplicationTypeFromModel(appType proj_model.OIDCApplicationType) manage } } -func oidcAuthMethodTypeToModel(authType management.OIDCAuthMethodType) proj_model.OIDCAuthMethodType { +func oidcAuthMethodTypeToDomain(authType management.OIDCAuthMethodType) domain.OIDCAuthMethodType { switch authType { case management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_BASIC: - return proj_model.OIDCAuthMethodTypeBasic + return domain.OIDCAuthMethodTypeBasic case management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_POST: - return proj_model.OIDCAuthMethodTypePost + return domain.OIDCAuthMethodTypePost case management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_NONE: - return proj_model.OIDCAuthMethodTypeNone + return domain.OIDCAuthMethodTypeNone default: - return proj_model.OIDCAuthMethodTypeBasic + return domain.OIDCAuthMethodTypeBasic + } +} + +func oidcAuthMethodTypeFromDomain(authType domain.OIDCAuthMethodType) management.OIDCAuthMethodType { + switch authType { + case domain.OIDCAuthMethodTypeBasic: + return management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_BASIC + case domain.OIDCAuthMethodTypePost: + return management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_POST + case domain.OIDCAuthMethodTypeNone: + return management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_NONE + default: + return management.OIDCAuthMethodType_OIDCAUTHMETHODTYPE_BASIC } } @@ -368,14 +435,25 @@ func oidcAuthMethodTypeFromModel(authType proj_model.OIDCAuthMethodType) managem } } -func oidcTokenTypeToModel(tokenType management.OIDCTokenType) proj_model.OIDCTokenType { +func oidcTokenTypeToDomain(tokenType management.OIDCTokenType) domain.OIDCTokenType { switch tokenType { case management.OIDCTokenType_OIDCTokenType_Bearer: - return proj_model.OIDCTokenTypeBearer + return domain.OIDCTokenTypeBearer case management.OIDCTokenType_OIDCTokenType_JWT: - return proj_model.OIDCTokenTypeJWT + return domain.OIDCTokenTypeJWT default: - return proj_model.OIDCTokenTypeBearer + return domain.OIDCTokenTypeBearer + } +} + +func oidcTokenTypeFromDomain(tokenType domain.OIDCTokenType) management.OIDCTokenType { + switch tokenType { + case domain.OIDCTokenTypeBearer: + return management.OIDCTokenType_OIDCTokenType_Bearer + case domain.OIDCTokenTypeJWT: + return management.OIDCTokenType_OIDCTokenType_JWT + default: + return management.OIDCTokenType_OIDCTokenType_Bearer } } @@ -390,6 +468,15 @@ func oidcTokenTypeFromModel(tokenType proj_model.OIDCTokenType) management.OIDCT } } +func oidcVersionFromDomain(version domain.OIDCVersion) management.OIDCVersion { + switch version { + case domain.OIDCVersionV1: + return management.OIDCVersion_OIDCV1_0 + default: + return management.OIDCVersion_OIDCV1_0 + } +} + func oidcVersionFromModel(version proj_model.OIDCVersion) management.OIDCVersion { switch version { case proj_model.OIDCVersionV1: diff --git a/internal/api/grpc/management/project.go b/internal/api/grpc/management/project.go index ddee42ec1d..1b114be418 100644 --- a/internal/api/grpc/management/project.go +++ b/internal/api/grpc/management/project.go @@ -12,36 +12,31 @@ import ( ) func (s *Server) CreateProject(ctx context.Context, in *management.ProjectCreateRequest) (*management.Project, error) { - project, err := s.project.CreateProject(ctx, projectCreateToModel(in)) + ctxData := authz.GetCtxData(ctx) + project, err := s.command.AddProject(ctx, projectCreateToDomain(in), ctxData.ResourceOwner, ctxData.UserID) if err != nil { return nil, err } - return projectFromModel(project), nil + return projectFromDomain(project), nil } func (s *Server) UpdateProject(ctx context.Context, in *management.ProjectUpdateRequest) (*management.Project, error) { - project, err := s.project.UpdateProject(ctx, projectUpdateToModel(in)) + project, err := s.command.ChangeProject(ctx, projectUpdateToDomain(in), authz.GetCtxData(ctx).ResourceOwner) if err != nil { return nil, err } - return projectFromModel(project), nil + return projectFromDomain(project), nil } -func (s *Server) DeactivateProject(ctx context.Context, in *management.ProjectID) (*management.Project, error) { - project, err := s.project.DeactivateProject(ctx, in.Id) - if err != nil { - return nil, err - } - return projectFromModel(project), nil +func (s *Server) DeactivateProject(ctx context.Context, in *management.ProjectID) (*empty.Empty, error) { + err := s.command.DeactivateProject(ctx, in.Id, authz.GetCtxData(ctx).ResourceOwner) + return &empty.Empty{}, err } -func (s *Server) ReactivateProject(ctx context.Context, in *management.ProjectID) (*management.Project, error) { - project, err := s.project.ReactivateProject(ctx, in.Id) - if err != nil { - return nil, err - } - return projectFromModel(project), nil +func (s *Server) ReactivateProject(ctx context.Context, in *management.ProjectID) (*empty.Empty, error) { + err := s.command.ReactivateProject(ctx, in.Id, authz.GetCtxData(ctx).ResourceOwner) + return &empty.Empty{}, err } func (s *Server) RemoveProject(ctx context.Context, in *management.ProjectID) (*empty.Empty, error) { - err := s.project.RemoveProject(ctx, in.Id) + err := s.command.RemoveProject(ctx, in.Id, authz.GetCtxData(ctx).OrgID) return &empty.Empty{}, err } @@ -82,28 +77,28 @@ func (s *Server) GetGrantedProjectByID(ctx context.Context, in *management.Proje } func (s *Server) AddProjectRole(ctx context.Context, in *management.ProjectRoleAdd) (*management.ProjectRole, error) { - role, err := s.project.AddProjectRole(ctx, projectRoleAddToModel(in)) + role, err := s.command.AddProjectRole(ctx, projectRoleAddToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return projectRoleFromModel(role), nil + return projectRoleFromDomain(role), nil } func (s *Server) BulkAddProjectRole(ctx context.Context, in *management.ProjectRoleAddBulk) (*empty.Empty, error) { - err := s.project.BulkAddProjectRole(ctx, projectRoleAddBulkToModel(in)) + err := s.command.BulkAddProjectRole(ctx, in.Id, authz.GetCtxData(ctx).OrgID, projectRoleAddBulkToDomain(in)) return &empty.Empty{}, err } func (s *Server) ChangeProjectRole(ctx context.Context, in *management.ProjectRoleChange) (*management.ProjectRole, error) { - role, err := s.project.ChangeProjectRole(ctx, projectRoleChangeToModel(in)) + role, err := s.command.ChangeProjectRole(ctx, projectRoleChangeToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return projectRoleFromModel(role), nil + return projectRoleFromDomain(role), nil } func (s *Server) RemoveProjectRole(ctx context.Context, in *management.ProjectRoleRemove) (*empty.Empty, error) { - err := s.project.RemoveProjectRole(ctx, in.Id, in.Key) + err := s.command.RemoveProjectRole(ctx, in.Id, in.Key, authz.GetCtxData(ctx).OrgID) return &empty.Empty{}, err } diff --git a/internal/api/grpc/management/project_converter.go b/internal/api/grpc/management/project_converter.go index 4bdbcdeaf6..9b3f1e6fe6 100644 --- a/internal/api/grpc/management/project_converter.go +++ b/internal/api/grpc/management/project_converter.go @@ -2,6 +2,8 @@ package management import ( "encoding/json" + "github.com/caos/zitadel/internal/v2/domain" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/caos/logging" "github.com/golang/protobuf/ptypes" @@ -14,18 +16,11 @@ import ( "github.com/caos/zitadel/pkg/grpc/message" ) -func projectFromModel(project *proj_model.Project) *management.Project { - creationDate, err := ptypes.TimestampProto(project.CreationDate) - logging.Log("GRPC-iejs3").OnError(err).Debug("unable to parse timestamp") - - changeDate, err := ptypes.TimestampProto(project.ChangeDate) - logging.Log("GRPC-di7rw").OnError(err).Debug("unable to parse timestamp") - +func projectFromDomain(project *domain.Project) *management.Project { return &management.Project{ Id: project.AggregateID, - State: projectStateFromModel(project.State), - CreationDate: creationDate, - ChangeDate: changeDate, + State: projectStateFromDomain(project.State), + ChangeDate: timestamppb.New(project.ChangeDate), Name: project.Name, Sequence: project.Sequence, ProjectRoleAssertion: project.ProjectRoleAssertion, @@ -110,6 +105,17 @@ func projectRoleViewFromModel(role *proj_model.ProjectRoleView) *management.Proj } } +func projectStateFromDomain(state domain.ProjectState) management.ProjectState { + switch state { + case domain.ProjectStateActive: + return management.ProjectState_PROJECTSTATE_ACTIVE + case domain.ProjectStateInactive: + return management.ProjectState_PROJECTSTATE_INACTIVE + default: + return management.ProjectState_PROJECTSTATE_UNSPECIFIED + } +} + func projectStateFromModel(state proj_model.ProjectState) management.ProjectState { switch state { case proj_model.ProjectStateActive: @@ -121,16 +127,16 @@ func projectStateFromModel(state proj_model.ProjectState) management.ProjectStat } } -func projectCreateToModel(project *management.ProjectCreateRequest) *proj_model.Project { - return &proj_model.Project{ +func projectCreateToDomain(project *management.ProjectCreateRequest) *domain.Project { + return &domain.Project{ Name: project.Name, ProjectRoleAssertion: project.ProjectRoleAssertion, ProjectRoleCheck: project.ProjectRoleCheck, } } -func projectUpdateToModel(project *management.ProjectUpdateRequest) *proj_model.Project { - return &proj_model.Project{ +func projectUpdateToDomain(project *management.ProjectUpdateRequest) *domain.Project { + return &domain.Project{ ObjectRoot: models.ObjectRoot{ AggregateID: project.Id, }, @@ -140,27 +146,20 @@ func projectUpdateToModel(project *management.ProjectUpdateRequest) *proj_model. } } -func projectRoleFromModel(role *proj_model.ProjectRole) *management.ProjectRole { - creationDate, err := ptypes.TimestampProto(role.CreationDate) - logging.Log("GRPC-due83").OnError(err).Debug("unable to parse timestamp") - - changeDate, err := ptypes.TimestampProto(role.ChangeDate) - logging.Log("GRPC-id93s").OnError(err).Debug("unable to parse timestamp") - +func projectRoleFromDomain(role *domain.ProjectRole) *management.ProjectRole { return &management.ProjectRole{ - CreationDate: creationDate, - ChangeDate: changeDate, - Sequence: role.Sequence, - Key: role.Key, - DisplayName: role.DisplayName, - Group: role.Group, + ChangeDate: timestamppb.New(role.ChangeDate), + Sequence: role.Sequence, + Key: role.Key, + DisplayName: role.DisplayName, + Group: role.Group, } } -func projectRoleAddBulkToModel(bulk *management.ProjectRoleAddBulk) []*proj_model.ProjectRole { - roles := make([]*proj_model.ProjectRole, len(bulk.ProjectRoles)) +func projectRoleAddBulkToDomain(bulk *management.ProjectRoleAddBulk) []*domain.ProjectRole { + roles := make([]*domain.ProjectRole, len(bulk.ProjectRoles)) for i, role := range bulk.ProjectRoles { - roles[i] = &proj_model.ProjectRole{ + roles[i] = &domain.ProjectRole{ ObjectRoot: models.ObjectRoot{ AggregateID: bulk.Id, }, @@ -172,8 +171,8 @@ func projectRoleAddBulkToModel(bulk *management.ProjectRoleAddBulk) []*proj_mode return roles } -func projectRoleAddToModel(role *management.ProjectRoleAdd) *proj_model.ProjectRole { - return &proj_model.ProjectRole{ +func projectRoleAddToDomain(role *management.ProjectRoleAdd) *domain.ProjectRole { + return &domain.ProjectRole{ ObjectRoot: models.ObjectRoot{ AggregateID: role.Id, }, @@ -183,8 +182,8 @@ func projectRoleAddToModel(role *management.ProjectRoleAdd) *proj_model.ProjectR } } -func projectRoleChangeToModel(role *management.ProjectRoleChange) *proj_model.ProjectRole { - return &proj_model.ProjectRole{ +func projectRoleChangeToDomain(role *management.ProjectRoleChange) *domain.ProjectRole { + return &domain.ProjectRole{ ObjectRoot: models.ObjectRoot{ AggregateID: role.Id, }, diff --git a/internal/api/grpc/management/project_grant.go b/internal/api/grpc/management/project_grant.go index b58e6e6208..5c89a3b84c 100644 --- a/internal/api/grpc/management/project_grant.go +++ b/internal/api/grpc/management/project_grant.go @@ -29,36 +29,30 @@ func (s *Server) ProjectGrantByID(ctx context.Context, in *management.ProjectGra } func (s *Server) CreateProjectGrant(ctx context.Context, in *management.ProjectGrantCreate) (*management.ProjectGrant, error) { - grant, err := s.project.AddProjectGrant(ctx, projectGrantCreateToModel(in)) + grant, err := s.command.AddProjectGrant(ctx, projectGrantCreateToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return projectGrantFromModel(grant), nil + return projectGrantFromDomain(grant), nil } func (s *Server) UpdateProjectGrant(ctx context.Context, in *management.ProjectGrantUpdate) (*management.ProjectGrant, error) { - grant, err := s.project.ChangeProjectGrant(ctx, projectGrantUpdateToModel(in)) + grant, err := s.command.ChangeProjectGrant(ctx, projectGrantUpdateToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return projectGrantFromModel(grant), nil + return projectGrantFromDomain(grant), nil } -func (s *Server) DeactivateProjectGrant(ctx context.Context, in *management.ProjectGrantID) (*management.ProjectGrant, error) { - grant, err := s.project.DeactivateProjectGrant(ctx, in.ProjectId, in.Id) - if err != nil { - return nil, err - } - return projectGrantFromModel(grant), nil +func (s *Server) DeactivateProjectGrant(ctx context.Context, in *management.ProjectGrantID) (*empty.Empty, error) { + err := s.command.DeactivateProjectGrant(ctx, in.ProjectId, in.Id, authz.GetCtxData(ctx).OrgID) + return &empty.Empty{}, err } -func (s *Server) ReactivateProjectGrant(ctx context.Context, in *management.ProjectGrantID) (*management.ProjectGrant, error) { - grant, err := s.project.ReactivateProjectGrant(ctx, in.ProjectId, in.Id) - if err != nil { - return nil, err - } - return projectGrantFromModel(grant), nil +func (s *Server) ReactivateProjectGrant(ctx context.Context, in *management.ProjectGrantID) (*empty.Empty, error) { + err := s.command.ReactivateProjectGrant(ctx, in.ProjectId, in.Id, authz.GetCtxData(ctx).OrgID) + return &empty.Empty{}, err } func (s *Server) RemoveProjectGrant(ctx context.Context, in *management.ProjectGrantID) (*empty.Empty, error) { - err := s.project.RemoveProjectGrant(ctx, in.ProjectId, in.Id) + err := s.command.RemoveProjectGrant(ctx, in.ProjectId, in.Id, authz.GetCtxData(ctx).OrgID) return &empty.Empty{}, err } diff --git a/internal/api/grpc/management/project_grant_converter.go b/internal/api/grpc/management/project_grant_converter.go index 3a6bab441e..a1c85ceed3 100644 --- a/internal/api/grpc/management/project_grant_converter.go +++ b/internal/api/grpc/management/project_grant_converter.go @@ -2,6 +2,8 @@ package management import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/v2/domain" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/golang/protobuf/ptypes" @@ -11,6 +13,19 @@ import ( "github.com/caos/zitadel/pkg/grpc/management" ) +func projectGrantFromDomain(grant *domain.ProjectGrant) *management.ProjectGrant { + return &management.ProjectGrant{ + Id: grant.GrantID, + State: projectGrantStateFromDomain(grant.State), + CreationDate: timestamppb.New(grant.CreationDate), + ChangeDate: timestamppb.New(grant.ChangeDate), + GrantedOrgId: grant.GrantedOrgID, + RoleKeys: grant.RoleKeys, + Sequence: grant.Sequence, + ProjectId: grant.AggregateID, + } +} + func projectGrantFromModel(grant *proj_model.ProjectGrant) *management.ProjectGrant { creationDate, err := ptypes.TimestampProto(grant.CreationDate) logging.Log("GRPC-8d73s").OnError(err).Debug("unable to parse timestamp") @@ -30,8 +45,8 @@ func projectGrantFromModel(grant *proj_model.ProjectGrant) *management.ProjectGr } } -func projectGrantCreateToModel(grant *management.ProjectGrantCreate) *proj_model.ProjectGrant { - return &proj_model.ProjectGrant{ +func projectGrantCreateToDomain(grant *management.ProjectGrantCreate) *domain.ProjectGrant { + return &domain.ProjectGrant{ ObjectRoot: models.ObjectRoot{ AggregateID: grant.ProjectId, }, @@ -40,8 +55,8 @@ func projectGrantCreateToModel(grant *management.ProjectGrantCreate) *proj_model } } -func projectGrantUpdateToModel(grant *management.ProjectGrantUpdate) *proj_model.ProjectGrant { - return &proj_model.ProjectGrant{ +func projectGrantUpdateToDomain(grant *management.ProjectGrantUpdate) *domain.ProjectGrant { + return &domain.ProjectGrant{ ObjectRoot: models.ObjectRoot{ AggregateID: grant.ProjectId, }, @@ -134,6 +149,16 @@ func projectGrantFromGrantedProjectModel(project *proj_model.ProjectGrantView) * } } +func projectGrantStateFromDomain(state domain.ProjectGrantState) management.ProjectGrantState { + switch state { + case domain.ProjectGrantStateActive: + return management.ProjectGrantState_PROJECTGRANTSTATE_ACTIVE + case domain.ProjectGrantStateInactive: + return management.ProjectGrantState_PROJECTGRANTSTATE_INACTIVE + default: + return management.ProjectGrantState_PROJECTGRANTSTATE_UNSPECIFIED + } +} func projectGrantStateFromModel(state proj_model.ProjectGrantState) management.ProjectGrantState { switch state { case proj_model.ProjectGrantStateActive: diff --git a/internal/api/grpc/management/project_grant_member.go b/internal/api/grpc/management/project_grant_member.go index 7743d2940f..f676133d6d 100644 --- a/internal/api/grpc/management/project_grant_member.go +++ b/internal/api/grpc/management/project_grant_member.go @@ -2,6 +2,7 @@ package management import ( "context" + "github.com/caos/zitadel/internal/api/authz" "github.com/golang/protobuf/ptypes/empty" @@ -21,22 +22,22 @@ func (s *Server) SearchProjectGrantMembers(ctx context.Context, in *management.P } func (s *Server) AddProjectGrantMember(ctx context.Context, in *management.ProjectGrantMemberAdd) (*management.ProjectGrantMember, error) { - member, err := s.project.AddProjectGrantMember(ctx, projectGrantMemberAddToModel(in)) + member, err := s.command.AddProjectGrantMember(ctx, projectGrantMemberAddToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return projectGrantMemberFromModel(member), nil + return projectGrantMemberFromDomain(member), nil } func (s *Server) ChangeProjectGrantMember(ctx context.Context, in *management.ProjectGrantMemberChange) (*management.ProjectGrantMember, error) { - member, err := s.project.ChangeProjectGrantMember(ctx, projectGrantMemberChangeToModel(in)) + member, err := s.command.ChangeProjectGrantMember(ctx, projectGrantMemberChangeToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return projectGrantMemberFromModel(member), nil + return projectGrantMemberFromDomain(member), nil } func (s *Server) RemoveProjectGrantMember(ctx context.Context, in *management.ProjectGrantMemberRemove) (*empty.Empty, error) { - err := s.project.RemoveProjectGrantMember(ctx, in.ProjectId, in.GrantId, in.UserId) + err := s.command.RemoveProjectGrantMember(ctx, in.ProjectId, in.UserId, in.GrantId, authz.GetCtxData(ctx).OrgID) return &empty.Empty{}, err } diff --git a/internal/api/grpc/management/project_grant_member_converter.go b/internal/api/grpc/management/project_grant_member_converter.go index 87fa6a2fef..ce675d86d6 100644 --- a/internal/api/grpc/management/project_grant_member_converter.go +++ b/internal/api/grpc/management/project_grant_member_converter.go @@ -3,13 +3,25 @@ package management import ( "github.com/caos/logging" "github.com/caos/zitadel/internal/model" + "github.com/caos/zitadel/internal/v2/domain" "github.com/golang/protobuf/ptypes" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/pkg/grpc/management" ) +func projectGrantMemberFromDomain(member *domain.ProjectGrantMember) *management.ProjectGrantMember { + return &management.ProjectGrantMember{ + CreationDate: timestamppb.New(member.CreationDate), + ChangeDate: timestamppb.New(member.ChangeDate), + Sequence: member.Sequence, + UserId: member.UserID, + Roles: member.Roles, + } +} + func projectGrantMemberFromModel(member *proj_model.ProjectGrantMember) *management.ProjectGrantMember { creationDate, err := ptypes.TimestampProto(member.CreationDate) logging.Log("GRPC-7du3s").OnError(err).Debug("unable to parse timestamp") @@ -26,8 +38,8 @@ func projectGrantMemberFromModel(member *proj_model.ProjectGrantMember) *managem } } -func projectGrantMemberAddToModel(member *management.ProjectGrantMemberAdd) *proj_model.ProjectGrantMember { - return &proj_model.ProjectGrantMember{ +func projectGrantMemberAddToDomain(member *management.ProjectGrantMemberAdd) *domain.ProjectGrantMember { + return &domain.ProjectGrantMember{ ObjectRoot: models.ObjectRoot{ AggregateID: member.ProjectId, }, @@ -37,8 +49,8 @@ func projectGrantMemberAddToModel(member *management.ProjectGrantMemberAdd) *pro } } -func projectGrantMemberChangeToModel(member *management.ProjectGrantMemberChange) *proj_model.ProjectGrantMember { - return &proj_model.ProjectGrantMember{ +func projectGrantMemberChangeToDomain(member *management.ProjectGrantMemberChange) *domain.ProjectGrantMember { + return &domain.ProjectGrantMember{ ObjectRoot: models.ObjectRoot{ AggregateID: member.ProjectId, }, diff --git a/internal/api/grpc/management/project_member.go b/internal/api/grpc/management/project_member.go index cbb16bc816..c3a83b53b3 100644 --- a/internal/api/grpc/management/project_member.go +++ b/internal/api/grpc/management/project_member.go @@ -2,6 +2,7 @@ package management import ( "context" + "github.com/caos/zitadel/internal/api/authz" "github.com/golang/protobuf/ptypes/empty" @@ -27,22 +28,22 @@ func (s *Server) SearchProjectMembers(ctx context.Context, in *management.Projec } func (s *Server) AddProjectMember(ctx context.Context, in *management.ProjectMemberAdd) (*management.ProjectMember, error) { - member, err := s.project.AddProjectMember(ctx, projectMemberAddToModel(in)) + member, err := s.command.AddProjectMember(ctx, projectMemberAddToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return projectMemberFromModel(member), nil + return projectMemberFromDomain(member), nil } func (s *Server) ChangeProjectMember(ctx context.Context, in *management.ProjectMemberChange) (*management.ProjectMember, error) { - member, err := s.project.ChangeProjectMember(ctx, projectMemberChangeToModel(in)) + member, err := s.command.ChangeProjectMember(ctx, projectMemberChangeToDomain(in), authz.GetCtxData(ctx).OrgID) if err != nil { return nil, err } - return projectMemberFromModel(member), nil + return projectMemberFromDomain(member), nil } func (s *Server) RemoveProjectMember(ctx context.Context, in *management.ProjectMemberRemove) (*empty.Empty, error) { - err := s.project.RemoveProjectMember(ctx, in.Id, in.UserId) + err := s.command.RemoveProjectMember(ctx, in.Id, in.UserId, authz.GetCtxData(ctx).OrgID) return &empty.Empty{}, err } diff --git a/internal/api/grpc/management/project_member_converter.go b/internal/api/grpc/management/project_member_converter.go index 325a069deb..c8a708c8ee 100644 --- a/internal/api/grpc/management/project_member_converter.go +++ b/internal/api/grpc/management/project_member_converter.go @@ -2,31 +2,27 @@ package management import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/v2/domain" "github.com/golang/protobuf/ptypes" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/caos/zitadel/internal/eventstore/models" proj_model "github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/pkg/grpc/management" ) -func projectMemberFromModel(member *proj_model.ProjectMember) *management.ProjectMember { - creationDate, err := ptypes.TimestampProto(member.CreationDate) - logging.Log("GRPC-kd8re").OnError(err).Debug("unable to parse timestamp") - - changeDate, err := ptypes.TimestampProto(member.ChangeDate) - logging.Log("GRPC-dlei3").OnError(err).Debug("unable to parse timestamp") - +func projectMemberFromDomain(member *domain.Member) *management.ProjectMember { return &management.ProjectMember{ - CreationDate: creationDate, - ChangeDate: changeDate, + CreationDate: timestamppb.New(member.CreationDate), + ChangeDate: timestamppb.New(member.ChangeDate), Sequence: member.Sequence, UserId: member.UserID, Roles: member.Roles, } } -func projectMemberAddToModel(member *management.ProjectMemberAdd) *proj_model.ProjectMember { - return &proj_model.ProjectMember{ +func projectMemberAddToDomain(member *management.ProjectMemberAdd) *domain.Member { + return &domain.Member{ ObjectRoot: models.ObjectRoot{ AggregateID: member.Id, }, @@ -35,8 +31,8 @@ func projectMemberAddToModel(member *management.ProjectMemberAdd) *proj_model.Pr } } -func projectMemberChangeToModel(member *management.ProjectMemberChange) *proj_model.ProjectMember { - return &proj_model.ProjectMember{ +func projectMemberChangeToDomain(member *management.ProjectMemberChange) *domain.Member { + return &domain.Member{ ObjectRoot: models.ObjectRoot{ AggregateID: member.Id, }, diff --git a/internal/eventstore/v2/search_query.go b/internal/eventstore/v2/search_query.go index 6053a2d3e7..64961e8e7f 100644 --- a/internal/eventstore/v2/search_query.go +++ b/internal/eventstore/v2/search_query.go @@ -115,7 +115,7 @@ func (factory *SearchQueryBuilder) build() (*repository.SearchQuery, error) { } return &repository.SearchQuery{ - Columns: repository.Columns(factory.columns), + Columns: factory.columns, Limit: factory.limit, Desc: factory.desc, Filters: filters, diff --git a/internal/management/repository/eventsourcing/eventstore/project.go b/internal/management/repository/eventsourcing/eventstore/project.go index 2d949a3f95..25c980366d 100644 --- a/internal/management/repository/eventsourcing/eventstore/project.go +++ b/internal/management/repository/eventsourcing/eventstore/project.go @@ -9,18 +9,13 @@ import ( "github.com/caos/zitadel/internal/api/authz" caos_errs "github.com/caos/zitadel/internal/errors" es_int "github.com/caos/zitadel/internal/eventstore" - "github.com/caos/zitadel/internal/eventstore/models" - es_models "github.com/caos/zitadel/internal/eventstore/models" - es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" iam_event "github.com/caos/zitadel/internal/iam/repository/eventsourcing" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view" global_model "github.com/caos/zitadel/internal/model" proj_model "github.com/caos/zitadel/internal/project/model" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" - es_proj_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" "github.com/caos/zitadel/internal/project/repository/view/model" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" - usr_grant_model "github.com/caos/zitadel/internal/usergrant/model" usr_grant_event "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing" ) @@ -68,54 +63,6 @@ func (repo *ProjectRepo) ProjectByID(ctx context.Context, id string) (*proj_mode return model.ProjectToModel(project), nil } -func (repo *ProjectRepo) CreateProject(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) { - ctxData := authz.GetCtxData(ctx) - iam, err := repo.IAMEvents.IAMByID(ctx, repo.IAMID) - if err != nil { - return nil, err - } - return repo.ProjectEvents.CreateProject(ctx, project, iam.GlobalOrgID == ctxData.OrgID) -} - -func (repo *ProjectRepo) UpdateProject(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) { - return repo.ProjectEvents.UpdateProject(ctx, project) -} - -func (repo *ProjectRepo) DeactivateProject(ctx context.Context, id string) (*proj_model.Project, error) { - return repo.ProjectEvents.DeactivateProject(ctx, id) -} - -func (repo *ProjectRepo) ReactivateProject(ctx context.Context, id string) (*proj_model.Project, error) { - return repo.ProjectEvents.ReactivateProject(ctx, id) -} - -func (repo *ProjectRepo) RemoveProject(ctx context.Context, projectID string) error { - proj := proj_model.NewProject(projectID) - aggregates := make([]*es_models.Aggregate, 0) - project, agg, err := repo.ProjectEvents.PrepareRemoveProject(ctx, proj) - if err != nil { - return err - } - aggregates = append(aggregates, agg) - - // remove user_grants - usergrants, err := repo.View.UserGrantsByProjectID(projectID) - if err != nil { - return err - } - for _, grant := range usergrants { - _, aggs, err := repo.UserGrantEvents.PrepareRemoveUserGrant(ctx, grant.ID, true) - if err != nil { - return err - } - for _, agg := range aggs { - aggregates = append(aggregates, agg) - } - } - - return es_sdk.PushAggregates(ctx, repo.Eventstore.PushAggregates, project.AppendEvents, aggregates...) -} - func (repo *ProjectRepo) SearchProjects(ctx context.Context, request *proj_model.ProjectViewSearchRequest) (*proj_model.ProjectViewSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) sequence, sequenceErr := repo.View.GetLatestProjectSequence("") @@ -183,19 +130,6 @@ func (repo *ProjectRepo) ProjectMemberByID(ctx context.Context, projectID, userI return model.ProjectMemberToModel(member), nil } -func (repo *ProjectRepo) AddProjectMember(ctx context.Context, member *proj_model.ProjectMember) (*proj_model.ProjectMember, error) { - return repo.ProjectEvents.AddProjectMember(ctx, member) -} - -func (repo *ProjectRepo) ChangeProjectMember(ctx context.Context, member *proj_model.ProjectMember) (*proj_model.ProjectMember, error) { - return repo.ProjectEvents.ChangeProjectMember(ctx, member) -} - -func (repo *ProjectRepo) RemoveProjectMember(ctx context.Context, projectID, userID string) error { - member := proj_model.NewProjectMember(projectID, userID) - return repo.ProjectEvents.RemoveProjectMember(ctx, member) -} - func (repo *ProjectRepo) SearchProjectMembers(ctx context.Context, request *proj_model.ProjectMemberSearchRequest) (*proj_model.ProjectMemberSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) sequence, sequenceErr := repo.View.GetLatestProjectMemberSequence("") @@ -217,56 +151,6 @@ func (repo *ProjectRepo) SearchProjectMembers(ctx context.Context, request *proj return result, nil } -func (repo *ProjectRepo) AddProjectRole(ctx context.Context, role *proj_model.ProjectRole) (*proj_model.ProjectRole, error) { - return repo.ProjectEvents.AddProjectRoles(ctx, role) -} - -func (repo *ProjectRepo) BulkAddProjectRole(ctx context.Context, roles []*proj_model.ProjectRole) error { - _, err := repo.ProjectEvents.AddProjectRoles(ctx, roles...) - return err -} - -func (repo *ProjectRepo) ChangeProjectRole(ctx context.Context, member *proj_model.ProjectRole) (*proj_model.ProjectRole, error) { - return repo.ProjectEvents.ChangeProjectRole(ctx, member) -} - -func (repo *ProjectRepo) RemoveProjectRole(ctx context.Context, projectID, key string) error { - role := proj_model.NewProjectRole(projectID, key) - aggregates := make([]*es_models.Aggregate, 0) - project, agg, err := repo.ProjectEvents.PrepareRemoveProjectRole(ctx, role) - if err != nil { - return err - } - aggregates = append(aggregates, agg) - - usergrants, err := repo.View.UserGrantsByProjectIDAndRoleKey(projectID, key) - if err != nil { - return err - } - for _, grant := range usergrants { - changed := &usr_grant_model.UserGrant{ - ObjectRoot: models.ObjectRoot{AggregateID: grant.ID, Sequence: grant.Sequence, ResourceOwner: grant.ResourceOwner}, - RoleKeys: grant.RoleKeys, - ProjectID: grant.ProjectID, - UserID: grant.UserID, - } - changed.RemoveRoleKeyIfExisting(key) - _, agg, err := repo.UserGrantEvents.PrepareChangeUserGrant(ctx, changed, true) - if err != nil { - return err - } - aggregates = append(aggregates, agg) - } - if err != nil { - return err - } - err = es_sdk.PushAggregates(ctx, repo.Eventstore.PushAggregates, project.AppendEvents, aggregates...) - if err != nil { - return err - } - return nil -} - func (repo *ProjectRepo) SearchProjectRoles(ctx context.Context, projectID string, request *proj_model.ProjectRoleSearchRequest) (*proj_model.ProjectRoleSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) request.AppendProjectQuery(projectID) @@ -343,27 +227,6 @@ func (repo *ProjectRepo) ApplicationByID(ctx context.Context, projectID, appID s return model.ApplicationViewToModel(app), nil } -func (repo *ProjectRepo) AddApplication(ctx context.Context, app *proj_model.Application) (*proj_model.Application, error) { - return repo.ProjectEvents.AddApplication(ctx, app) -} - -func (repo *ProjectRepo) ChangeApplication(ctx context.Context, app *proj_model.Application) (*proj_model.Application, error) { - return repo.ProjectEvents.ChangeApplication(ctx, app) -} - -func (repo *ProjectRepo) DeactivateApplication(ctx context.Context, projectID, appID string) (*proj_model.Application, error) { - return repo.ProjectEvents.DeactivateApplication(ctx, projectID, appID) -} - -func (repo *ProjectRepo) ReactivateApplication(ctx context.Context, projectID, appID string) (*proj_model.Application, error) { - return repo.ProjectEvents.ReactivateApplication(ctx, projectID, appID) -} - -func (repo *ProjectRepo) RemoveApplication(ctx context.Context, projectID, appID string) error { - app := proj_model.NewApplication(projectID, appID) - return repo.ProjectEvents.RemoveApplication(ctx, app) -} - func (repo *ProjectRepo) SearchApplications(ctx context.Context, request *proj_model.ApplicationSearchRequest) (*proj_model.ApplicationSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) sequence, sequenceErr := repo.View.GetLatestApplicationSequence("") @@ -405,10 +268,6 @@ func (repo *ProjectRepo) ApplicationChanges(ctx context.Context, id string, appI return changes, nil } -func (repo *ProjectRepo) ChangeOIDCConfig(ctx context.Context, config *proj_model.OIDCConfig) (*proj_model.OIDCConfig, error) { - return repo.ProjectEvents.ChangeOIDCConfig(ctx, config) -} - func (repo *ProjectRepo) ChangeOIDConfigSecret(ctx context.Context, projectID, appID string) (*proj_model.OIDCConfig, error) { return repo.ProjectEvents.ChangeOIDCConfigSecret(ctx, projectID, appID) } @@ -493,102 +352,6 @@ func (repo *ProjectRepo) SearchGrantedProjects(ctx context.Context, request *pro return result, nil } -func (repo *ProjectRepo) AddProjectGrant(ctx context.Context, grant *proj_model.ProjectGrant) (*proj_model.ProjectGrant, error) { - return repo.ProjectEvents.AddProjectGrant(ctx, grant) -} - -func (repo *ProjectRepo) ChangeProjectGrant(ctx context.Context, grant *proj_model.ProjectGrant) (*proj_model.ProjectGrant, error) { - project, aggFunc, removedRoles, err := repo.ProjectEvents.PrepareChangeProjectGrant(ctx, grant) - if err != nil { - return nil, err - } - agg, err := aggFunc(ctx) - if err != nil { - return nil, err - } - aggregates := make([]*es_models.Aggregate, 0) - aggregates = append(aggregates, agg) - - usergrants, err := repo.View.UserGrantsByProjectID(grant.AggregateID) - if err != nil { - return nil, err - } - for _, grant := range usergrants { - changed := &usr_grant_model.UserGrant{ - ObjectRoot: models.ObjectRoot{AggregateID: grant.ID, Sequence: grant.Sequence, ResourceOwner: grant.ResourceOwner}, - RoleKeys: grant.RoleKeys, - ProjectID: grant.ProjectID, - UserID: grant.UserID, - } - roleDeleted := changed.RemoveRoleKeysIfExisting(removedRoles) - if roleDeleted { - _, agg, err := repo.UserGrantEvents.PrepareChangeUserGrant(ctx, changed, true) - if err != nil { - return nil, err - } - aggregates = append(aggregates, agg) - } - } - if err != nil { - return nil, err - } - err = es_sdk.PushAggregates(ctx, repo.Eventstore.PushAggregates, project.AppendEvents, aggregates...) - if err != nil { - return nil, err - } - if _, g := es_proj_model.GetProjectGrant(project.Grants, grant.GrantID); g != nil { - return es_proj_model.GrantToModel(g), nil - } - return nil, caos_errs.ThrowInternal(nil, "EVENT-dksi8", "Could not find app in list") -} - -func (repo *ProjectRepo) DeactivateProjectGrant(ctx context.Context, projectID, grantID string) (*proj_model.ProjectGrant, error) { - return repo.ProjectEvents.DeactivateProjectGrant(ctx, projectID, grantID) -} - -func (repo *ProjectRepo) ReactivateProjectGrant(ctx context.Context, projectID, grantID string) (*proj_model.ProjectGrant, error) { - return repo.ProjectEvents.ReactivateProjectGrant(ctx, projectID, grantID) -} - -func (repo *ProjectRepo) RemoveProjectGrant(ctx context.Context, projectID, grantID string) error { - grant, err := repo.ProjectEvents.ProjectGrantByIDs(ctx, projectID, grantID) - if err != nil { - return err - } - aggregates := make([]*es_models.Aggregate, 0) - project, aggFunc, err := repo.ProjectEvents.PrepareRemoveProjectGrant(ctx, grant) - if err != nil { - return err - } - agg, err := aggFunc(ctx) - if err != nil { - return err - } - aggregates = append(aggregates, agg) - - usergrants, err := repo.View.UserGrantsByOrgIDAndProjectID(grant.GrantedOrgID, projectID) - if err != nil { - return err - } - for _, grant := range usergrants { - _, grantAggregates, err := repo.UserGrantEvents.PrepareRemoveUserGrant(ctx, grant.ID, true) - if err != nil { - return err - } - for _, agg := range grantAggregates { - aggregates = append(aggregates, agg) - } - } - if err != nil { - return err - } - err = es_sdk.PushAggregates(ctx, repo.Eventstore.PushAggregates, project.AppendEvents, aggregates...) - if err != nil { - return err - } - return nil -} - func (repo *ProjectRepo) ProjectGrantMemberByID(ctx context.Context, projectID, userID string) (*proj_model.ProjectGrantMemberView, error) { member, err := repo.View.ProjectGrantMemberByIDs(projectID, userID) if err != nil { @@ -597,19 +360,6 @@ func (repo *ProjectRepo) ProjectGrantMemberByID(ctx context.Context, projectID, return model.ProjectGrantMemberToModel(member), nil } -func (repo *ProjectRepo) AddProjectGrantMember(ctx context.Context, member *proj_model.ProjectGrantMember) (*proj_model.ProjectGrantMember, error) { - return repo.ProjectEvents.AddProjectGrantMember(ctx, member) -} - -func (repo *ProjectRepo) ChangeProjectGrantMember(ctx context.Context, member *proj_model.ProjectGrantMember) (*proj_model.ProjectGrantMember, error) { - return repo.ProjectEvents.ChangeProjectGrantMember(ctx, member) -} - -func (repo *ProjectRepo) RemoveProjectGrantMember(ctx context.Context, projectID, grantID, userID string) error { - member := proj_model.NewProjectGrantMember(projectID, grantID, userID) - return repo.ProjectEvents.RemoveProjectGrantMember(ctx, member) -} - func (repo *ProjectRepo) SearchProjectGrantMembers(ctx context.Context, request *proj_model.ProjectGrantMemberSearchRequest) (*proj_model.ProjectGrantMemberSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) sequence, sequenceErr := repo.View.GetLatestProjectGrantMemberSequence("") diff --git a/internal/management/repository/project.go b/internal/management/repository/project.go index 06c1e2225a..153e133701 100644 --- a/internal/management/repository/project.go +++ b/internal/management/repository/project.go @@ -8,52 +8,26 @@ import ( type ProjectRepository interface { ProjectByID(ctx context.Context, id string) (*model.ProjectView, error) - CreateProject(ctx context.Context, project *model.Project) (*model.Project, error) - UpdateProject(ctx context.Context, project *model.Project) (*model.Project, error) - DeactivateProject(ctx context.Context, id string) (*model.Project, error) - ReactivateProject(ctx context.Context, id string) (*model.Project, error) - RemoveProject(ctx context.Context, id string) error SearchProjects(ctx context.Context, request *model.ProjectViewSearchRequest) (*model.ProjectViewSearchResponse, error) SearchProjectGrants(ctx context.Context, request *model.ProjectGrantViewSearchRequest) (*model.ProjectGrantViewSearchResponse, error) SearchGrantedProjects(ctx context.Context, request *model.ProjectGrantViewSearchRequest) (*model.ProjectGrantViewSearchResponse, error) ProjectGrantViewByID(ctx context.Context, grantID string) (*model.ProjectGrantView, error) ProjectMemberByID(ctx context.Context, projectID, userID string) (*model.ProjectMemberView, error) - AddProjectMember(ctx context.Context, member *model.ProjectMember) (*model.ProjectMember, error) - ChangeProjectMember(ctx context.Context, member *model.ProjectMember) (*model.ProjectMember, error) - RemoveProjectMember(ctx context.Context, projectID, userID string) error SearchProjectMembers(ctx context.Context, request *model.ProjectMemberSearchRequest) (*model.ProjectMemberSearchResponse, error) GetProjectMemberRoles(ctx context.Context) ([]string, error) - AddProjectRole(ctx context.Context, role *model.ProjectRole) (*model.ProjectRole, error) - ChangeProjectRole(ctx context.Context, role *model.ProjectRole) (*model.ProjectRole, error) - RemoveProjectRole(ctx context.Context, projectID, key string) error SearchProjectRoles(ctx context.Context, projectId string, request *model.ProjectRoleSearchRequest) (*model.ProjectRoleSearchResponse, error) ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*model.ProjectChanges, error) - BulkAddProjectRole(ctx context.Context, role []*model.ProjectRole) error ApplicationByID(ctx context.Context, projectID, appID string) (*model.ApplicationView, error) - AddApplication(ctx context.Context, app *model.Application) (*model.Application, error) - ChangeApplication(ctx context.Context, app *model.Application) (*model.Application, error) - DeactivateApplication(ctx context.Context, projectID, appID string) (*model.Application, error) - ReactivateApplication(ctx context.Context, projectID, appID string) (*model.Application, error) - RemoveApplication(ctx context.Context, projectID, appID string) error - ChangeOIDCConfig(ctx context.Context, config *model.OIDCConfig) (*model.OIDCConfig, error) ChangeOIDConfigSecret(ctx context.Context, projectID, appID string) (*model.OIDCConfig, error) SearchApplications(ctx context.Context, request *model.ApplicationSearchRequest) (*model.ApplicationSearchResponse, error) ApplicationChanges(ctx context.Context, id string, secId string, lastSequence uint64, limit uint64, sortAscending bool) (*model.ApplicationChanges, error) ProjectGrantByID(ctx context.Context, grantID string) (*model.ProjectGrantView, error) - AddProjectGrant(ctx context.Context, grant *model.ProjectGrant) (*model.ProjectGrant, error) - ChangeProjectGrant(ctx context.Context, grant *model.ProjectGrant) (*model.ProjectGrant, error) - DeactivateProjectGrant(ctx context.Context, projectID, grantID string) (*model.ProjectGrant, error) - ReactivateProjectGrant(ctx context.Context, projectID, grantID string) (*model.ProjectGrant, error) - RemoveProjectGrant(ctx context.Context, projectID, grantID string) error SearchProjectGrantMembers(ctx context.Context, request *model.ProjectGrantMemberSearchRequest) (*model.ProjectGrantMemberSearchResponse, error) ProjectGrantMemberByID(ctx context.Context, projectID, userID string) (*model.ProjectGrantMemberView, error) - AddProjectGrantMember(ctx context.Context, member *model.ProjectGrantMember) (*model.ProjectGrantMember, error) - ChangeProjectGrantMember(ctx context.Context, member *model.ProjectGrantMember) (*model.ProjectGrantMember, error) - RemoveProjectGrantMember(ctx context.Context, projectID, grantID, userID string) error GetProjectGrantMemberRoles() []string } diff --git a/internal/project/repository/eventsourcing/eventstore.go b/internal/project/repository/eventsourcing/eventstore.go index 81d60dbcf8..1b53c4e3e8 100644 --- a/internal/project/repository/eventsourcing/eventstore.go +++ b/internal/project/repository/eventsourcing/eventstore.go @@ -213,19 +213,19 @@ func (es *ProjectEventstore) ProjectMemberByIDs(ctx context.Context, member *pro if _, m := project.GetMember(member.UserID); m != nil { return m, nil } - return nil, caos_errs.ThrowNotFound(nil, "EVENT-3udjs", "Errors.Project.MemberNotFound") + return nil, caos_errs.ThrowNotFound(nil, "EVENT-3udjs", "Errors.Project.Member.NotFound") } func (es *ProjectEventstore) AddProjectMember(ctx context.Context, member *proj_model.ProjectMember) (*proj_model.ProjectMember, error) { if !member.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-2OWkC", "Errors.Project.MemberInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-2OWkC", "Errors.Project.Member.Invalid") } existingProject, err := es.ProjectByID(ctx, member.AggregateID) if err != nil { return nil, err } if _, m := existingProject.GetMember(member.UserID); m != nil { - return nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-idke6", "Errors.Project.MemberAlreadyExists") + return nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-idke6", "Errors.Project.Member.AlreadyExists") } repoProject := model.ProjectFromModel(existingProject) repoMember := model.ProjectMemberFromModel(member) @@ -245,14 +245,14 @@ func (es *ProjectEventstore) AddProjectMember(ctx context.Context, member *proj_ func (es *ProjectEventstore) ChangeProjectMember(ctx context.Context, member *proj_model.ProjectMember) (*proj_model.ProjectMember, error) { if !member.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-Buh04", "Errors.Project.MemberInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-Buh04", "Errors.Project.Member.Invalid") } existingProject, err := es.ProjectByID(ctx, member.AggregateID) if err != nil { return nil, err } if _, m := existingProject.GetMember(member.UserID); m == nil { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-oe39f", "Errors.Project.MemberNotExisting") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-oe39f", "Errors.Project.Member.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoMember := model.ProjectMemberFromModel(member) @@ -272,14 +272,14 @@ func (es *ProjectEventstore) ChangeProjectMember(ctx context.Context, member *pr func (es *ProjectEventstore) RemoveProjectMember(ctx context.Context, member *proj_model.ProjectMember) error { if member.UserID == "" { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-d43fs", "Errors.Project.MemberInvalid") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-d43fs", "Errors.Project.Member.Invalid") } existingProject, err := es.ProjectByID(ctx, member.AggregateID) if err != nil { return err } if _, m := existingProject.GetMember(member.UserID); m == nil { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-swf34", "Errors.Project.MemberNotExisting") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-swf34", "Errors.Project.Member.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoMember := model.ProjectMemberFromModel(member) @@ -295,14 +295,14 @@ func (es *ProjectEventstore) RemoveProjectMember(ctx context.Context, member *pr func (es *ProjectEventstore) PrepareRemoveProjectMember(ctx context.Context, member *proj_model.ProjectMember) (*model.ProjectMember, *es_models.Aggregate, error) { if member.UserID == "" { - return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-tCXHE", "Errors.Project.MemberInvalid") + return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-tCXHE", "Errors.Project.Member.Invalid") } existingProject, err := es.ProjectByID(ctx, member.AggregateID) if err != nil { return nil, nil, err } if _, m := existingProject.GetMember(member.UserID); m == nil { - return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-wPcg5", "Errors.Project.MemberNotExisting") + return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-wPcg5", "Errors.Project.Member.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoMember := model.ProjectMemberFromModel(member) @@ -322,7 +322,7 @@ func (es *ProjectEventstore) AddProjectRoles(ctx context.Context, roles ...*proj } for _, role := range roles { if !role.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-iduG4", "Errors.Project.RoleInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-iduG4", "Errors.Project.Role.Invalid") } } existingProject, err := es.ProjectByID(ctx, roles[0].AggregateID) @@ -331,7 +331,7 @@ func (es *ProjectEventstore) AddProjectRoles(ctx context.Context, roles ...*proj } for _, role := range roles { if existingProject.ContainsRole(role) { - return nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-sk35t", "Errors.Project.RoleAlreadyExists") + return nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-sk35t", "Errors.Project.Role.AlreadyExists") } } @@ -354,14 +354,14 @@ func (es *ProjectEventstore) AddProjectRoles(ctx context.Context, roles ...*proj func (es *ProjectEventstore) ChangeProjectRole(ctx context.Context, role *proj_model.ProjectRole) (*proj_model.ProjectRole, error) { if !role.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9die3", "Errors.Project.RoleInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9die3", "Errors.Project.Role.Invalid") } existingProject, err := es.ProjectByID(ctx, role.AggregateID) if err != nil { return nil, err } if !existingProject.ContainsRole(role) { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die34", "Errors.Project.RoleNotExisting") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die34", "Errors.Project.Role.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoRole := model.ProjectRoleFromModel(role) @@ -381,14 +381,14 @@ func (es *ProjectEventstore) ChangeProjectRole(ctx context.Context, role *proj_m func (es *ProjectEventstore) PrepareRemoveProjectRole(ctx context.Context, role *proj_model.ProjectRole) (*model.Project, *es_models.Aggregate, error) { if role.Key == "" { - return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-id823", "Errors.Project.RoleInvalid") + return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-id823", "Errors.Project.Role.Invalid") } existingProject, err := es.ProjectByID(ctx, role.AggregateID) if err != nil { return nil, nil, err } if !existingProject.ContainsRole(role) { - return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-oe823", "Errors.Project.RoleNotExisting") + return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-oe823", "Errors.Project.Role.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoRole := model.ProjectRoleFromModel(role) @@ -502,12 +502,12 @@ func (es *ProjectEventstore) ApplicationByIDs(ctx context.Context, projectID, ap if _, a := project.GetApp(appID); a != nil { return a, nil } - return nil, caos_errs.ThrowNotFound(nil, "EVENT-8ei2s", "Errors.Project.AppNotFound") + return nil, caos_errs.ThrowNotFound(nil, "EVENT-8ei2s", "Errors.Project.App.NotFound") } func (es *ProjectEventstore) AddApplication(ctx context.Context, app *proj_model.Application) (*proj_model.Application, error) { if app == nil || !app.IsValid(true) { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9eidw", "Errors.Project.AppInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9eidw", "Errors.Project.App.Invalid") } existingProject, err := es.ProjectByID(ctx, app.AggregateID) if err != nil { @@ -551,14 +551,14 @@ func (es *ProjectEventstore) AddApplication(ctx context.Context, app *proj_model func (es *ProjectEventstore) ChangeApplication(ctx context.Context, app *proj_model.Application) (*proj_model.Application, error) { if app == nil || !app.IsValid(false) { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-dieuw", "Errors.Project.AppInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-dieuw", "Errors.Project.App.Invalid") } existingProject, err := es.ProjectByID(ctx, app.AggregateID) if err != nil { return nil, err } if _, app := existingProject.GetApp(app.AppID); app == nil { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die83", "Errors.Project.AppNotExisting") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die83", "Errors.Project.App.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoApp := model.AppFromModel(app) @@ -584,7 +584,7 @@ func (es *ProjectEventstore) RemoveApplication(ctx context.Context, app *proj_mo return err } if _, app := existingProject.GetApp(app.AppID); app == nil { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-di83s", "Errors.Project.AppNotExisting") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-di83s", "Errors.Project.App.NotExisting") } repoProject := model.ProjectFromModel(existingProject) appRepo := model.AppFromModel(app) @@ -606,7 +606,7 @@ func (es *ProjectEventstore) PrepareRemoveApplication(ctx context.Context, app * return nil, nil, err } if _, app := existingProject.GetApp(app.AppID); app == nil { - return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-gaOD2", "Errors.Project.AppNotExisting") + return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-gaOD2", "Errors.Project.App.NotExisting") } repoProject := model.ProjectFromModel(existingProject) appRepo := model.AppFromModel(app) @@ -674,7 +674,7 @@ func (es *ProjectEventstore) DeactivateApplication(ctx context.Context, projectI } app := &proj_model.Application{AppID: appID} if _, app := existingProject.GetApp(app.AppID); app == nil { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-slpe9", "Errors.Project.AppNotExisting") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-slpe9", "Errors.Project.App.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoApp := model.AppFromModel(app) @@ -701,7 +701,7 @@ func (es *ProjectEventstore) ReactivateApplication(ctx context.Context, projectI } app := &proj_model.Application{AppID: appID} if _, app := existingProject.GetApp(app.AppID); app == nil { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-ld92d", "Errors.Project.AppNotExisting") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-ld92d", "Errors.Project.App.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoApp := model.AppFromModel(app) @@ -720,7 +720,7 @@ func (es *ProjectEventstore) ReactivateApplication(ctx context.Context, projectI func (es *ProjectEventstore) ChangeOIDCConfig(ctx context.Context, config *proj_model.OIDCConfig) (*proj_model.OIDCConfig, error) { if config == nil || !config.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-du834", "Errors.Project.OIDCConfigInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-du834", "Errors.Project.App.OIDCConfigInvalid") } existingProject, err := es.ProjectByID(ctx, config.AggregateID) if err != nil { @@ -731,7 +731,7 @@ func (es *ProjectEventstore) ChangeOIDCConfig(ctx context.Context, config *proj_ return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-dkso8", "Errors.Project.AppNoExisting") } if app.Type != proj_model.AppTypeOIDC { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-98uje", "Errors.Project.AppIsNotOIDC") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-98uje", "Errors.Project.App.IsNotOIDC") } repoProject := model.ProjectFromModel(existingProject) repoConfig := model.OIDCConfigFromModel(config) @@ -750,7 +750,7 @@ func (es *ProjectEventstore) ChangeOIDCConfig(ctx context.Context, config *proj_ func (es *ProjectEventstore) ChangeOIDCConfigSecret(ctx context.Context, projectID, appID string) (*proj_model.OIDCConfig, error) { if appID == "" { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-7ue34", "Errors.Project.OIDCConfigInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-7ue34", "Errors.Project.App.OIDCConfigInvalid") } existingProject, err := es.ProjectByID(ctx, projectID) if err != nil { @@ -758,10 +758,10 @@ func (es *ProjectEventstore) ChangeOIDCConfigSecret(ctx context.Context, project } var app *proj_model.Application if _, app = existingProject.GetApp(appID); app == nil { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9odi4", "Errors.Project.AppNotExisting") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9odi4", "Errors.Project.App.NotExisting") } if app.Type != proj_model.AppTypeOIDC { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-dile4", "Errors.Project.AppIsNotOIDC") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-dile4", "Errors.Project.App.IsNotOIDC") } if app.OIDCConfig.AuthMethodType == proj_model.OIDCAuthMethodTypeNone { return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-GDrg2", "Errors.Project.OIDCAuthMethodNoneSecret") @@ -804,7 +804,7 @@ func (es *ProjectEventstore) VerifyOIDCClientSecret(ctx context.Context, project return caos_errs.ThrowPreconditionFailed(nil, "EVENT-D6hba", "Errors.Project.AppNoExisting") } if app.Type != proj_model.AppTypeOIDC { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-huywq", "Errors.Project.AppIsNotOIDC") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-huywq", "Errors.Project.App.IsNotOIDC") } ctx, spanHash := tracing.NewSpan(ctx) @@ -842,22 +842,22 @@ func (es *ProjectEventstore) ProjectGrantByIDs(ctx context.Context, projectID, g if _, g := project.GetGrant(grantID); g != nil { return g, nil } - return nil, caos_errs.ThrowNotFound(nil, "EVENT-slo45", "Errors.Project.GrantNotFound") + return nil, caos_errs.ThrowNotFound(nil, "EVENT-slo45", "Errors.Project.Grant.NotFound") } func (es *ProjectEventstore) AddProjectGrant(ctx context.Context, grant *proj_model.ProjectGrant) (*proj_model.ProjectGrant, error) { if grant == nil || !grant.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-37dhs", "Errors.Project.GrantInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-37dhs", "Errors.Project.Grant.Invalid") } project, err := es.ProjectByID(ctx, grant.AggregateID) if err != nil { return nil, err } if project.ContainsGrantForOrg(grant.GrantedOrgID) { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-7ug4g", "Errors.Project.GrantAlreadyExists") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-7ug4g", "Errors.Project.Grant.AlreadyExists") } if !project.ContainsRoles(grant.RoleKeys) { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-di83d", "Errors.Project.GrantHasNotExistingRole") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-di83d", "Errors.Project.Grant.HasNotExistingRole") } id, err := es.idGenerator.Next() if err != nil { @@ -881,7 +881,7 @@ func (es *ProjectEventstore) AddProjectGrant(ctx context.Context, grant *proj_mo func (es *ProjectEventstore) PrepareChangeProjectGrant(ctx context.Context, grant *proj_model.ProjectGrant) (*model.Project, func(ctx context.Context) (*es_models.Aggregate, error), []string, error) { if grant == nil && grant.GrantID == "" { - return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-8sie3", "Errors.Project.GrantInvalid") + return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-8sie3", "Errors.Project.Grant.Invalid") } existingProject, err := es.ProjectByID(ctx, grant.AggregateID) if err != nil { @@ -889,10 +889,10 @@ func (es *ProjectEventstore) PrepareChangeProjectGrant(ctx context.Context, gran } _, existingGrant := existingProject.GetGrant(grant.GrantID) if existingGrant == nil { - return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die83", "Errors.Project.GrantNotExisting") + return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die83", "Errors.Project.Grant.NotExisting") } if !existingProject.ContainsRoles(grant.RoleKeys) { - return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-di83d", "Error.Project.GrantHasNotExistingRole") + return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-di83d", "Error.Project.Grant.HasNotExistingRole") } removedRoles := existingGrant.GetRemovedRoles(grant.RoleKeys) repoProject := model.ProjectFromModel(existingProject) @@ -940,7 +940,7 @@ func (es *ProjectEventstore) PrepareRemoveProjectGrant(ctx context.Context, gran return nil, nil, err } if _, g := existingProject.GetGrant(grant.GrantID); g == nil { - return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9ie3s", "Errors.Project.GrantNotExisting") + return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9ie3s", "Errors.Project.Grant.NotExisting") } repoProject := model.ProjectFromModel(existingProject) grantRepo := model.GrantFromModel(grant) @@ -958,7 +958,7 @@ func (es *ProjectEventstore) DeactivateProjectGrant(ctx context.Context, project } grant := &proj_model.ProjectGrant{GrantID: grantID} if _, g := existingProject.GetGrant(grant.GrantID); g == nil { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-slpe9", "Errors.Project.GrantNotExisting") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-slpe9", "Errors.Project.Grant.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoGrant := model.GrantFromModel(grant) @@ -985,7 +985,7 @@ func (es *ProjectEventstore) ReactivateProjectGrant(ctx context.Context, project } grant := &proj_model.ProjectGrant{GrantID: grantID} if _, g := existingProject.GetGrant(grant.GrantID); g == nil { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-0spew", "Errors.Project.GrantNotExisting") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-0spew", "Errors.Project.Grant.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoGrant := model.GrantFromModel(grant) @@ -1016,19 +1016,19 @@ func (es *ProjectEventstore) ProjectGrantMemberByIDs(ctx context.Context, member return m, nil } } - return nil, caos_errs.ThrowNotFound(nil, "EVENT-LxiBI", "Errors.Project.MemberNotFound") + return nil, caos_errs.ThrowNotFound(nil, "EVENT-LxiBI", "Errors.Project.Member.NotFound") } func (es *ProjectEventstore) AddProjectGrantMember(ctx context.Context, member *proj_model.ProjectGrantMember) (*proj_model.ProjectGrantMember, error) { if !member.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-0dor4", "Errors.Project.MemberInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-0dor4", "Errors.Project.Member.Invalid") } existingProject, err := es.ProjectByID(ctx, member.AggregateID) if err != nil { return nil, err } if existingProject.ContainsGrantMember(member) { - return nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-8die3", "Errors.Project.MemberAlreadyExists") + return nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-8die3", "Errors.Project.Member.AlreadyExists") } repoProject := model.ProjectFromModel(existingProject) repoMember := model.GrantMemberFromModel(member) @@ -1049,14 +1049,14 @@ func (es *ProjectEventstore) AddProjectGrantMember(ctx context.Context, member * func (es *ProjectEventstore) ChangeProjectGrantMember(ctx context.Context, member *proj_model.ProjectGrantMember) (*proj_model.ProjectGrantMember, error) { if !member.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-dkw35", "Errors.Project.MemberInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-dkw35", "Errors.Project.Member.Invalid") } existingProject, err := es.ProjectByID(ctx, member.AggregateID) if err != nil { return nil, err } if !existingProject.ContainsGrantMember(member) { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-8dj4s", "Errors.Project.MemberNotExisting") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-8dj4s", "Errors.Project.Member.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoMember := model.GrantMemberFromModel(member) @@ -1077,14 +1077,14 @@ func (es *ProjectEventstore) ChangeProjectGrantMember(ctx context.Context, membe func (es *ProjectEventstore) RemoveProjectGrantMember(ctx context.Context, member *proj_model.ProjectGrantMember) error { if member.UserID == "" { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-8su4r", "Errors.Project.MemberInvalid") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-8su4r", "Errors.Project.Member.Invalid") } existingProject, err := es.ProjectByID(ctx, member.AggregateID) if err != nil { return err } if !existingProject.ContainsGrantMember(member) { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-9ode4", "Errors.Project.MemberNotExisting") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-9ode4", "Errors.Project.Member.NotExisting") } repoProject := model.ProjectFromModel(existingProject) repoMember := model.GrantMemberFromModel(member) diff --git a/internal/project/repository/view/application_view.go b/internal/project/repository/view/application_view.go index 2abfd9f47d..5dd284d96e 100644 --- a/internal/project/repository/view/application_view.go +++ b/internal/project/repository/view/application_view.go @@ -40,7 +40,7 @@ func ApplicationByOIDCClientID(db *gorm.DB, table, clientID string) (*model.Appl query := repository.PrepareGetByQuery(table, clientIDQuery) err := query(db, app) if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-DG1qh", "Errors.Project.AppNotFound") + return nil, caos_errs.ThrowNotFound(nil, "VIEW-DG1qh", "Errors.Project.App.NotFound") } return app, err } @@ -52,7 +52,7 @@ func ApplicationByProjectIDAndAppName(db *gorm.DB, table, projectID, appName str query := repository.PrepareGetByQuery(table, projectIDQuery, appNameQuery) err := query(db, app) if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-Jqw1z", "Errors.Project.AppNotFound") + return nil, caos_errs.ThrowNotFound(nil, "VIEW-Jqw1z", "Errors.Project.App.NotFound") } return app, err } diff --git a/internal/project/repository/view/project_grant_member_view.go b/internal/project/repository/view/project_grant_member_view.go index 46b32eb518..adff81d8a9 100644 --- a/internal/project/repository/view/project_grant_member_view.go +++ b/internal/project/repository/view/project_grant_member_view.go @@ -17,7 +17,7 @@ func ProjectGrantMemberByIDs(db *gorm.DB, table, grantID, userID string) (*model query := repository.PrepareGetByQuery(table, grantIDQuery, userIDQuery) err := query(db, grant) if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-Sgr32", "Errors.Project.MemberNotExisting") + return nil, caos_errs.ThrowNotFound(nil, "VIEW-Sgr32", "Errors.Project.Member.NotExisting") } return grant, err } diff --git a/internal/project/repository/view/project_grant_view.go b/internal/project/repository/view/project_grant_view.go index 3cc1d68520..d2f583dff4 100644 --- a/internal/project/repository/view/project_grant_view.go +++ b/internal/project/repository/view/project_grant_view.go @@ -17,7 +17,7 @@ func ProjectGrantByProjectAndOrg(db *gorm.DB, table, projectID, orgID string) (* query := repository.PrepareGetByQuery(table, projectIDQuery, orgIDQuery) err := query(db, projectGrant) if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-WR3z2", "Errors.Project.GrantNotExists") + return nil, caos_errs.ThrowNotFound(nil, "VIEW-WR3z2", "Errors.Project.Grant.NotExisting") } return projectGrant, err } @@ -28,7 +28,7 @@ func ProjectGrantByID(db *gorm.DB, table, grantID string) (*model.ProjectGrantVi query := repository.PrepareGetByQuery(table, grantIDQuery) err := query(db, projectGrant) if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-EGdh4", "Errors.Project.GrantNotFound") + return nil, caos_errs.ThrowNotFound(nil, "VIEW-EGdh4", "Errors.Project.Grant.NotFound") } return projectGrant, err } diff --git a/internal/project/repository/view/project_member_view.go b/internal/project/repository/view/project_member_view.go index cf26bce3d8..ccd24f6988 100644 --- a/internal/project/repository/view/project_member_view.go +++ b/internal/project/repository/view/project_member_view.go @@ -17,7 +17,7 @@ func ProjectMemberByIDs(db *gorm.DB, table, projectID, userID string) (*model.Pr query := repository.PrepareGetByQuery(table, projectIDQuery, userIDQuery) err := query(db, role) if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-EgWQ2", "Errors.Project.MemberNotExisting") + return nil, caos_errs.ThrowNotFound(nil, "VIEW-EgWQ2", "Errors.Project.Member.NotExisting") } return role, err } diff --git a/internal/project/repository/view/project_role_view.go b/internal/project/repository/view/project_role_view.go index 4065439d46..fdd27951d4 100644 --- a/internal/project/repository/view/project_role_view.go +++ b/internal/project/repository/view/project_role_view.go @@ -18,7 +18,7 @@ func ProjectRoleByIDs(db *gorm.DB, table, projectID, orgID, key string) (*model. query := repository.PrepareGetByQuery(table, projectIDQuery, grantIDQuery, keyQuery) err := query(db, role) if caos_errs.IsNotFound(err) { - return nil, caos_errs.ThrowNotFound(nil, "VIEW-Wtg72", "Errors.Project.RoleNotExisting") + return nil, caos_errs.ThrowNotFound(nil, "VIEW-Wtg72", "Errors.Project.Role.NotExisting") } return role, err } diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index 216f018ade..6d72a857a6 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -158,27 +158,36 @@ Errors: NotInactive: Projekt ist nicht deaktiviert NotFound: Project konnte nicht gefunden werden UserIDMissing: User ID fehlt - MemberNotFound: Member konnte nicht gefunden werden - MemberInvalid: Member ist ungültig - MemberAlreadyExists: Member existiert bereits - MemberNotExisting: Member existiert nicht + Member: + Invalid: Member ist ungültig + AlreadyExists: Member existiert bereits + NotExisting: Member existiert nicht + NotFound: Member konnte nicht gefunden werden MinimumOneRoleNeeded: Es muss mindestend eine Rolle hinzugefügt werden - RoleAlreadyExists: Rolle existiert bereits - RoleInvalid: Rolle ist ungültig - RoleNotExisting: Rolle existiert nicht + Role: + AlreadyExists: Rolle existiert bereits + Invalid: Rolle ist ungültig + NotExisting: Rolle existiert nicht IDMissing: ID fehlt - AppNotFound: Applikation nicht gefunden - AppInvalid: Applikation ist ungültig - AppNotExisting: Applikation exisitert nicht - OIDCConfigInvalid: OIDC Konfiguration ist ungültig - AppIsNotOIDC: Applikation ist nicht vom Typ OIDC + App: + AlreadyExists: Applikation existiert bereits + NotFound: Applikation nicht gefunden + Invalid: Applikation ist ungültig + NotExisting: Applikation exisitert nicht + IsNotOIDC: Applikation ist nicht vom Typ OIDC + NotActive: Applikation ist nicht aktiv + NotInactive: Applikation ist nickt inaktiv + OIDCConfigInvalid: OIDC Konfiguration ist ungültig OIDCAuthMethodNoneSecret: OIDC Auth Method None benötigt kein Secret RequiredFieldsMissing: Benötigte Felder fehlen - GrantNotFound: Grant konnte nicht gefunden werden - GrantInvalid: Projekt Grant ist ungültig - GrantAlreadyExists: Projekt Grant existiert bereits - GrantNotExists: Projekt Grant existiert nicht - GrantHasNotExistingRole: Eine der Rollen existiert nicht auf dem Projekt + Grant: + AlreadyExists: Projekt Grant existiert bereits + Invalid: Projekt Grant ist ungültig + NotFound: Grant konnte nicht gefunden werden + NotExisting: Projekt Grant existiert nicht + HasNotExistingRole: Eine der Rollen existiert nicht auf dem Projekt + NotActive: Projekt Grant ist nicht aktiv + NotInactive: Projekt Grant ist nicht inaktiv UserIDMisisng: User ID fehlt OIDCSecretInvalid: Client Secret ist ungültig IAM: diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index ed91ff7b45..9ba5001c48 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -158,27 +158,35 @@ Errors: NotInactive: Project is not deactivated NotFound: Porject not found UserIDMissing: User ID missing - MemberNotFound: Project member not found - MemberInvalid: Project member is invalid - MemberAlreadyExists: Project member already exists - MemberNotExisting: Project member doesn't exist + Member: + NotFound: Project member not found + Invalid: Project member is invalid + AlreadyExists: Project member already exists + NotExisting: Project member doesn't exist MinimumOneRoleNeeded: At least one role must be added - RoleAlreadyExists: Role already exists - RoleInvalid: Role is invalid - RoleNotExisting: Role doesn't exist + Role: + AlreadyExists: Role already exists + Invalid: Role is invalid + NotExisting: Role doesn't exist IDMissing: ID missing - AppNotFound: Application not found - AppInvalid: Application invalid - AppNotExisting: Application doesn't exist + App: + AlreadyExists: Application already exists + Invalid: Application invalid + NotExisting: Application doesn't exist + IsNotOIDC: Application is not type oidc + NotActive:: Application is not active + NotInactive: Application is not inactive OIDCConfigInvalid: OIDC configuration is invalid - AppIsNotOIDC: Application is not type oidc OIDCAuthMethodNoneSecret: OIDC Auth Method None does not require a secret RequiredFieldsMissing: Some required fields are missing - GrantNotFound: Grant not found - GrantInvalid: Project grant is invalid - GrantAlreadyExists: Project grant already exists - GrantNotExists: Project grant doesn't exist - GrantHasNotExistingRole: One role doesn't exist on project + Grant: + AlreadyExists: Project grant already exists + NotFound: Grant not found + Invalid: Project grant is invalid + NotExisting: Project grant doesn't exist + HasNotExistingRole: One role doesn't exist on project + NotActive: Project grant is not active + NotInactive: Project grant is not inactive UserIDMisisng: User ID missing OIDCSecretInvalid: Client Secret is invalid IAM: diff --git a/internal/v2/command/org.go b/internal/v2/command/org.go index 2062c6b681..27b73938d9 100644 --- a/internal/v2/command/org.go +++ b/internal/v2/command/org.go @@ -14,12 +14,23 @@ func (r *CommandSide) getOrg(ctx context.Context, orgID string) (*domain.Org, er if err != nil { return nil, err } - if writeModel.State == domain.OrgStateActive { + if writeModel.State == domain.OrgStateUnspecified || writeModel.State == domain.OrgStateRemoved { return nil, caos_errs.ThrowInternal(err, "COMMAND-4M9sf", "Errors.Org.NotFound") } return orgWriteModelToOrg(writeModel), nil } +func (r *CommandSide) checkOrgExists(ctx context.Context, orgID string) error { + orgWriteModel, err := r.getOrgWriteModelByID(ctx, orgID) + if err != nil { + return err + } + if orgWriteModel.State == domain.OrgStateUnspecified || orgWriteModel.State == domain.OrgStateRemoved { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0fs", "Errors.Org.NotFound") + } + return nil +} + func (r *CommandSide) SetUpOrg(ctx context.Context, organisation *domain.Org, admin *domain.Human) error { orgAgg, userAgg, orgMemberAgg, err := r.setUpOrg(ctx, organisation, admin) if err != nil { @@ -36,13 +47,10 @@ func (r *CommandSide) AddOrg(ctx context.Context, name, userID, resourceOwner st return nil, err } - active, err := r.checkUserExists(ctx, userID, resourceOwner) + err = r.checkUserExists(ctx, userID, resourceOwner) if err != nil { return nil, err } - if !active { - return nil, caos_errs.ThrowPreconditionFailed(err, "ORG-HBR2z", "Errors.User.NotFound") - } addedMember := NewOrgMemberWriteModel(orgAgg.ID(), userID) err = r.addOrgMember(ctx, orgAgg, addedMember, domain.NewMember(orgAgg.ID(), userID, domain.RoleOrgOwner)) if err != nil { diff --git a/internal/v2/command/project.go b/internal/v2/command/project.go index f2740ff1d1..a21398b166 100644 --- a/internal/v2/command/project.go +++ b/internal/v2/command/project.go @@ -32,13 +32,17 @@ func (r *CommandSide) addProject(ctx context.Context, projectAdd *domain.Project addedProject := NewProjectWriteModel(projectAdd.AggregateID, resourceOwner) projectAgg := ProjectAggregateFromWriteModel(&addedProject.WriteModel) - projectRole := domain.RoleOrgOwner - //if global { //TODO: ! - // projectRole = domain.RoleProjectOwnerGlobal - //} + projectRole := domain.RoleProjectOwner + iam, err := r.GetIAM(ctx) + if err != nil { + return nil, nil, err + } + if iam.GlobalOrgID == resourceOwner { + projectRole = domain.RoleProjectOwnerGlobal + } projectAgg.PushEvents( project.NewProjectAddedEvent(ctx, projectAdd.Name, resourceOwner), - project.NewMemberAddedEvent(ctx, ownerUserID, projectRole), + project.NewProjectMemberAddedEvent(ctx, ownerUserID, projectRole), ) return projectAgg, addedProject, nil } @@ -65,6 +69,101 @@ func (r *CommandSide) checkProjectExists(ctx context.Context, projectID, resourc return nil } +func (r *CommandSide) ChangeProject(ctx context.Context, projectChange *domain.Project, resourceOwner string) (*domain.Project, error) { + if !projectChange.IsValid() && projectChange.AggregateID != "" { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.Invalid") + } + + existingProject, err := r.getProjectWriteModelByID(ctx, projectChange.AggregateID, resourceOwner) + if err != nil { + return nil, err + } + if existingProject.State == domain.ProjectStateUnspecified || existingProject.State == domain.ProjectStateRemoved { + return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound") + } + + changedEvent, hasChanged, err := existingProject.NewChangedEvent(ctx, projectChange.Name, projectChange.ProjectRoleAssertion, projectChange.ProjectRoleCheck) + if err != nil { + return nil, err + } + if !hasChanged { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0fs", "Errors.NoChangesFound") + } + projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel) + projectAgg.PushEvents(changedEvent) + + err = r.eventstore.PushAggregate(ctx, existingProject, projectAgg) + if err != nil { + return nil, err + } + + return projectWriteModelToProject(existingProject), nil +} + +func (r *CommandSide) DeactivateProject(ctx context.Context, projectID string, resourceOwner string) error { + if projectID == "" || resourceOwner == "" { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-88iF0", "Errors.Project.ProjectIDMissing") + } + + existingProject, err := r.getProjectWriteModelByID(ctx, projectID, resourceOwner) + if err != nil { + return err + } + if existingProject.State == domain.ProjectStateUnspecified || existingProject.State == domain.ProjectStateRemoved { + return caos_errs.ThrowNotFound(nil, "COMMAND-112M9", "Errors.Project.NotFound") + } + if existingProject.State != domain.ProjectStateActive { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-mki55", "Errors.Project.NotActive") + } + + projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel) + projectAgg.PushEvents(project.NewProjectDeactivatedEvent(ctx)) + + return r.eventstore.PushAggregate(ctx, existingProject, projectAgg) +} + +func (r *CommandSide) ReactivateProject(ctx context.Context, projectID string, resourceOwner string) error { + if projectID == "" || resourceOwner == "" { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.ProjectIDMissing") + } + + existingProject, err := r.getProjectWriteModelByID(ctx, projectID, resourceOwner) + if err != nil { + return err + } + if existingProject.State == domain.ProjectStateUnspecified || existingProject.State == domain.ProjectStateRemoved { + return caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound") + } + if existingProject.State != domain.ProjectStateInactive { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M9bs", "Errors.Project.NotInctive") + } + + projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel) + projectAgg.PushEvents(project.NewProjectDeactivatedEvent(ctx)) + + return r.eventstore.PushAggregate(ctx, existingProject, projectAgg) +} + +func (r *CommandSide) RemoveProject(ctx context.Context, projectID, resourceOwner string) error { + if projectID == "" || resourceOwner == "" { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-66hM9", "Errors.Project.ProjectIDMissing") + } + + existingProject, err := r.getProjectWriteModelByID(ctx, projectID, resourceOwner) + if err != nil { + return err + } + if existingProject.State == domain.ProjectStateUnspecified || existingProject.State == domain.ProjectStateRemoved { + return caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.NotFound") + } + + projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel) + projectAgg.PushEvents(project.NewProjectRemovedEvent(ctx, existingProject.Name, existingProject.ResourceOwner)) + //TODO: Remove User Grants by ProjectID + + return r.eventstore.PushAggregate(ctx, existingProject, projectAgg) +} + func (r *CommandSide) getProjectWriteModelByID(ctx context.Context, projectID, resourceOwner string) (*ProjectWriteModel, error) { projectWriteModel := NewProjectWriteModel(projectID, resourceOwner) err := r.eventstore.FilterToQueryReducer(ctx, projectWriteModel) diff --git a/internal/v2/command/project_application.go b/internal/v2/command/project_application.go index 8f6aa56241..09f5d12f1e 100644 --- a/internal/v2/command/project_application.go +++ b/internal/v2/command/project_application.go @@ -8,66 +8,99 @@ import ( "github.com/caos/zitadel/internal/v2/repository/project" ) -func (r *CommandSide) AddApplication(ctx context.Context, application *domain.Application, resourceOwner string) (_ *domain.Application, err error) { - project, err := r.getProjectByID(ctx, application.AggregateID, resourceOwner) +func (r *CommandSide) ChangeApplication(ctx context.Context, projectID string, appChange domain.Application, resourceOwner string) (domain.Application, error) { + if appChange.GetAppID() == "" || appChange.GetApplicationName() == "" { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.App.Invalid") + } + + existingApp, err := r.getApplicationWriteModel(ctx, projectID, appChange.GetAppID(), resourceOwner) if err != nil { return nil, err } - addedApplication := NewApplicationWriteModel(application.AggregateID, resourceOwner) - projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel) - err = r.addApplication(ctx, projectAgg, project, application) - if err != nil { - return nil, err + if existingApp.State == domain.AppStateUnspecified || existingApp.State == domain.AppStateRemoved { + return nil, caos_errs.ThrowNotFound(nil, "COMMAND-28di9", "Errors.Project.App.NotExisting") } - err = r.eventstore.PushAggregate(ctx, addedApplication, projectAgg) + if existingApp.Name == appChange.GetApplicationName() { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2m8vx", "Errors.NoChangesFound") + } + projectAgg := ProjectAggregateFromWriteModel(&existingApp.WriteModel) + projectAgg.PushEvents( + project.NewApplicationChangedEvent(ctx, appChange.GetAppID(), existingApp.Name, appChange.GetApplicationName(), projectID), + ) + + err = r.eventstore.PushAggregate(ctx, existingApp, projectAgg) if err != nil { return nil, err } - return applicationWriteModelToApplication(addedApplication), nil + return applicationWriteModelToApplication(existingApp), nil } -func (r *CommandSide) addApplication(ctx context.Context, projectAgg *project.Aggregate, proj *domain.Project, application *domain.Application) (err error) { - if !application.IsValid(true) { - return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-Bff2g", "Errors.Application.Invalid") +func (r *CommandSide) DeactivateApplication(ctx context.Context, projectID, appID, resourceOwner string) error { + if projectID == "" || appID == "" { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-88fi0", "Errors.IDMissing") } - application.AppID, err = r.idGenerator.Next() + + existingApp, err := r.getApplicationWriteModel(ctx, projectID, appID, resourceOwner) if err != nil { return err } - - projectAgg.PushEvents(project.NewApplicationAddedEvent(ctx, application.AppID, application.Name, application.Type)) - - var stringPw string - if application.OIDCConfig != nil { - application.OIDCConfig.AppID = application.AppID - err = application.OIDCConfig.GenerateNewClientID(r.idGenerator, proj) - if err != nil { - return err - } - stringPw, err = application.OIDCConfig.GenerateClientSecretIfNeeded(r.applicationSecretGenerator) - if err != nil { - return err - } - projectAgg.PushEvents(project.NewOIDCConfigAddedEvent(ctx, - application.OIDCConfig.OIDCVersion, - application.OIDCConfig.AppID, - application.OIDCConfig.ClientID, - application.OIDCConfig.ClientSecret, - application.OIDCConfig.RedirectUris, - application.OIDCConfig.ResponseTypes, - application.OIDCConfig.GrantTypes, - application.OIDCConfig.ApplicationType, - application.OIDCConfig.AuthMethodType, - application.OIDCConfig.PostLogoutRedirectUris, - application.OIDCConfig.DevMode, - application.OIDCConfig.AccessTokenType, - application.OIDCConfig.AccessTokenRoleAssertion, - application.OIDCConfig.IDTokenRoleAssertion, - application.OIDCConfig.IDTokenUserinfoAssertion, - application.OIDCConfig.ClockSkew)) + if existingApp.State == domain.AppStateUnspecified || existingApp.State == domain.AppStateRemoved { + return caos_errs.ThrowNotFound(nil, "COMMAND-ov9d3", "Errors.Project.App.NotExisting") } - _ = stringPw + if existingApp.State != domain.AppStateActive { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-dsh35", "Errors.Project.App.NotActive") + } + projectAgg := ProjectAggregateFromWriteModel(&existingApp.WriteModel) + projectAgg.PushEvents(project.NewApplicationDeactivatedEvent(ctx, appID)) - return nil + return r.eventstore.PushAggregate(ctx, existingApp, projectAgg) +} + +func (r *CommandSide) ReactivateApplication(ctx context.Context, projectID, appID, resourceOwner string) error { + if projectID == "" || appID == "" { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-983dF", "Errors.IDMissing") + } + + existingApp, err := r.getApplicationWriteModel(ctx, projectID, appID, resourceOwner) + if err != nil { + return err + } + if existingApp.State == domain.AppStateUnspecified || existingApp.State == domain.AppStateRemoved { + return caos_errs.ThrowNotFound(nil, "COMMAND-ov9d3", "Errors.Project.App.NotExisting") + } + if existingApp.State != domain.AppStateInactive { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-1n8cM", "Errors.Project.App.NotInactive") + } + projectAgg := ProjectAggregateFromWriteModel(&existingApp.WriteModel) + projectAgg.PushEvents(project.NewApplicationReactivatedEvent(ctx, appID)) + + return r.eventstore.PushAggregate(ctx, existingApp, projectAgg) +} + +func (r *CommandSide) RemoveApplication(ctx context.Context, projectID, appID, resourceOwner string) error { + if projectID == "" || appID == "" { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-1b7Jf", "Errors.IDMissing") + } + + existingApp, err := r.getApplicationWriteModel(ctx, projectID, appID, resourceOwner) + if err != nil { + return err + } + if existingApp.State == domain.AppStateUnspecified || existingApp.State == domain.AppStateRemoved { + return caos_errs.ThrowNotFound(nil, "COMMAND-0po9s", "Errors.Project.App.NotExisting") + } + projectAgg := ProjectAggregateFromWriteModel(&existingApp.WriteModel) + projectAgg.PushEvents(project.NewApplicationRemovedEvent(ctx, appID, existingApp.Name, projectID)) + + return r.eventstore.PushAggregate(ctx, existingApp, projectAgg) +} + +func (r *CommandSide) getApplicationWriteModel(ctx context.Context, projectID, appID, resourceOwner string) (*ApplicationWriteModel, error) { + appWriteModel := NewApplicationWriteModelWithAppIDC(projectID, appID, resourceOwner) + err := r.eventstore.FilterToQueryReducer(ctx, appWriteModel) + if err != nil { + return nil, err + } + return appWriteModel, nil } diff --git a/internal/v2/command/project_application_model.go b/internal/v2/command/project_application_model.go index 8a9a18b47b..9ae62d391b 100644 --- a/internal/v2/command/project_application_model.go +++ b/internal/v2/command/project_application_model.go @@ -9,11 +9,19 @@ import ( type ApplicationWriteModel struct { eventstore.WriteModel - AppID string - State domain.AppState - Name string - Type domain.AppType - OIDCConfig *domain.OIDCConfig + AppID string + State domain.AppState + Name string +} + +func NewApplicationWriteModelWithAppIDC(projectID, appID, resourceOwner string) *ApplicationWriteModel { + return &ApplicationWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: projectID, + ResourceOwner: resourceOwner, + }, + AppID: appID, + } } func NewApplicationWriteModel(projectID, resourceOwner string) *ApplicationWriteModel { @@ -24,12 +32,35 @@ func NewApplicationWriteModel(projectID, resourceOwner string) *ApplicationWrite }, } } - func (wm *ApplicationWriteModel) AppendEvents(events ...eventstore.EventReader) { - wm.WriteModel.AppendEvents(events...) for _, event := range events { switch e := event.(type) { case *project.ApplicationAddedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.ApplicationChangedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.ApplicationDeactivatedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.ApplicationReactivatedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.ApplicationRemovedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.ProjectRemovedEvent: wm.WriteModel.AppendEvents(e) } } @@ -41,15 +72,37 @@ func (wm *ApplicationWriteModel) Reduce() error { case *project.ApplicationAddedEvent: wm.Name = e.Name wm.State = domain.AppStateActive - //case *project.ApplicationChangedEvent: - // wm.Name = e.Name + case *project.ApplicationChangedEvent: + wm.Name = e.Name + case *project.ApplicationDeactivatedEvent: + if wm.State == domain.AppStateRemoved { + continue + } + wm.State = domain.AppStateInactive + case *project.ApplicationReactivatedEvent: + if wm.State == domain.AppStateRemoved { + continue + } + wm.State = domain.AppStateActive + case *project.ApplicationRemovedEvent: + wm.State = domain.AppStateRemoved + case *project.ProjectRemovedEvent: + wm.State = domain.AppStateRemoved } } - return nil + return wm.WriteModel.Reduce() } func (wm *ApplicationWriteModel) Query() *eventstore.SearchQueryBuilder { return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, project.AggregateType). AggregateIDs(wm.AggregateID). ResourceOwner(wm.ResourceOwner) + //EventTypes( + // project.ApplicationAddedType, + // project.ApplicationChangedType, + // project.ApplicationDeactivatedType, + // project.ApplicationReactivatedType, + // project.ApplicationRemovedType, + // project.ProjectRemovedType, + //) } diff --git a/internal/v2/command/project_application_oidc.go b/internal/v2/command/project_application_oidc.go new file mode 100644 index 0000000000..92ba61e9b5 --- /dev/null +++ b/internal/v2/command/project_application_oidc.go @@ -0,0 +1,157 @@ +package command + +import ( + "context" + caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/v2/domain" + "github.com/caos/zitadel/internal/v2/repository/project" +) + +func (r *CommandSide) AddOIDCApplication(ctx context.Context, application *domain.OIDCApp, resourceOwner string) (_ *domain.OIDCApp, err error) { + project, err := r.getProjectByID(ctx, application.AggregateID, resourceOwner) + if err != nil { + return nil, err + } + addedApplication := NewOIDCApplicationWriteModel(application.AggregateID, resourceOwner) + projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel) + stringPw, err := r.addOIDCApplication(ctx, projectAgg, project, application, resourceOwner) + if err != nil { + return nil, err + } + addedApplication.AppID = application.AppID + err = r.eventstore.PushAggregate(ctx, addedApplication, projectAgg) + if err != nil { + return nil, err + } + + result := oidcWriteModelToOIDCConfig(addedApplication) + result.ClientSecretString = stringPw + result.FillCompliance() + return result, nil +} + +func (r *CommandSide) addOIDCApplication(ctx context.Context, projectAgg *project.Aggregate, proj *domain.Project, oidcApp *domain.OIDCApp, resourceOwner string) (stringPW string, err error) { + if !oidcApp.IsValid() { + return "", caos_errs.ThrowPreconditionFailed(nil, "PROJECT-Bff2g", "Errors.Application.Invalid") + } + oidcApp.AppID, err = r.idGenerator.Next() + if err != nil { + return "", err + } + + projectAgg.PushEvents(project.NewApplicationAddedEvent(ctx, oidcApp.AppID, oidcApp.AppName, resourceOwner)) + + var stringPw string + err = oidcApp.GenerateNewClientID(r.idGenerator, proj) + if err != nil { + return "", err + } + stringPw, err = oidcApp.GenerateClientSecretIfNeeded(r.applicationSecretGenerator) + if err != nil { + return "", err + } + projectAgg.PushEvents(project.NewOIDCConfigAddedEvent(ctx, + oidcApp.OIDCVersion, + oidcApp.AppID, + oidcApp.ClientID, + oidcApp.ClientSecret, + oidcApp.RedirectUris, + oidcApp.ResponseTypes, + oidcApp.GrantTypes, + oidcApp.ApplicationType, + oidcApp.AuthMethodType, + oidcApp.PostLogoutRedirectUris, + oidcApp.DevMode, + oidcApp.AccessTokenType, + oidcApp.AccessTokenRoleAssertion, + oidcApp.IDTokenRoleAssertion, + oidcApp.IDTokenUserinfoAssertion, + oidcApp.ClockSkew)) + + return stringPw, nil +} + +func (r *CommandSide) ChangeOIDCApplication(ctx context.Context, oidc *domain.OIDCApp, resourceOwner string) (*domain.OIDCApp, error) { + if !oidc.IsValid() { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-1m900", "Errors.Project.App.OIDCConfigInvalid") + } + + existingOIDC, err := r.getOIDCAppWriteModel(ctx, oidc.AggregateID, oidc.AppID, resourceOwner) + if err != nil { + return nil, err + } + if existingOIDC.State == domain.AppStateUnspecified || existingOIDC.State == domain.AppStateRemoved { + return nil, caos_errs.ThrowNotFound(nil, "COMMAND-2n8uU", "Errors.Project.App.NotExisting") + } + changedEvent, hasChanged, err := existingOIDC.NewChangedEvent( + ctx, + oidc.AppID, + oidc.ClientID, + oidc.RedirectUris, + oidc.PostLogoutRedirectUris, + oidc.ResponseTypes, + oidc.GrantTypes, + oidc.ApplicationType, + oidc.AuthMethodType, + oidc.OIDCVersion, + oidc.AccessTokenType, + oidc.DevMode, + oidc.AccessTokenRoleAssertion, + oidc.IDTokenRoleAssertion, + oidc.IDTokenUserinfoAssertion, + oidc.ClockSkew) + if err != nil { + return nil, err + } + if !hasChanged { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-1m88i", "Errors.NoChangesFound") + } + projectAgg := ProjectAggregateFromWriteModel(&existingOIDC.WriteModel) + projectAgg.PushEvents(changedEvent) + + err = r.eventstore.PushAggregate(ctx, existingOIDC, projectAgg) + if err != nil { + return nil, err + } + result := oidcWriteModelToOIDCConfig(existingOIDC) + result.FillCompliance() + return result, nil +} + +func (r *CommandSide) ChangeOIDCApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string) (*domain.OIDCApp, error) { + if projectID == "" || appID == "" { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-99i83", "Errors.IDMissing") + } + + existingOIDC, err := r.getOIDCAppWriteModel(ctx, projectID, appID, resourceOwner) + if err != nil { + return nil, err + } + if existingOIDC.State == domain.AppStateUnspecified || existingOIDC.State == domain.AppStateRemoved { + return nil, caos_errs.ThrowNotFound(nil, "COMMAND-2g66f", "Errors.Project.App.NotExisting") + } + cryptoSecret, stringPW, err := domain.NewClientSecret(r.applicationSecretGenerator) + if err != nil { + return nil, err + } + + projectAgg := ProjectAggregateFromWriteModel(&existingOIDC.WriteModel) + projectAgg.PushEvents(project.NewOIDCConfigSecretChangedEvent(ctx, appID, cryptoSecret)) + + err = r.eventstore.PushAggregate(ctx, existingOIDC, projectAgg) + if err != nil { + return nil, err + } + + result := oidcWriteModelToOIDCConfig(existingOIDC) + result.ClientSecretString = stringPW + return result, err +} +func (r *CommandSide) getOIDCAppWriteModel(ctx context.Context, projectID, appID, resourceOwner string) (*OIDCApplicationWriteModel, error) { + appWriteModel := NewOIDCApplicationWriteModelWithAppIDC(projectID, appID, resourceOwner) + err := r.eventstore.FilterToQueryReducer(ctx, appWriteModel) + if err != nil { + return nil, err + } + return appWriteModel, nil +} diff --git a/internal/v2/command/project_application_oidc_model.go b/internal/v2/command/project_application_oidc_model.go new file mode 100644 index 0000000000..4904dfbef4 --- /dev/null +++ b/internal/v2/command/project_application_oidc_model.go @@ -0,0 +1,289 @@ +package command + +import ( + "context" + "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/v2/domain" + "github.com/caos/zitadel/internal/v2/repository/project" + "reflect" + "time" +) + +type OIDCApplicationWriteModel struct { + eventstore.WriteModel + + AppID string + AppName string + ClientID string + ClientSecret *crypto.CryptoValue + ClientSecretString string + RedirectUris []string + ResponseTypes []domain.OIDCResponseType + GrantTypes []domain.OIDCGrantType + ApplicationType domain.OIDCApplicationType + AuthMethodType domain.OIDCAuthMethodType + PostLogoutRedirectUris []string + OIDCVersion domain.OIDCVersion + Compliance *domain.Compliance + DevMode bool + AccessTokenType domain.OIDCTokenType + AccessTokenRoleAssertion bool + IDTokenRoleAssertion bool + IDTokenUserinfoAssertion bool + ClockSkew time.Duration + State domain.AppState +} + +func NewOIDCApplicationWriteModelWithAppIDC(projectID, appID, resourceOwner string) *OIDCApplicationWriteModel { + return &OIDCApplicationWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: projectID, + ResourceOwner: resourceOwner, + }, + AppID: appID, + } +} + +func NewOIDCApplicationWriteModel(projectID, resourceOwner string) *OIDCApplicationWriteModel { + return &OIDCApplicationWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: projectID, + ResourceOwner: resourceOwner, + }, + } +} +func (wm *OIDCApplicationWriteModel) AppendEvents(events ...eventstore.EventReader) { + for _, event := range events { + switch e := event.(type) { + case *project.ApplicationAddedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.ApplicationChangedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.ApplicationDeactivatedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.ApplicationReactivatedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.ApplicationRemovedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.OIDCConfigAddedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.OIDCConfigChangedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.OIDCConfigSecretChangedEvent: + if e.AppID != wm.AppID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.ProjectRemovedEvent: + wm.WriteModel.AppendEvents(e) + } + } +} + +func (wm *OIDCApplicationWriteModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *project.ApplicationAddedEvent: + wm.AppName = e.Name + wm.State = domain.AppStateActive + case *project.ApplicationChangedEvent: + wm.AppName = e.Name + case *project.ApplicationDeactivatedEvent: + if wm.State == domain.AppStateRemoved { + continue + } + wm.State = domain.AppStateInactive + case *project.ApplicationReactivatedEvent: + if wm.State == domain.AppStateRemoved { + continue + } + wm.State = domain.AppStateActive + case *project.ApplicationRemovedEvent: + wm.State = domain.AppStateRemoved + case *project.OIDCConfigAddedEvent: + wm.appendAddOIDCEvent(e) + case *project.OIDCConfigChangedEvent: + wm.appendChangeOIDCEvent(e) + case *project.OIDCConfigSecretChangedEvent: + wm.ClientSecret = e.ClientSecret + case *project.ProjectRemovedEvent: + wm.State = domain.AppStateRemoved + } + } + return wm.WriteModel.Reduce() +} + +func (wm *OIDCApplicationWriteModel) appendAddOIDCEvent(e *project.OIDCConfigAddedEvent) { + wm.ClientID = e.ClientID + wm.ClientSecret = e.ClientSecret + wm.RedirectUris = e.RedirectUris + wm.ResponseTypes = e.ResponseTypes + wm.GrantTypes = e.GrantTypes + wm.ApplicationType = e.ApplicationType + wm.AuthMethodType = e.AuthMethodType + wm.PostLogoutRedirectUris = e.PostLogoutRedirectUris + wm.OIDCVersion = e.Version + wm.DevMode = e.DevMode + wm.AccessTokenType = e.AccessTokenType + wm.AccessTokenRoleAssertion = e.AccessTokenRoleAssertion + wm.IDTokenRoleAssertion = e.IDTokenRoleAssertion + wm.IDTokenUserinfoAssertion = e.IDTokenUserinfoAssertion + wm.ClockSkew = e.ClockSkew +} + +func (wm *OIDCApplicationWriteModel) appendChangeOIDCEvent(e *project.OIDCConfigChangedEvent) { + if e.ClientID != nil { + wm.ClientID = *e.ClientID + } + if e.RedirectUris != nil { + wm.RedirectUris = *e.RedirectUris + } + if e.ResponseTypes != nil { + wm.ResponseTypes = *e.ResponseTypes + } + if e.GrantTypes != nil { + wm.GrantTypes = *e.GrantTypes + } + if e.ApplicationType != nil { + wm.ApplicationType = *e.ApplicationType + } + if e.AuthMethodType != nil { + wm.AuthMethodType = *e.AuthMethodType + } + if e.PostLogoutRedirectUris != nil { + wm.PostLogoutRedirectUris = *e.PostLogoutRedirectUris + } + if e.Version != nil { + wm.OIDCVersion = *e.Version + } + if e.DevMode != nil { + wm.DevMode = *e.DevMode + } + if e.AccessTokenType != nil { + wm.AccessTokenType = *e.AccessTokenType + } + if e.AccessTokenRoleAssertion != nil { + wm.AccessTokenRoleAssertion = *e.AccessTokenRoleAssertion + } + if e.IDTokenRoleAssertion != nil { + wm.IDTokenRoleAssertion = *e.IDTokenRoleAssertion + } + if e.IDTokenUserinfoAssertion != nil { + wm.IDTokenUserinfoAssertion = *e.IDTokenUserinfoAssertion + } + if e.ClockSkew != nil { + wm.ClockSkew = *e.ClockSkew + } +} + +func (wm *OIDCApplicationWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, project.AggregateType). + AggregateIDs(wm.AggregateID). + ResourceOwner(wm.ResourceOwner) + //EventTypes( + // project.ApplicationAddedType, + // project.ApplicationChangedType, + // project.ApplicationDeactivatedType, + // project.ApplicationReactivatedType, + // project.ApplicationRemovedType, + // project.OIDCConfigAddedType, + // project.OIDCConfigChangedType, + // project.OIDCConfigSecretChangedType, + // project.ProjectRemovedType, + //) +} + +func (wm *OIDCApplicationWriteModel) NewChangedEvent( + ctx context.Context, + appID, + clientID string, + redirectURIS, + postLogoutRedirectURIs []string, + responseTypes []domain.OIDCResponseType, + grantTypes []domain.OIDCGrantType, + appType domain.OIDCApplicationType, + authMethodType domain.OIDCAuthMethodType, + oidcVersion domain.OIDCVersion, + accessTokenType domain.OIDCTokenType, + devMode, + accessTokenRoleAssertion, + idTokenRoleAssertion, + idTokenUserinfoAssertion bool, + clockSkew time.Duration, +) (*project.OIDCConfigChangedEvent, bool, error) { + changes := make([]project.OIDCConfigChanges, 0) + var err error + + if wm.ClientID != clientID { + changes = append(changes, project.ChangeClientID(clientID)) + } + if !reflect.DeepEqual(wm.RedirectUris, redirectURIS) { + changes = append(changes, project.ChangeRedirectURIs(redirectURIS)) + } + if !reflect.DeepEqual(wm.ResponseTypes, responseTypes) { + changes = append(changes, project.ChangeResponseTypes(responseTypes)) + } + if !reflect.DeepEqual(wm.GrantTypes, grantTypes) { + changes = append(changes, project.ChangeGrantTypes(grantTypes)) + } + if wm.ApplicationType != appType { + changes = append(changes, project.ChangeApplicationType(appType)) + } + if wm.AuthMethodType != authMethodType { + changes = append(changes, project.ChangeAuthMethodType(authMethodType)) + } + if !reflect.DeepEqual(wm.PostLogoutRedirectUris, postLogoutRedirectURIs) { + changes = append(changes, project.ChangePostLogoutRedirectURIs(postLogoutRedirectURIs)) + } + if wm.OIDCVersion != oidcVersion { + changes = append(changes, project.ChangeVersion(oidcVersion)) + } + if wm.DevMode != devMode { + changes = append(changes, project.ChangeDevMode(devMode)) + } + if wm.AccessTokenType != accessTokenType { + changes = append(changes, project.ChangeAccessTokenType(accessTokenType)) + } + if wm.AccessTokenRoleAssertion != accessTokenRoleAssertion { + changes = append(changes, project.ChangeAccessTokenRoleAssertion(accessTokenRoleAssertion)) + } + if wm.IDTokenRoleAssertion != idTokenRoleAssertion { + changes = append(changes, project.ChangeIDTokenRoleAssertion(idTokenRoleAssertion)) + } + if wm.IDTokenUserinfoAssertion != idTokenUserinfoAssertion { + changes = append(changes, project.ChangeIDTokenUserinfoAssertion(idTokenUserinfoAssertion)) + } + if wm.ClockSkew != clockSkew { + changes = append(changes, project.ChangeClockSkew(clockSkew)) + } + if len(changes) == 0 { + return nil, false, nil + } + changeEvent, err := project.NewOIDCConfigChangedEvent(ctx, appID, changes) + if err != nil { + return nil, false, err + } + return changeEvent, true, nil +} diff --git a/internal/v2/command/project_converter.go b/internal/v2/command/project_converter.go index 2502955dfc..37f20204d5 100644 --- a/internal/v2/command/project_converter.go +++ b/internal/v2/command/project_converter.go @@ -13,13 +13,62 @@ func projectWriteModelToProject(writeModel *ProjectWriteModel) *domain.Project { } } -func applicationWriteModelToApplication(writeModel *ApplicationWriteModel) *domain.Application { - return &domain.Application{ - ObjectRoot: writeModelToObjectRoot(writeModel.WriteModel), - AppID: writeModel.AggregateID, - State: writeModel.State, - Name: writeModel.Name, - Type: writeModel.Type, - //TODO: OIDC Config +func projectGrantWriteModelToProjectGrant(writeModel *ProjectGrantWriteModel) *domain.ProjectGrant { + return &domain.ProjectGrant{ + ObjectRoot: writeModelToObjectRoot(writeModel.WriteModel), + GrantID: writeModel.GrantID, + GrantedOrgID: writeModel.GrantedOrgID, + RoleKeys: writeModel.RoleKeys, + State: writeModel.State, + } +} + +func applicationWriteModelToApplication(writeModel *ApplicationWriteModel) domain.Application { + return &domain.ChangeApp{ + AppID: writeModel.AppID, + AppName: writeModel.Name, + State: writeModel.State, + } +} + +func oidcWriteModelToOIDCConfig(writeModel *OIDCApplicationWriteModel) *domain.OIDCApp { + return &domain.OIDCApp{ + ObjectRoot: writeModelToObjectRoot(writeModel.WriteModel), + AppID: writeModel.AggregateID, + AppName: writeModel.AppName, + State: writeModel.State, + ClientID: writeModel.ClientID, + ClientSecret: writeModel.ClientSecret, + RedirectUris: writeModel.RedirectUris, + ResponseTypes: writeModel.ResponseTypes, + GrantTypes: writeModel.GrantTypes, + ApplicationType: writeModel.ApplicationType, + AuthMethodType: writeModel.AuthMethodType, + PostLogoutRedirectUris: writeModel.PostLogoutRedirectUris, + OIDCVersion: writeModel.OIDCVersion, + DevMode: writeModel.DevMode, + AccessTokenType: writeModel.AccessTokenType, + AccessTokenRoleAssertion: writeModel.AccessTokenRoleAssertion, + IDTokenRoleAssertion: writeModel.IDTokenRoleAssertion, + IDTokenUserinfoAssertion: writeModel.IDTokenUserinfoAssertion, + ClockSkew: writeModel.ClockSkew, + } +} + +func roleWriteModelToRole(writeModel *ProjectRoleWriteModel) *domain.ProjectRole { + return &domain.ProjectRole{ + ObjectRoot: writeModelToObjectRoot(writeModel.WriteModel), + Key: writeModel.Key, + DisplayName: writeModel.DisplayName, + Group: writeModel.Group, + } +} + +func memberWriteModelToProjectGrantMember(writeModel *ProjectGrantMemberWriteModel) *domain.ProjectGrantMember { + return &domain.ProjectGrantMember{ + ObjectRoot: writeModelToObjectRoot(writeModel.WriteModel), + Roles: writeModel.Roles, + GrantID: writeModel.GrantID, + UserID: writeModel.UserID, } } diff --git a/internal/v2/command/project_grant.go b/internal/v2/command/project_grant.go new file mode 100644 index 0000000000..d11dafdf8c --- /dev/null +++ b/internal/v2/command/project_grant.go @@ -0,0 +1,145 @@ +package command + +import ( + "context" + caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/telemetry/tracing" + "github.com/caos/zitadel/internal/v2/domain" + "github.com/caos/zitadel/internal/v2/repository/project" + "reflect" +) + +func (r *CommandSide) AddProjectGrant(ctx context.Context, grant *domain.ProjectGrant, resourceOwner string) (_ *domain.ProjectGrant, err error) { + if !grant.IsValid() { + return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-Bff2g", "Errors.Project.Grant.Invalid") + } + grant.GrantID, err = r.idGenerator.Next() + if err != nil { + return nil, err + } + err = r.checkProjectExists(ctx, grant.AggregateID, resourceOwner) + if err != nil { + return nil, err + } + err = r.checkOrgExists(ctx, grant.GrantedOrgID) + if err != nil { + return nil, err + } + addedGrant := NewProjectGrantWriteModel(grant.GrantID, grant.AggregateID, resourceOwner) + projectAgg := ProjectAggregateFromWriteModel(&addedGrant.WriteModel) + + projectAgg.PushEvents(project.NewGrantAddedEvent(ctx, grant.GrantID, grant.GrantedOrgID, grant.AggregateID, grant.RoleKeys)) + + if err != nil { + return nil, err + } + err = r.eventstore.PushAggregate(ctx, addedGrant, projectAgg) + if err != nil { + return nil, err + } + + return projectGrantWriteModelToProjectGrant(addedGrant), nil +} + +func (r *CommandSide) ChangeProjectGrant(ctx context.Context, grant *domain.ProjectGrant, resourceOwner string) (_ *domain.ProjectGrant, err error) { + if grant.GrantID == "" { + return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-1j83s", "Errors.IDMissing") + } + err = r.checkProjectExists(ctx, grant.AggregateID, resourceOwner) + if err != nil { + return nil, err + } + existingGrant, err := r.projectGrantWriteModelByID(ctx, grant.GrantID, grant.AggregateID, resourceOwner) + if err != nil { + return nil, err + } + projectAgg := ProjectAggregateFromWriteModel(&existingGrant.WriteModel) + + if reflect.DeepEqual(existingGrant.RoleKeys, grant.RoleKeys) { + return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-0o0pL", "Errors.NoChangesFoundc") + } + projectAgg.PushEvents(project.NewGrantChangedEvent(ctx, grant.GrantID, grant.RoleKeys)) + //TODO: Change UserGrants (if role removed should be removed from user grant) + err = r.eventstore.PushAggregate(ctx, existingGrant, projectAgg) + if err != nil { + return nil, err + } + + return projectGrantWriteModelToProjectGrant(existingGrant), nil +} + +func (r *CommandSide) DeactivateProjectGrant(ctx context.Context, projectID, grantID, resourceOwner string) (err error) { + if grantID == "" || projectID == "" { + return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-p0s4V", "Errors.IDMissing") + } + err = r.checkProjectExists(ctx, projectID, resourceOwner) + if err != nil { + return err + } + existingGrant, err := r.projectGrantWriteModelByID(ctx, grantID, projectID, resourceOwner) + if err != nil { + return err + } + if existingGrant.State != domain.ProjectGrantStateActive { + return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-47fu8", "Errors.Project.Grant.NotActive") + } + projectAgg := ProjectAggregateFromWriteModel(&existingGrant.WriteModel) + + projectAgg.PushEvents(project.NewGrantDeactivateEvent(ctx, grantID)) + return r.eventstore.PushAggregate(ctx, existingGrant, projectAgg) +} + +func (r *CommandSide) ReactivateProjectGrant(ctx context.Context, projectID, grantID, resourceOwner string) (err error) { + if grantID == "" || projectID == "" { + return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-p0s4V", "Errors.IDMissing") + } + err = r.checkProjectExists(ctx, projectID, resourceOwner) + if err != nil { + return err + } + existingGrant, err := r.projectGrantWriteModelByID(ctx, grantID, projectID, resourceOwner) + if err != nil { + return err + } + if existingGrant.State != domain.ProjectGrantStateInactive { + return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-47fu8", "Errors.Project.Grant.NotInactive") + } + projectAgg := ProjectAggregateFromWriteModel(&existingGrant.WriteModel) + projectAgg.PushEvents(project.NewGrantReactivatedEvent(ctx, grantID)) + return r.eventstore.PushAggregate(ctx, existingGrant, projectAgg) +} + +func (r *CommandSide) RemoveProjectGrant(ctx context.Context, projectID, grantID, resourceOwner string) (err error) { + if grantID == "" || projectID == "" { + return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-1m9fJ", "Errors.IDMissing") + } + err = r.checkProjectExists(ctx, projectID, resourceOwner) + if err != nil { + return err + } + existingGrant, err := r.projectGrantWriteModelByID(ctx, grantID, projectID, resourceOwner) + if err != nil { + return err + } + projectAgg := ProjectAggregateFromWriteModel(&existingGrant.WriteModel) + projectAgg.PushEvents(project.NewGrantRemovedEvent(ctx, grantID, existingGrant.GrantedOrgID, projectID)) + //TODO: Cascade Remove usergrants + return r.eventstore.PushAggregate(ctx, existingGrant, projectAgg) +} + +func (r *CommandSide) projectGrantWriteModelByID(ctx context.Context, grantID, projectID, resourceOwner string) (member *ProjectGrantWriteModel, err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + + writeModel := NewProjectGrantWriteModel(grantID, projectID, resourceOwner) + err = r.eventstore.FilterToQueryReducer(ctx, writeModel) + if err != nil { + return nil, err + } + + if writeModel.State == domain.ProjectGrantStateUnspecified || writeModel.State == domain.ProjectGrantStateRemoved { + return nil, caos_errs.ThrowNotFound(nil, "PROJECT-D8JxR", "Errors.Project.Grant.NotFound") + } + + return writeModel, nil +} diff --git a/internal/v2/command/project_grant_member.go b/internal/v2/command/project_grant_member.go new file mode 100644 index 0000000000..60c0f64006 --- /dev/null +++ b/internal/v2/command/project_grant_member.go @@ -0,0 +1,100 @@ +package command + +import ( + "context" + "reflect" + + "github.com/caos/zitadel/internal/errors" + caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/telemetry/tracing" + "github.com/caos/zitadel/internal/v2/domain" + "github.com/caos/zitadel/internal/v2/repository/project" +) + +func (r *CommandSide) AddProjectGrantMember(ctx context.Context, member *domain.ProjectGrantMember, resourceOwner string) (*domain.ProjectGrantMember, error) { + if !member.IsValid() { + return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-8fi7G", "Errors.Project.Member.Invalid") + } + err := r.checkUserExists(ctx, member.UserID, "") + if err != nil { + return nil, err + } + addedMember := NewProjectGrantMemberWriteModel(member.AggregateID, member.UserID, member.GrantID) + err = r.eventstore.FilterToQueryReducer(ctx, addedMember) + if err != nil { + return nil, err + } + if addedMember.State == domain.MemberStateActive { + return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-16dVN", "Errors.Project.Member.AlreadyExists") + } + projectAgg := ProjectAggregateFromWriteModel(&addedMember.WriteModel) + projectAgg.PushEvents(project.NewProjectGrantMemberAddedEvent(ctx, member.AggregateID, member.UserID, member.GrantID, member.Roles...)) + + err = r.eventstore.PushAggregate(ctx, addedMember, projectAgg) + if err != nil { + return nil, err + } + + return memberWriteModelToProjectGrantMember(addedMember), nil +} + +//ChangeProjectGrantMember updates an existing member +func (r *CommandSide) ChangeProjectGrantMember(ctx context.Context, member *domain.ProjectGrantMember, resourceOwner string) (*domain.ProjectGrantMember, error) { + //TODO: check if roles valid + + if !member.IsValid() { + return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-109fs", "Errors.Project.Member.Invalid") + } + + existingMember, err := r.projectGrantMemberWriteModelByID(ctx, member.AggregateID, member.UserID, member.GrantID) + if err != nil { + return nil, err + } + + if reflect.DeepEqual(existingMember.Roles, member.Roles) { + return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-2n8vx", "Errors.Project.Member.RolesNotChanged") + } + projectAgg := ProjectAggregateFromWriteModel(&existingMember.WriteModel) + projectAgg.PushEvents(project.NewProjectGrantMemberChangedEvent(ctx, member.UserID, member.GrantID, member.Roles...)) + + events, err := r.eventstore.PushAggregates(ctx, projectAgg) + if err != nil { + return nil, err + } + + existingMember.AppendEvents(events...) + if err = existingMember.Reduce(); err != nil { + return nil, err + } + + return memberWriteModelToProjectGrantMember(existingMember), nil +} + +func (r *CommandSide) RemoveProjectGrantMember(ctx context.Context, projectID, userID, grantID, resourceOwner string) error { + m, err := r.projectGrantMemberWriteModelByID(ctx, projectID, userID, grantID) + if err != nil { + return err + } + + projectAgg := ProjectAggregateFromWriteModel(&m.WriteModel) + projectAgg.PushEvents(project.NewProjectGrantMemberRemovedEvent(ctx, projectID, userID, grantID)) + + return r.eventstore.PushAggregate(ctx, m, projectAgg) +} + +func (r *CommandSide) projectGrantMemberWriteModelByID(ctx context.Context, projectID, userID, grantID string) (member *ProjectGrantMemberWriteModel, err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + + writeModel := NewProjectGrantMemberWriteModel(projectID, userID, grantID) + err = r.eventstore.FilterToQueryReducer(ctx, writeModel) + if err != nil { + return nil, err + } + + if writeModel.State == domain.MemberStateUnspecified || writeModel.State == domain.MemberStateRemoved { + return nil, errors.ThrowNotFound(nil, "PROJECT-37fug", "Errors.NotFound") + } + + return writeModel, nil +} diff --git a/internal/v2/command/project_grant_member_model.go b/internal/v2/command/project_grant_member_model.go new file mode 100644 index 0000000000..f7d03861d3 --- /dev/null +++ b/internal/v2/command/project_grant_member_model.go @@ -0,0 +1,82 @@ +package command + +import ( + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/v2/domain" + "github.com/caos/zitadel/internal/v2/repository/project" +) + +type ProjectGrantMemberWriteModel struct { + eventstore.WriteModel + + GrantID string + UserID string + Roles []string + + State domain.MemberState +} + +func NewProjectGrantMemberWriteModel(projectID, userID, grantID string) *ProjectGrantMemberWriteModel { + return &ProjectGrantMemberWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: projectID, + }, + UserID: userID, + GrantID: grantID, + } +} + +func (wm *ProjectGrantMemberWriteModel) AppendEvents(events ...eventstore.EventReader) { + for _, event := range events { + switch e := event.(type) { + case *project.GrantMemberAddedEvent: + if e.UserID != wm.UserID || e.GrantID != wm.GrantID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.GrantMemberChangedEvent: + if e.UserID != wm.UserID || e.GrantID != wm.GrantID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.GrantMemberRemovedEvent: + if e.UserID != wm.UserID || e.GrantID != wm.GrantID { + continue + } + wm.WriteModel.AppendEvents(e) + case *project.GrantRemovedEvent: + if e.GrantID != wm.GrantID { + continue + } + wm.WriteModel.AppendEvents(e) + } + } +} + +func (wm *ProjectGrantMemberWriteModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *project.GrantMemberAddedEvent: + wm.Roles = e.Roles + wm.State = domain.MemberStateActive + case *project.GrantMemberChangedEvent: + wm.Roles = e.Roles + case *project.GrantMemberRemovedEvent: + wm.State = domain.MemberStateRemoved + case *project.GrantRemovedEvent, *project.ProjectRemovedEvent: + wm.State = domain.MemberStateRemoved + } + } + return wm.WriteModel.Reduce() +} + +func (wm *ProjectGrantMemberWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, project.AggregateType). + AggregateIDs(wm.AggregateID) + //EventTypes( + // project.GrantMemberAddedType, + // project.GrantMemberChangedType, + // project.GrantMemberRemovedType, + // project.GrantRemovedType, + // project.ProjectRemovedType) +} diff --git a/internal/v2/command/project_grant_model.go b/internal/v2/command/project_grant_model.go new file mode 100644 index 0000000000..bab4efa7d5 --- /dev/null +++ b/internal/v2/command/project_grant_model.go @@ -0,0 +1,104 @@ +package command + +import ( + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/v2/domain" + "github.com/caos/zitadel/internal/v2/repository/project" +) + +type ProjectGrantWriteModel struct { + eventstore.WriteModel + + GrantID string + GrantedOrgID string + RoleKeys []string + State domain.ProjectGrantState +} + +func NewProjectGrantWriteModel(grantID, projectID, resourceOwner string) *ProjectGrantWriteModel { + return &ProjectGrantWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: projectID, + ResourceOwner: resourceOwner, + }, + GrantID: grantID, + } +} + +func (wm *ProjectGrantWriteModel) AppendEvents(events ...eventstore.EventReader) { + for _, event := range events { + switch e := event.(type) { + case *project.GrantAddedEvent: + if e.GrantID == wm.GrantID { + wm.WriteModel.AppendEvents(e) + } + case *project.GrantChangedEvent: + if e.GrantID == wm.GrantID { + wm.WriteModel.AppendEvents(e) + } + case *project.GrantCascadeChangedEvent: + if e.GrantID == wm.GrantID { + wm.WriteModel.AppendEvents(e) + } + case *project.GrantDeactivateEvent: + if e.GrantID == wm.GrantID { + wm.WriteModel.AppendEvents(e) + } + case *project.GrantReactivatedEvent: + if e.GrantID == wm.GrantID { + wm.WriteModel.AppendEvents(e) + } + case *project.GrantRemovedEvent: + if e.GrantID == wm.GrantID { + wm.WriteModel.AppendEvents(e) + } + case *project.ProjectRemovedEvent: + wm.WriteModel.AppendEvents(e) + } + } +} + +func (wm *ProjectGrantWriteModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *project.GrantAddedEvent: + wm.GrantID = e.GrantID + wm.GrantedOrgID = e.GrantedOrgID + wm.RoleKeys = e.RoleKeys + wm.State = domain.ProjectGrantStateActive + case *project.GrantChangedEvent: + wm.RoleKeys = e.RoleKeys + case *project.GrantCascadeChangedEvent: + wm.RoleKeys = e.RoleKeys + case *project.GrantDeactivateEvent: + if wm.State == domain.ProjectGrantStateRemoved { + continue + } + wm.State = domain.ProjectGrantStateInactive + case *project.GrantReactivatedEvent: + if wm.State == domain.ProjectGrantStateRemoved { + continue + } + wm.State = domain.ProjectGrantStateActive + case *project.GrantRemovedEvent: + wm.State = domain.ProjectGrantStateRemoved + case *project.ProjectRemovedEvent: + wm.State = domain.ProjectGrantStateRemoved + } + } + return wm.WriteModel.Reduce() +} + +func (wm *ProjectGrantWriteModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, project.AggregateType). + AggregateIDs(wm.AggregateID). + ResourceOwner(wm.ResourceOwner) + //EventTypes( + // project.GrantAddedType, + // project.GrantChangedType, + // project.GrantCascadeChangedType, + // project.GrantDeactivatedType, + // project.GrantReactivatedType, + // project.GrantRemovedType, + // project.ProjectRemovedType) +} diff --git a/internal/v2/command/project_member.go b/internal/v2/command/project_member.go index a61cae1a0a..346a40dd6a 100644 --- a/internal/v2/command/project_member.go +++ b/internal/v2/command/project_member.go @@ -31,10 +31,14 @@ func (r *CommandSide) addProjectMember(ctx context.Context, projectAgg *project. //TODO: check if roles valid if !member.IsValid() { - return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-W8m4l", "Errors.Project.MemberInvalid") + return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-W8m4l", "Errors.Project.Member.Invalid") } - err := r.eventstore.FilterToQueryReducer(ctx, addedMember) + err := r.checkUserExists(ctx, addedMember.UserID, "") + if err != nil { + return err + } + err = r.eventstore.FilterToQueryReducer(ctx, addedMember) if err != nil { return err } @@ -42,7 +46,7 @@ func (r *CommandSide) addProjectMember(ctx context.Context, projectAgg *project. return errors.ThrowAlreadyExists(nil, "PROJECT-PtXi1", "Errors.Project.Member.AlreadyExists") } - projectAgg.PushEvents(project.NewMemberAddedEvent(ctx, member.UserID, member.Roles...)) + projectAgg.PushEvents(project.NewProjectMemberAddedEvent(ctx, member.UserID, member.Roles...)) return nil } @@ -52,7 +56,7 @@ func (r *CommandSide) ChangeProjectMember(ctx context.Context, member *domain.Me //TODO: check if roles valid if !member.IsValid() { - return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-LiaZi", "Errors.Project.MemberInvalid") + return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-LiaZi", "Errors.Project.Member.Invalid") } existingMember, err := r.projectMemberWriteModelByID(ctx, member.AggregateID, member.UserID, resourceOwner) @@ -64,7 +68,7 @@ func (r *CommandSide) ChangeProjectMember(ctx context.Context, member *domain.Me return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-LiaZi", "Errors.Project.Member.RolesNotChanged") } projectAgg := ProjectAggregateFromWriteModel(&existingMember.MemberWriteModel.WriteModel) - projectAgg.PushEvents(project.NewMemberChangedEvent(ctx, member.UserID, member.Roles...)) + projectAgg.PushEvents(project.NewProjectMemberChangedEvent(ctx, member.UserID, member.Roles...)) events, err := r.eventstore.PushAggregates(ctx, projectAgg) if err != nil { @@ -89,7 +93,7 @@ func (r *CommandSide) RemoveProjectMember(ctx context.Context, projectID, userID } projectAgg := ProjectAggregateFromWriteModel(&m.MemberWriteModel.WriteModel) - projectAgg.PushEvents(project.NewMemberRemovedEvent(ctx, userID)) + projectAgg.PushEvents(project.NewProjectMemberRemovedEvent(ctx, userID)) return r.eventstore.PushAggregate(ctx, m, projectAgg) } diff --git a/internal/v2/command/project_model.go b/internal/v2/command/project_model.go index b1261c5de2..1106fdd829 100644 --- a/internal/v2/command/project_model.go +++ b/internal/v2/command/project_model.go @@ -1,6 +1,7 @@ package command import ( + "context" "github.com/caos/zitadel/internal/eventstore/v2" "github.com/caos/zitadel/internal/v2/domain" "github.com/caos/zitadel/internal/v2/repository/project" @@ -33,12 +34,34 @@ func (wm *ProjectWriteModel) Reduce() error { switch e := event.(type) { case *project.ProjectAddedEvent: wm.Name = e.Name + wm.ProjectRoleAssertion = e.ProjectRoleAssertion + wm.ProjectRoleCheck = e.ProjectRoleCheck wm.State = domain.ProjectStateActive - //case *project.ProjectChangedEvent: - // wm.Name = e.Name + case *project.ProjectChangeEvent: + if e.Name != nil { + wm.Name = *e.Name + } + if e.ProjectRoleAssertion != nil { + wm.ProjectRoleAssertion = *e.ProjectRoleAssertion + } + if e.ProjectRoleCheck != nil { + wm.ProjectRoleCheck = *e.ProjectRoleCheck + } + case *project.ProjectDeactivatedEvent: + if wm.State == domain.ProjectStateRemoved { + continue + } + wm.State = domain.ProjectStateInactive + case *project.ProjectReactivatedEvent: + if wm.State == domain.ProjectStateRemoved { + continue + } + wm.State = domain.ProjectStateActive + case *project.ProjectRemovedEvent: + wm.State = domain.ProjectStateRemoved } } - return nil + return wm.WriteModel.Reduce() } func (wm *ProjectWriteModel) Query() *eventstore.SearchQueryBuilder { @@ -47,6 +70,34 @@ func (wm *ProjectWriteModel) Query() *eventstore.SearchQueryBuilder { ResourceOwner(wm.ResourceOwner) } +func (wm *ProjectWriteModel) NewChangedEvent( + ctx context.Context, + name string, + projectRoleAssertion, + projectRoleCheck bool, +) (*project.ProjectChangeEvent, bool, error) { + changes := make([]project.ProjectChanges, 0) + var err error + + if wm.Name != name { + changes = append(changes, project.ChangeName(name)) + } + if wm.ProjectRoleAssertion != projectRoleAssertion { + changes = append(changes, project.ChangeProjectRoleAssertion(projectRoleAssertion)) + } + if wm.ProjectRoleCheck != projectRoleCheck { + changes = append(changes, project.ChangeProjectRoleCheck(projectRoleCheck)) + } + if len(changes) == 0 { + return nil, false, nil + } + changeEvent, err := project.NewProjectChangeEvent(ctx, changes) + if err != nil { + return nil, false, err + } + return changeEvent, true, nil +} + func ProjectAggregateFromWriteModel(wm *eventstore.WriteModel) *project.Aggregate { return &project.Aggregate{ Aggregate: *eventstore.AggregateFromWriteModel(wm, project.AggregateType, project.AggregateVersion), diff --git a/internal/v2/command/project_role.go b/internal/v2/command/project_role.go new file mode 100644 index 0000000000..ce48b6cb87 --- /dev/null +++ b/internal/v2/command/project_role.go @@ -0,0 +1,121 @@ +package command + +import ( + "context" + caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/v2/domain" + "github.com/caos/zitadel/internal/v2/repository/project" +) + +func (r *CommandSide) AddProjectRole(ctx context.Context, projectRole *domain.ProjectRole, resourceOwner string) (_ *domain.ProjectRole, err error) { + err = r.checkProjectExists(ctx, projectRole.AggregateID, resourceOwner) + if err != nil { + return nil, err + } + + roleWriteModel := NewProjectRoleWriteModelWithKey(projectRole.Key, projectRole.AggregateID, resourceOwner) + projectAgg := ProjectAggregateFromWriteModel(&roleWriteModel.WriteModel) + r.addProjectRoles(ctx, projectAgg, projectRole.AggregateID, resourceOwner, projectRole) + + err = r.eventstore.PushAggregate(ctx, roleWriteModel, projectAgg) + if err != nil { + return nil, err + } + return roleWriteModelToRole(roleWriteModel), nil +} + +func (r *CommandSide) BulkAddProjectRole(ctx context.Context, projectID, resourceOwner string, projectRoles []*domain.ProjectRole) (err error) { + err = r.checkProjectExists(ctx, projectID, resourceOwner) + if err != nil { + return err + } + + roleWriteModel := NewProjectRoleWriteModel(projectID, resourceOwner) + projectAgg := ProjectAggregateFromWriteModel(&roleWriteModel.WriteModel) + r.addProjectRoles(ctx, projectAgg, projectID, resourceOwner, projectRoles...) + + return r.eventstore.PushAggregate(ctx, roleWriteModel, projectAgg) +} + +func (r *CommandSide) addProjectRoles(ctx context.Context, projectAgg *project.Aggregate, projectID, resourceOwner string, projectRoles ...*domain.ProjectRole) error { + for _, projectRole := range projectRoles { + if !projectRole.IsValid() { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.Invalid") + } + projectAgg.PushEvents( + project.NewRoleAddedEvent( + ctx, + projectRole.Key, + projectRole.DisplayName, + projectRole.Group, + projectID, + resourceOwner, + ), + ) + } + + return nil +} + +func (r *CommandSide) ChangeProjectRole(ctx context.Context, projectRole *domain.ProjectRole, resourceOwner string) (_ *domain.ProjectRole, err error) { + if !projectRole.IsValid() { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.Invalid") + } + err = r.checkProjectExists(ctx, projectRole.AggregateID, resourceOwner) + if err != nil { + return nil, err + } + + existingRole, err := r.getProjectRoleWriteModelByID(ctx, projectRole.Key, projectRole.AggregateID, resourceOwner) + if err != nil { + return nil, err + } + if existingRole.State == domain.ProjectRoleStateUnspecified || existingRole.State == domain.ProjectRoleStateRemoved { + return nil, caos_errs.ThrowNotFound(nil, "COMMAND-vv8M9", "Errors.Project.Role.NotExisting") + } + + projectAgg := ProjectAggregateFromWriteModel(&existingRole.WriteModel) + + changeEvent, changed, err := existingRole.NewProjectRoleChangedEvent(ctx, projectRole.Key, projectRole.DisplayName, projectRole.Group) + if err != nil { + return nil, err + } + if !changed { + return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0cs", "Errors.NoChangesFound") + } + projectAgg.PushEvents(changeEvent) + + err = r.eventstore.PushAggregate(ctx, existingRole, projectAgg) + if err != nil { + return nil, err + } + return roleWriteModelToRole(existingRole), nil +} + +func (r *CommandSide) RemoveProjectRole(ctx context.Context, projectID, key, resourceOwner string) (err error) { + if projectID == "" || key == "" { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4m9vS", "Errors.Project.Role.Invalid") + } + existingRole, err := r.getProjectRoleWriteModelByID(ctx, key, projectID, resourceOwner) + if err != nil { + return err + } + if existingRole.State == domain.ProjectRoleStateUnspecified || existingRole.State == domain.ProjectRoleStateRemoved { + return caos_errs.ThrowNotFound(nil, "COMMAND-m9vMf", "Errors.Project.Role.NotExisting") + } + + projectAgg := ProjectAggregateFromWriteModel(&existingRole.WriteModel) + projectAgg.PushEvents(project.NewRoleRemovedEvent(ctx, key, projectID, existingRole.ResourceOwner)) + //TODO: Update UserGrants (remove roles if on usergrants) + + return r.eventstore.PushAggregate(ctx, existingRole, projectAgg) +} + +func (r *CommandSide) getProjectRoleWriteModelByID(ctx context.Context, key, projectID, resourceOwner string) (*ProjectRoleWriteModel, error) { + projectRoleWriteModel := NewProjectRoleWriteModelWithKey(key, projectID, resourceOwner) + err := r.eventstore.FilterToQueryReducer(ctx, projectRoleWriteModel) + if err != nil { + return nil, err + } + return projectRoleWriteModel, nil +} diff --git a/internal/v2/command/project_role_model.go b/internal/v2/command/project_role_model.go new file mode 100644 index 0000000000..dc54d912a1 --- /dev/null +++ b/internal/v2/command/project_role_model.go @@ -0,0 +1,121 @@ +package command + +import ( + "context" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/v2/domain" + "github.com/caos/zitadel/internal/v2/repository/project" +) + +type ProjectRoleWriteModel struct { + eventstore.WriteModel + + Key string + DisplayName string + Group string + State domain.ProjectRoleState +} + +func NewProjectRoleWriteModelWithKey(key, projectID, resourceOwner string) *ProjectRoleWriteModel { + return &ProjectRoleWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: projectID, + ResourceOwner: resourceOwner, + }, + Key: key, + } +} + +func NewProjectRoleWriteModel(projectID, resourceOwner string) *ProjectRoleWriteModel { + return &ProjectRoleWriteModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: projectID, + ResourceOwner: resourceOwner, + }, + } +} + +func (wm *ProjectRoleWriteModel) AppendEvents(events ...eventstore.EventReader) { + for _, event := range events { + switch e := event.(type) { + case *project.RoleAddedEvent: + if e.Key == wm.Key { + wm.WriteModel.AppendEvents(e) + } + case *project.RoleChangedEvent: + if e.Key == wm.Key { + wm.WriteModel.AppendEvents(e) + } + case *project.RoleRemovedEvent: + if e.Key == wm.Key { + wm.WriteModel.AppendEvents(e) + } + case *project.ProjectRemovedEvent: + wm.WriteModel.AppendEvents(e) + } + } +} + +func (wm *ProjectRoleWriteModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *project.RoleAddedEvent: + wm.Key = e.Key + wm.DisplayName = e.DisplayName + wm.Group = e.Group + wm.State = domain.ProjectRoleStateActive + case *project.RoleChangedEvent: + wm.Key = e.Key + if e.DisplayName != nil { + wm.DisplayName = *e.DisplayName + } + if e.Group != nil { + wm.Group = *e.Group + } + case *project.RoleRemovedEvent: + wm.State = domain.ProjectRoleStateRemoved + case *project.ProjectRemovedEvent: + wm.State = domain.ProjectRoleStateRemoved + } + } + return wm.WriteModel.Reduce() +} + +func (wm *ProjectRoleWriteModel) Query() *eventstore.SearchQueryBuilder { + //types := []eventstore.EventType{ + // project.RoleAddedType, + // project.RoleChangedType, + // project.RoleRemovedType, + // project.ProjectRemovedType, + //} + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, project.AggregateType). + AggregateIDs(wm.AggregateID). + ResourceOwner(wm.ResourceOwner) + //EventTypes(types...) +} + +func (wm *ProjectRoleWriteModel) NewProjectRoleChangedEvent( + ctx context.Context, + key, + displayName, + group string, +) (*project.RoleChangedEvent, bool, error) { + changes := make([]project.RoleChanges, 0) + var err error + changes = append(changes, project.ChangeKey(key)) + + if wm.DisplayName != displayName { + changes = append(changes, project.ChangeDisplayName(displayName)) + } + if wm.Group != group { + changes = append(changes, project.ChangeGroup(group)) + } + if len(changes) == 0 { + return nil, false, nil + } + changeEvent, err := project.NewRoleChangedEvent(ctx, changes) + if err != nil { + return nil, false, err + } + return changeEvent, true, nil +} diff --git a/internal/v2/command/setup_step1.go b/internal/v2/command/setup_step1.go index 768e35d066..a0e2633d75 100644 --- a/internal/v2/command/setup_step1.go +++ b/internal/v2/command/setup_step1.go @@ -2,11 +2,11 @@ package command import ( "context" + "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/logging" caos_errs "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/eventstore/v2" "github.com/caos/zitadel/internal/v2/domain" iam_repo "github.com/caos/zitadel/internal/v2/repository/iam" @@ -159,7 +159,7 @@ func (r *CommandSide) SetupStep1(ctx context.Context, step1 *Step1) error { } //create applications for _, app := range proj.OIDCApps { - err = setUpApplication(ctx, r, projectAgg, project, app) + err = setUpApplication(ctx, r, projectAgg, project, app, orgAgg.ID()) if err != nil { return err } @@ -177,27 +177,25 @@ func (r *CommandSide) SetupStep1(ctx context.Context, step1 *Step1) error { return nil } -func setUpApplication(ctx context.Context, r *CommandSide, projectAgg *project.Aggregate, project *domain.Project, oidcApp OIDCApp) error { - app := &domain.Application{ +func setUpApplication(ctx context.Context, r *CommandSide, projectAgg *project.Aggregate, project *domain.Project, oidcApp OIDCApp, resourceOwner string) error { + app := &domain.OIDCApp{ ObjectRoot: models.ObjectRoot{ AggregateID: projectAgg.ID(), }, - Name: oidcApp.Name, - Type: domain.AppTypeOIDC, - OIDCConfig: &domain.OIDCConfig{ - RedirectUris: oidcApp.RedirectUris, - ResponseTypes: getOIDCResponseTypes(oidcApp.ResponseTypes), - GrantTypes: getOIDCGrantTypes(oidcApp.GrantTypes), - ApplicationType: getOIDCApplicationType(oidcApp.ApplicationType), - AuthMethodType: getOIDCAuthMethod(oidcApp.AuthMethodType), - DevMode: oidcApp.DevMode, - }, + AppName: oidcApp.Name, + RedirectUris: oidcApp.RedirectUris, + ResponseTypes: getOIDCResponseTypes(oidcApp.ResponseTypes), + GrantTypes: getOIDCGrantTypes(oidcApp.GrantTypes), + ApplicationType: getOIDCApplicationType(oidcApp.ApplicationType), + AuthMethodType: getOIDCAuthMethod(oidcApp.AuthMethodType), + DevMode: oidcApp.DevMode, } - err := r.addApplication(ctx, projectAgg, project, app) + + _, err := r.addOIDCApplication(ctx, projectAgg, project, app, resourceOwner) if err != nil { return err } - logging.LogWithFields("SETUP-Edgw4", "name", app.Name, "clientID", app.OIDCConfig.ClientID).Info("application set up") + logging.LogWithFields("SETUP-Edgw4", "name", app.AppName, "clientID", app.ClientID).Info("application set up") return nil } diff --git a/internal/v2/command/user.go b/internal/v2/command/user.go index 07c140515f..53576c39e1 100644 --- a/internal/v2/command/user.go +++ b/internal/v2/command/user.go @@ -139,12 +139,15 @@ func (r *CommandSide) RemoveUser(ctx context.Context, userID, resourceOwner stri return r.eventstore.PushAggregate(ctx, existingUser, userAgg) } -func (r *CommandSide) checkUserExists(ctx context.Context, userID, resourceOwner string) (bool, error) { +func (r *CommandSide) checkUserExists(ctx context.Context, userID, resourceOwner string) error { userWriteModel, err := r.userWriteModelByID(ctx, userID, resourceOwner) if err != nil { - return false, err + return err } - return userWriteModel.UserState != domain.UserStateUnspecified && userWriteModel.UserState != domain.UserStateDeleted, nil + if userWriteModel.UserState == domain.UserStateUnspecified || userWriteModel.UserState == domain.UserStateDeleted { + return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0fs", "Errors.User.NotFound") + } + return nil } func (r *CommandSide) userWriteModelByID(ctx context.Context, userID, resourceOwner string) (writeModel *UserWriteModel, err error) { diff --git a/internal/v2/command/user_grant.go b/internal/v2/command/user_grant.go index 8b50291c84..aec6bdf2f1 100644 --- a/internal/v2/command/user_grant.go +++ b/internal/v2/command/user_grant.go @@ -31,13 +31,10 @@ func (r *CommandSide) addUserGrant(ctx context.Context, userGrant *domain.UserGr if !userGrant.IsValid() { return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0fs", "Errors.UserGrant.Invalid") } - exists, err := r.checkUserExists(ctx, userGrant.UserID, resourceOwner) + err = r.checkUserExists(ctx, userGrant.UserID, "") if err != nil { return nil, nil, err } - if !exists { - return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0fs", "Errors.User.NotFound") - } err = r.checkProjectExists(ctx, userGrant.ProjectID, resourceOwner) if err != nil { return nil, nil, err diff --git a/internal/v2/command/user_grant_model.go b/internal/v2/command/user_grant_model.go index 94d9b90fda..65cc9363f3 100644 --- a/internal/v2/command/user_grant_model.go +++ b/internal/v2/command/user_grant_model.go @@ -58,7 +58,7 @@ func (wm *UserGrantWriteModel) Reduce() error { wm.State = domain.UserGrantStateRemoved } } - return nil + return wm.WriteModel.Reduce() } func (wm *UserGrantWriteModel) Query() *eventstore.SearchQueryBuilder { diff --git a/internal/v2/command/user_model.go b/internal/v2/command/user_model.go index 1fab282942..23c9fe87c2 100644 --- a/internal/v2/command/user_model.go +++ b/internal/v2/command/user_model.go @@ -68,9 +68,12 @@ func (wm *UserWriteModel) Reduce() error { } func (wm *UserWriteModel) Query() *eventstore.SearchQueryBuilder { - return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType). - AggregateIDs(wm.AggregateID). - ResourceOwner(wm.ResourceOwner) + query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType). + AggregateIDs(wm.AggregateID) + if wm.ResourceOwner != "" { + query.ResourceOwner(wm.ResourceOwner) + } + return query } func UserAggregateFromWriteModel(wm *eventstore.WriteModel) *user.Aggregate { diff --git a/internal/v2/domain/application.go b/internal/v2/domain/application.go index a543ab3e13..3fb1e451ae 100644 --- a/internal/v2/domain/application.go +++ b/internal/v2/domain/application.go @@ -1,17 +1,9 @@ package domain -import ( - "github.com/caos/zitadel/internal/eventstore/models" -) - -type Application struct { - models.ObjectRoot - - AppID string - State AppState - Name string - Type AppType - OIDCConfig *OIDCConfig +type Application interface { + GetAppID() string + GetApplicationName() string + GetState() AppState } type AppState int32 @@ -23,27 +15,20 @@ const ( AppStateRemoved ) -type AppType int32 - -const ( - AppTypeUnspecified AppType = iota - AppTypeOIDC - AppTypeSAML -) - -func NewApplication(projectID, appID string) *Application { - return &Application{ObjectRoot: models.ObjectRoot{AggregateID: projectID}, AppID: appID, State: AppStateActive} +type ChangeApp struct { + AppID string + AppName string + State AppState } -func (a *Application) IsValid(includeConfig bool) bool { - if a.Name == "" || a.AggregateID == "" { - return false - } - if !includeConfig { - return true - } - if a.Type == AppTypeOIDC && !a.OIDCConfig.IsValid() { - return false - } - return true +func (a *ChangeApp) GetAppID() string { + return a.AppID +} + +func (a *ChangeApp) GetApplicationName() string { + return a.AppName +} + +func (a *ChangeApp) GetState() AppState { + return a.State } diff --git a/internal/v2/domain/application_oidc.go b/internal/v2/domain/application_oidc.go new file mode 100644 index 0000000000..b652b8f2b6 --- /dev/null +++ b/internal/v2/domain/application_oidc.go @@ -0,0 +1,327 @@ +package domain + +import ( + "fmt" + "strings" + "time" + + "github.com/caos/logging" + + "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/id" +) + +const ( + http = "http://" + httpLocalhost = "http://localhost:" + httpLocalhost2 = "http://localhost/" + https = "https://" +) + +type OIDCApp struct { + models.ObjectRoot + + AppID string + AppName string + ClientID string + ClientSecret *crypto.CryptoValue + ClientSecretString string + RedirectUris []string + ResponseTypes []OIDCResponseType + GrantTypes []OIDCGrantType + ApplicationType OIDCApplicationType + AuthMethodType OIDCAuthMethodType + PostLogoutRedirectUris []string + OIDCVersion OIDCVersion + Compliance *Compliance + DevMode bool + AccessTokenType OIDCTokenType + AccessTokenRoleAssertion bool + IDTokenRoleAssertion bool + IDTokenUserinfoAssertion bool + ClockSkew time.Duration + + State AppState +} + +func (h OIDCApp) GetUsername() string { + return h.AppName +} + +func (h OIDCApp) GetState() AppState { + return h.State +} + +type OIDCVersion int32 + +const ( + OIDCVersionV1 OIDCVersion = iota +) + +type OIDCResponseType int32 + +const ( + OIDCResponseTypeCode OIDCResponseType = iota + OIDCResponseTypeIDToken + OIDCResponseTypeIDTokenToken +) + +type OIDCGrantType int32 + +const ( + OIDCGrantTypeAuthorizationCode OIDCGrantType = iota + OIDCGrantTypeImplicit + OIDCGrantTypeRefreshToken +) + +type OIDCApplicationType int32 + +const ( + OIDCApplicationTypeWeb OIDCApplicationType = iota + OIDCApplicationTypeUserAgent + OIDCApplicationTypeNative +) + +type OIDCAuthMethodType int32 + +const ( + OIDCAuthMethodTypeBasic OIDCAuthMethodType = iota + OIDCAuthMethodTypePost + OIDCAuthMethodTypeNone +) + +type Compliance struct { + NoneCompliant bool + Problems []string +} + +type OIDCTokenType int32 + +const ( + OIDCTokenTypeBearer OIDCTokenType = iota + OIDCTokenTypeJWT +) + +func (c *OIDCApp) IsValid() bool { + grantTypes := c.getRequiredGrantTypes() + for _, grantType := range grantTypes { + ok := containsOIDCGrantType(c.GrantTypes, grantType) + if !ok { + return false + } + } + return true +} + +//ClientID random_number@projectname (eg. 495894098234@zitadel) +func (c *OIDCApp) GenerateNewClientID(idGenerator id.Generator, project *Project) error { + rndID, err := idGenerator.Next() + if err != nil { + return err + } + + c.ClientID = fmt.Sprintf("%v@%v", rndID, strings.ReplaceAll(strings.ToLower(project.Name), " ", "_")) + return nil +} + +func (c *OIDCApp) GenerateClientSecretIfNeeded(generator crypto.Generator) (string, error) { + if c.AuthMethodType == OIDCAuthMethodTypeNone { + return "", nil + } + return c.GenerateNewClientSecret(generator) +} + +func (c *OIDCApp) GenerateNewClientSecret(generator crypto.Generator) (string, error) { + cryptoValue, stringSecret, err := NewClientSecret(generator) + if err != nil { + return "", err + } + c.ClientSecret = cryptoValue + return stringSecret, nil +} + +func NewClientSecret(generator crypto.Generator) (*crypto.CryptoValue, string, error) { + cryptoValue, stringSecret, err := crypto.NewCode(generator) + if err != nil { + logging.Log("MODEL-UpnTI").OnError(err).Error("unable to create client secret") + return nil, "", errors.ThrowInternal(err, "MODEL-gH2Wl", "Errors.Project.CouldNotGenerateClientSecret") + } + return cryptoValue, stringSecret, nil +} + +func (c *OIDCApp) getRequiredGrantTypes() []OIDCGrantType { + grantTypes := make([]OIDCGrantType, 0) + implicit := false + for _, r := range c.ResponseTypes { + switch r { + case OIDCResponseTypeCode: + grantTypes = append(grantTypes, OIDCGrantTypeAuthorizationCode) + case OIDCResponseTypeIDToken, OIDCResponseTypeIDTokenToken: + if !implicit { + implicit = true + grantTypes = append(grantTypes, OIDCGrantTypeImplicit) + } + } + } + return grantTypes +} + +func containsOIDCGrantType(grantTypes []OIDCGrantType, grantType OIDCGrantType) bool { + for _, gt := range grantTypes { + if gt == grantType { + return true + } + } + return false +} + +func (c *OIDCApp) FillCompliance() { + c.Compliance = GetOIDCCompliance(c.OIDCVersion, c.ApplicationType, c.GrantTypes, c.ResponseTypes, c.AuthMethodType, c.RedirectUris) +} + +func GetOIDCCompliance(version OIDCVersion, appType OIDCApplicationType, grantTypes []OIDCGrantType, responseTypes []OIDCResponseType, authMethod OIDCAuthMethodType, redirectUris []string) *Compliance { + switch version { + case OIDCVersionV1: + return GetOIDCV1Compliance(appType, grantTypes, authMethod, redirectUris) + } + return nil +} + +func GetOIDCV1Compliance(appType OIDCApplicationType, grantTypes []OIDCGrantType, authMethod OIDCAuthMethodType, redirectUris []string) *Compliance { + compliance := &Compliance{NoneCompliant: false} + if redirectUris == nil || len(redirectUris) == 0 { + compliance.NoneCompliant = true + compliance.Problems = append([]string{"Application.OIDC.V1.NoRedirectUris"}, compliance.Problems...) + } + if containsOIDCGrantType(grantTypes, OIDCGrantTypeImplicit) && containsOIDCGrantType(grantTypes, OIDCGrantTypeAuthorizationCode) { + CheckRedirectUrisImplicitAndCode(compliance, appType, redirectUris) + } else { + if containsOIDCGrantType(grantTypes, OIDCGrantTypeImplicit) { + CheckRedirectUrisImplicit(compliance, appType, redirectUris) + } + if containsOIDCGrantType(grantTypes, OIDCGrantTypeAuthorizationCode) { + CheckRedirectUrisCode(compliance, appType, redirectUris) + } + } + + switch appType { + case OIDCApplicationTypeNative: + GetOIDCV1NativeApplicationCompliance(compliance, authMethod) + case OIDCApplicationTypeUserAgent: + GetOIDCV1UserAgentApplicationCompliance(compliance, authMethod) + } + if compliance.NoneCompliant { + compliance.Problems = append([]string{"Application.OIDC.V1.NotCompliant"}, compliance.Problems...) + } + return compliance +} + +func GetOIDCV1NativeApplicationCompliance(compliance *Compliance, authMethod OIDCAuthMethodType) { + if authMethod != OIDCAuthMethodTypeNone { + compliance.NoneCompliant = true + compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.Native.AuthMethodType.NotNone") + } +} + +func GetOIDCV1UserAgentApplicationCompliance(compliance *Compliance, authMethod OIDCAuthMethodType) { + if authMethod != OIDCAuthMethodTypeNone { + compliance.NoneCompliant = true + compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.UserAgent.AuthMethodType.NotNone") + } +} + +func CheckRedirectUrisCode(compliance *Compliance, appType OIDCApplicationType, redirectUris []string) { + if urlsAreHttps(redirectUris) { + return + } + if urlContainsPrefix(redirectUris, http) && appType != OIDCApplicationTypeWeb { + compliance.NoneCompliant = true + compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.Code.RedirectUris.HttpOnlyForWeb") + } + if containsCustom(redirectUris) && appType != OIDCApplicationTypeNative { + compliance.NoneCompliant = true + compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.Code.RedirectUris.CustomOnlyForNative") + } +} + +func CheckRedirectUrisImplicit(compliance *Compliance, appType OIDCApplicationType, redirectUris []string) { + if urlsAreHttps(redirectUris) { + return + } + if containsCustom(redirectUris) { + compliance.NoneCompliant = true + compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.Implicit.RedirectUris.CustomNotAllowed") + } + if urlContainsPrefix(redirectUris, http) { + if appType == OIDCApplicationTypeNative { + if !onlyLocalhostIsHttp(redirectUris) { + compliance.NoneCompliant = true + compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.Implicit.RedirectUris.NativeShouldBeHttpLocalhost") + } + return + } + compliance.NoneCompliant = true + compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.Implicit.RedirectUris.HttpNotAllowed") + } +} + +func CheckRedirectUrisImplicitAndCode(compliance *Compliance, appType OIDCApplicationType, redirectUris []string) { + if urlsAreHttps(redirectUris) { + return + } + if containsCustom(redirectUris) && appType != OIDCApplicationTypeNative { + compliance.NoneCompliant = true + compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.Implicit.RedirectUris.CustomNotAllowed") + } + if (urlContainsPrefix(redirectUris, httpLocalhost) || urlContainsPrefix(redirectUris, httpLocalhost2)) && appType != OIDCApplicationTypeNative { + compliance.NoneCompliant = true + compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.Implicit.RedirectUris.HttpLocalhostOnlyForNative") + } + if urlContainsPrefix(redirectUris, http) && !(urlContainsPrefix(redirectUris, httpLocalhost) || urlContainsPrefix(redirectUris, httpLocalhost2)) && appType != OIDCApplicationTypeWeb { + compliance.NoneCompliant = true + compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.Code.RedirectUris.HttpOnlyForWeb") + } + if !compliance.NoneCompliant { + compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.NotAllCombinationsAreAllowed") + } +} + +func urlsAreHttps(uris []string) bool { + for _, uri := range uris { + if !strings.HasPrefix(uri, https) { + return false + } + } + return true +} + +func urlContainsPrefix(uris []string, prefix string) bool { + for _, uri := range uris { + if strings.HasPrefix(uri, prefix) { + return true + } + } + return false +} + +func containsCustom(uris []string) bool { + for _, uri := range uris { + if !strings.HasPrefix(uri, http) && !strings.HasPrefix(uri, https) { + return true + } + } + return false +} + +func onlyLocalhostIsHttp(uris []string) bool { + for _, uri := range uris { + if strings.HasPrefix(uri, http) { + if !strings.HasPrefix(uri, httpLocalhost) && !strings.HasPrefix(uri, httpLocalhost2) { + return false + } + } + } + return true +} diff --git a/internal/v2/domain/oidc_config.go b/internal/v2/domain/oidc_config.go deleted file mode 100644 index 41673937cb..0000000000 --- a/internal/v2/domain/oidc_config.go +++ /dev/null @@ -1,152 +0,0 @@ -package domain - -import ( - "fmt" - "strings" - "time" - - "github.com/caos/logging" - - "github.com/caos/zitadel/internal/crypto" - "github.com/caos/zitadel/internal/errors" - "github.com/caos/zitadel/internal/eventstore/models" - "github.com/caos/zitadel/internal/id" -) - -type OIDCConfig struct { - models.ObjectRoot - - AppID string - ClientID string - ClientSecret *crypto.CryptoValue - ClientSecretString string - RedirectUris []string - ResponseTypes []OIDCResponseType - GrantTypes []OIDCGrantType - ApplicationType OIDCApplicationType - AuthMethodType OIDCAuthMethodType - PostLogoutRedirectUris []string - OIDCVersion OIDCVersion - Compliance *Compliance - DevMode bool - AccessTokenType OIDCTokenType - AccessTokenRoleAssertion bool - IDTokenRoleAssertion bool - IDTokenUserinfoAssertion bool - ClockSkew time.Duration -} - -type OIDCVersion int32 - -const ( - OIDCVersionV1 OIDCVersion = iota -) - -type OIDCResponseType int32 - -const ( - OIDCResponseTypeCode OIDCResponseType = iota - OIDCResponseTypeIDToken - OIDCResponseTypeIDTokenToken -) - -type OIDCGrantType int32 - -const ( - OIDCGrantTypeAuthorizationCode OIDCGrantType = iota - OIDCGrantTypeImplicit - OIDCGrantTypeRefreshToken -) - -type OIDCApplicationType int32 - -const ( - OIDCApplicationTypeWeb OIDCApplicationType = iota - OIDCApplicationTypeUserAgent - OIDCApplicationTypeNative -) - -type OIDCAuthMethodType int32 - -const ( - OIDCAuthMethodTypeBasic OIDCAuthMethodType = iota - OIDCAuthMethodTypePost - OIDCAuthMethodTypeNone -) - -type Compliance struct { - NoneCompliant bool - Problems []string -} - -type OIDCTokenType int32 - -const ( - OIDCTokenTypeBearer OIDCTokenType = iota - OIDCTokenTypeJWT -) - -func (c *OIDCConfig) IsValid() bool { - grantTypes := c.getRequiredGrantTypes() - for _, grantType := range grantTypes { - ok := containsOIDCGrantType(c.GrantTypes, grantType) - if !ok { - return false - } - } - return true -} - -//ClientID random_number@projectname (eg. 495894098234@zitadel) -func (c *OIDCConfig) GenerateNewClientID(idGenerator id.Generator, project *Project) error { - rndID, err := idGenerator.Next() - if err != nil { - return err - } - - c.ClientID = fmt.Sprintf("%v@%v", rndID, strings.ReplaceAll(strings.ToLower(project.Name), " ", "_")) - return nil -} - -func (c *OIDCConfig) GenerateClientSecretIfNeeded(generator crypto.Generator) (string, error) { - if c.AuthMethodType == OIDCAuthMethodTypeNone { - return "", nil - } - return c.GenerateNewClientSecret(generator) -} - -func (c *OIDCConfig) GenerateNewClientSecret(generator crypto.Generator) (string, error) { - cryptoValue, stringSecret, err := crypto.NewCode(generator) - if err != nil { - logging.Log("MODEL-UpnTI").OnError(err).Error("unable to create client secret") - return "", errors.ThrowInternal(err, "MODEL-gH2Wl", "Errors.Project.CouldNotGenerateClientSecret") - } - c.ClientSecret = cryptoValue - return stringSecret, nil -} - -func (c *OIDCConfig) getRequiredGrantTypes() []OIDCGrantType { - grantTypes := make([]OIDCGrantType, 0) - implicit := false - for _, r := range c.ResponseTypes { - switch r { - case OIDCResponseTypeCode: - grantTypes = append(grantTypes, OIDCGrantTypeAuthorizationCode) - case OIDCResponseTypeIDToken, OIDCResponseTypeIDTokenToken: - if !implicit { - implicit = true - grantTypes = append(grantTypes, OIDCGrantTypeImplicit) - } - } - } - return grantTypes -} - -func containsOIDCGrantType(grantTypes []OIDCGrantType, grantType OIDCGrantType) bool { - for _, gt := range grantTypes { - if gt == grantType { - return true - } - } - return false -} diff --git a/internal/v2/domain/project_grant.go b/internal/v2/domain/project_grant.go new file mode 100644 index 0000000000..a5c8cb6e6f --- /dev/null +++ b/internal/v2/domain/project_grant.go @@ -0,0 +1,30 @@ +package domain + +import es_models "github.com/caos/zitadel/internal/eventstore/models" + +type ProjectGrant struct { + es_models.ObjectRoot + + GrantID string + GrantedOrgID string + State ProjectGrantState + RoleKeys []string +} + +type ProjectGrantIDs struct { + ProjectID string + GrantID string +} + +type ProjectGrantState int32 + +const ( + ProjectGrantStateUnspecified ProjectGrantState = iota + ProjectGrantStateActive + ProjectGrantStateInactive + ProjectGrantStateRemoved +) + +func (p *ProjectGrant) IsValid() bool { + return p.GrantedOrgID != "" +} diff --git a/internal/v2/domain/project_grant_member.go b/internal/v2/domain/project_grant_member.go new file mode 100644 index 0000000000..b26e05e4ec --- /dev/null +++ b/internal/v2/domain/project_grant_member.go @@ -0,0 +1,28 @@ +package domain + +import ( + es_models "github.com/caos/zitadel/internal/eventstore/models" +) + +type ProjectGrantMember struct { + es_models.ObjectRoot + + GrantID string + UserID string + Roles []string +} + +func NewProjectGrantMember(aggregateID, userID, grantID string, roles ...string) *ProjectGrantMember { + return &ProjectGrantMember{ + ObjectRoot: es_models.ObjectRoot{ + AggregateID: aggregateID, + }, + GrantID: grantID, + UserID: userID, + Roles: roles, + } +} + +func (i *ProjectGrantMember) IsValid() bool { + return i.AggregateID != "" && i.GrantID != "" && i.UserID != "" && len(i.Roles) != 0 +} diff --git a/internal/v2/domain/project_role.go b/internal/v2/domain/project_role.go index afcb1fc798..b656be9dff 100644 --- a/internal/v2/domain/project_role.go +++ b/internal/v2/domain/project_role.go @@ -12,6 +12,14 @@ type ProjectRole struct { Group string } +type ProjectRoleState int32 + +const ( + ProjectRoleStateUnspecified ProjectRoleState = iota + ProjectRoleStateActive + ProjectRoleStateRemoved +) + func NewProjectRole(projectID, key string) *ProjectRole { return &ProjectRole{ObjectRoot: models.ObjectRoot{AggregateID: projectID}, Key: key} } diff --git a/internal/v2/repository/org/org.go b/internal/v2/repository/org/org.go index 5ea7b2224e..3ef4221cee 100644 --- a/internal/v2/repository/org/org.go +++ b/internal/v2/repository/org/org.go @@ -24,14 +24,14 @@ type OrgnameUniqueConstraint struct { action eventstore.UniqueConstraintAction } -func NewAddOrgnameUniqueConstraint(orgName string) *eventstore.EventUniqueConstraint { +func NewAddOrgNameUniqueConstraint(orgName string) *eventstore.EventUniqueConstraint { return eventstore.NewAddEventUniqueConstraint( uniqueOrgname, orgName, "Errors.Org.AlreadyExists") } -func NewRemoveUsernameUniqueConstraint(orgName string) *eventstore.EventUniqueConstraint { +func NewRemoveOrgNameUniqueConstraint(orgName string) *eventstore.EventUniqueConstraint { return eventstore.NewRemoveEventUniqueConstraint( uniqueOrgname, orgName) @@ -48,7 +48,7 @@ func (e *OrgAddedEvent) Data() interface{} { } func (e *OrgAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return []*eventstore.EventUniqueConstraint{NewAddOrgnameUniqueConstraint(e.Name)} + return []*eventstore.EventUniqueConstraint{NewAddOrgNameUniqueConstraint(e.Name)} } func NewOrgAddedEvent(ctx context.Context, name string) *OrgAddedEvent { @@ -174,3 +174,38 @@ func OrgReactivatedEventMapper(event *repository.Event) (eventstore.EventReader, return orgChanged, nil } + +type OrgRemovedEvent struct { + eventstore.BaseEvent `json:"-"` + name string +} + +func (e *OrgRemovedEvent) Data() interface{} { + return e +} + +func (e *OrgRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{NewRemoveOrgNameUniqueConstraint(e.name)} +} + +func NewOrgRemovedEvent(ctx context.Context, name string) *OrgRemovedEvent { + return &OrgRemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + OrgRemovedEventType, + ), + name: name, + } +} + +func OrgRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + orgChanged := &OrgRemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, orgChanged) + if err != nil { + return nil, errors.ThrowInternal(err, "ORG-DAfbs", "unable to unmarshal org deactivated") + } + + return orgChanged, nil +} diff --git a/internal/v2/repository/project/application.go b/internal/v2/repository/project/application.go index 553a864003..18b977ebd3 100644 --- a/internal/v2/repository/project/application.go +++ b/internal/v2/repository/project/application.go @@ -3,28 +3,42 @@ package project import ( "context" "encoding/json" + "fmt" "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/v2" "github.com/caos/zitadel/internal/eventstore/v2/repository" - "github.com/caos/zitadel/internal/v2/domain" ) const ( + uniqueAppNameType = "appname" applicationEventTypePrefix = projectEventTypePrefix + "application." - ApplicationAdded = applicationEventTypePrefix + "added" - ApplicationChanged = applicationEventTypePrefix + "changed" - ApplicationDeactivated = applicationEventTypePrefix + "deactivated" - ApplicationReactivated = applicationEventTypePrefix + "reactivated" - ApplicationRemoved = applicationEventTypePrefix + "removed" + ApplicationAddedType = applicationEventTypePrefix + "added" + ApplicationChangedType = applicationEventTypePrefix + "changed" + ApplicationDeactivatedType = applicationEventTypePrefix + "deactivated" + ApplicationReactivatedType = applicationEventTypePrefix + "reactivated" + ApplicationRemovedType = applicationEventTypePrefix + "removed" ) +func NewAddApplicationUniqueConstraint(name, projectID string) *eventstore.EventUniqueConstraint { + return eventstore.NewAddEventUniqueConstraint( + uniqueAppNameType, + fmt.Sprintf("%s:%s", name, projectID), + "Errors.Project.App.AlreadyExists") +} + +func NewRemoveApplicationUniqueConstraint(name, projectID string) *eventstore.EventUniqueConstraint { + return eventstore.NewRemoveEventUniqueConstraint( + uniqueAppNameType, + fmt.Sprintf("%s:%s", name, projectID)) +} + type ApplicationAddedEvent struct { eventstore.BaseEvent `json:"-"` - AppID string `json:"appId,omitempty"` - Name string `json:"name,omitempty"` - AppType domain.AppType `json:"appType,omitempty"` + AppID string `json:"appId,omitempty"` + Name string `json:"name,omitempty"` + projectID string } func (e *ApplicationAddedEvent) Data() interface{} { @@ -32,18 +46,18 @@ func (e *ApplicationAddedEvent) Data() interface{} { } func (e *ApplicationAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { - return nil + return []*eventstore.EventUniqueConstraint{NewAddApplicationUniqueConstraint(e.Name, e.projectID)} } -func NewApplicationAddedEvent(ctx context.Context, appID, name string, appType domain.AppType) *ApplicationAddedEvent { +func NewApplicationAddedEvent(ctx context.Context, appID, name, projectID string) *ApplicationAddedEvent { return &ApplicationAddedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( ctx, - ApplicationAdded, + ApplicationAddedType, ), - AppID: appID, - Name: name, - AppType: appType, + AppID: appID, + Name: name, + projectID: projectID, } } @@ -59,3 +73,164 @@ func ApplicationAddedEventMapper(event *repository.Event) (eventstore.EventReade return e, nil } + +type ApplicationChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + AppID string `json:"appId,omitempty"` + Name string `json:"name,omitempty"` + oldName string + projectID string +} + +func (e *ApplicationChangedEvent) Data() interface{} { + return e +} + +func (e *ApplicationChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{ + NewRemoveApplicationUniqueConstraint(e.oldName, e.projectID), + NewAddApplicationUniqueConstraint(e.Name, e.projectID), + } +} + +func NewApplicationChangedEvent(ctx context.Context, appID, oldName, newName, projectID string) *ApplicationChangedEvent { + return &ApplicationChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + ApplicationChangedType, + ), + AppID: appID, + Name: newName, + oldName: oldName, + projectID: projectID, + } +} + +func ApplicationChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &ApplicationChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "APPLICATION-9l0cs", "unable to unmarshal application") + } + + return e, nil +} + +type ApplicationDeactivatedEvent struct { + eventstore.BaseEvent `json:"-"` + + AppID string `json:"appId,omitempty"` +} + +func (e *ApplicationDeactivatedEvent) Data() interface{} { + return e +} + +func (e *ApplicationDeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewApplicationDeactivatedEvent(ctx context.Context, appID string) *ApplicationDeactivatedEvent { + return &ApplicationDeactivatedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + ApplicationDeactivatedType, + ), + AppID: appID, + } +} + +func ApplicationDeactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &ApplicationDeactivatedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "APPLICATION-0p9fB", "unable to unmarshal application") + } + + return e, nil +} + +type ApplicationReactivatedEvent struct { + eventstore.BaseEvent `json:"-"` + + AppID string `json:"appId,omitempty"` +} + +func (e *ApplicationReactivatedEvent) Data() interface{} { + return e +} + +func (e *ApplicationReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewApplicationReactivatedEvent(ctx context.Context, appID string) *ApplicationReactivatedEvent { + return &ApplicationReactivatedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + ApplicationReactivatedType, + ), + AppID: appID, + } +} + +func ApplicationReactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &ApplicationReactivatedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "APPLICATION-1m9e3", "unable to unmarshal application") + } + + return e, nil +} + +type ApplicationRemovedEvent struct { + eventstore.BaseEvent `json:"-"` + + AppID string `json:"appId,omitempty"` + name string + projectID string +} + +func (e *ApplicationRemovedEvent) Data() interface{} { + return e +} + +func (e *ApplicationRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{NewRemoveApplicationUniqueConstraint(e.name, e.projectID)} +} + +func NewApplicationRemovedEvent(ctx context.Context, appID, name, projectID string) *ApplicationRemovedEvent { + return &ApplicationRemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + ApplicationRemovedType, + ), + AppID: appID, + name: name, + projectID: projectID, + } +} + +func ApplicationRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &ApplicationRemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "APPLICATION-1m9e3", "unable to unmarshal application") + } + + return e, nil +} diff --git a/internal/v2/repository/project/eventstore.go b/internal/v2/repository/project/eventstore.go index 722788f47e..ee5c632365 100644 --- a/internal/v2/repository/project/eventstore.go +++ b/internal/v2/repository/project/eventstore.go @@ -5,5 +5,34 @@ import ( ) func RegisterEventMappers(es *eventstore.Eventstore) { - es.RegisterFilterEventMapper(ProjectAddedType, ProjectAddedEventMapper) + es.RegisterFilterEventMapper(ProjectAddedType, ProjectAddedEventMapper). + RegisterFilterEventMapper(ProjectChangedType, ProjectChangeEventMapper). + RegisterFilterEventMapper(ProjectDeactivatedType, ProjectDeactivatedEventMapper). + RegisterFilterEventMapper(ProjectReactivatedType, ProjectReactivatedEventMapper). + RegisterFilterEventMapper(ProjectRemovedType, ProjectRemovedEventMapper). + RegisterFilterEventMapper(MemberAddedType, MemberAddedEventMapper). + RegisterFilterEventMapper(MemberChangedType, MemberChangedEventMapper). + RegisterFilterEventMapper(MemberRemovedType, MemberRemovedEventMapper). + RegisterFilterEventMapper(RoleAddedType, RoleAddedEventMapper). + RegisterFilterEventMapper(RoleChangedType, RoleChangedEventMapper). + RegisterFilterEventMapper(RoleRemovedType, RoleRemovedEventMapper). + RegisterFilterEventMapper(GrantAddedType, GrantAddedEventMapper). + RegisterFilterEventMapper(GrantChangedType, GrantChangedEventMapper). + RegisterFilterEventMapper(GrantCascadeChangedType, GrantChangedEventMapper). + RegisterFilterEventMapper(GrantDeactivatedType, GrantDeactivateEventMapper). + RegisterFilterEventMapper(GrantReactivatedType, GrantReactivatedEventMapper). + RegisterFilterEventMapper(GrantRemovedType, GrantRemovedEventMapper). + RegisterFilterEventMapper(GrantMemberAddedType, GrantMemberAddedEventMapper). + RegisterFilterEventMapper(GrantMemberChangedType, GrantMemberChangedEventMapper). + RegisterFilterEventMapper(GrantMemberRemovedType, GrantMemberRemovedEventMapper). + RegisterFilterEventMapper(ApplicationAddedType, ApplicationAddedEventMapper). + RegisterFilterEventMapper(ApplicationChangedType, ApplicationAddedEventMapper). + RegisterFilterEventMapper(ApplicationRemovedType, ApplicationRemovedEventMapper). + RegisterFilterEventMapper(ApplicationDeactivatedType, ApplicationDeactivatedEventMapper). + RegisterFilterEventMapper(ApplicationReactivatedType, ApplicationReactivatedEventMapper). + RegisterFilterEventMapper(OIDCConfigAddedType, OIDCConfigAddedEventMapper). + RegisterFilterEventMapper(OIDCConfigChangedType, OIDCConfigChangedEventMapper). + RegisterFilterEventMapper(OIDCConfigSecretChangedType, OIDCConfigSecretChangedEventMapper). + RegisterFilterEventMapper(OIDCClientSecretCheckSucceededType, OIDCConfigSecretCheckSucceededEventMapper). + RegisterFilterEventMapper(OIDCClientSecretCheckFailedType, OIDCConfigSecretCheckFailedEventMapper) } diff --git a/internal/v2/repository/project/grant.go b/internal/v2/repository/project/grant.go new file mode 100644 index 0000000000..45d3dc9021 --- /dev/null +++ b/internal/v2/repository/project/grant.go @@ -0,0 +1,270 @@ +package project + +import ( + "context" + "encoding/json" + "fmt" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" +) + +var ( + uniqueGrantType = "project_Grant" + grantEventTypePrefix = projectEventTypePrefix + "grant." + GrantAddedType = grantEventTypePrefix + "added" + GrantChangedType = grantEventTypePrefix + "changed" + GrantCascadeChangedType = grantEventTypePrefix + "cascade.changed" + GrantDeactivatedType = grantEventTypePrefix + "deactivated" + GrantReactivatedType = grantEventTypePrefix + "reactivated" + GrantRemovedType = grantEventTypePrefix + "removed" +) + +func NewAddProjectGrantUniqueConstraint(grantedOrgID, projectID string) *eventstore.EventUniqueConstraint { + return eventstore.NewAddEventUniqueConstraint( + uniqueRoleType, + fmt.Sprintf("%s:%s", grantedOrgID, projectID), + "Errors.Project.Grant.AlreadyExists") +} + +func NewRemoveProjectGrantUniqueConstraint(grantedOrgID, projectID string) *eventstore.EventUniqueConstraint { + return eventstore.NewRemoveEventUniqueConstraint( + uniqueRoleType, + fmt.Sprintf("%s:%s", grantedOrgID, projectID)) +} + +type GrantAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + GrantID string `json:"grantId,omitempty"` + GrantedOrgID string `json:"grantedOrgId,omitempty"` + RoleKeys []string `json:"roleKeys,omitempty"` + projectID string +} + +func (e *GrantAddedEvent) Data() interface{} { + return e +} + +func (e *GrantAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{NewAddProjectGrantUniqueConstraint(e.GrantedOrgID, e.projectID)} +} + +func NewGrantAddedEvent(ctx context.Context, grantID, grantedOrgID, projectID string, roleKeys []string) *GrantAddedEvent { + return &GrantAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + GrantAddedType, + ), + GrantID: grantID, + GrantedOrgID: grantedOrgID, + RoleKeys: roleKeys, + projectID: projectID, + } +} + +func GrantAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &GrantAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-mL0vs", "unable to unmarshal project grant") + } + + return e, nil +} + +type GrantChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + GrantID string `json:"grantId,omitempty"` + RoleKeys []string `json:"roleKeys,omitempty"` +} + +func (e *GrantChangedEvent) Data() interface{} { + return e +} + +func (e *GrantChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewGrantChangedEvent(ctx context.Context, grantID string, roleKeys []string) *GrantChangedEvent { + return &GrantChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + GrantChangedType, + ), + GrantID: grantID, + RoleKeys: roleKeys, + } +} + +func GrantChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &GrantChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-mL0vs", "unable to unmarshal project grant") + } + + return e, nil +} + +type GrantCascadeChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + GrantID string `json:"grantId,omitempty"` + RoleKeys []string `json:"roleKeys,omitempty"` +} + +func (e *GrantCascadeChangedEvent) Data() interface{} { + return e +} + +func (e *GrantCascadeChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewGrantCascadeChangedEvent(ctx context.Context, grantID string, roleKeys []string) *GrantCascadeChangedEvent { + return &GrantCascadeChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + GrantCascadeChangedType, + ), + GrantID: grantID, + RoleKeys: roleKeys, + } +} + +func GrantCascadeChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &GrantCascadeChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-9o0se", "unable to unmarshal project grant") + } + + return e, nil +} + +type GrantDeactivateEvent struct { + eventstore.BaseEvent `json:"-"` + + GrantID string `json:"grantId,omitempty"` +} + +func (e *GrantDeactivateEvent) Data() interface{} { + return e +} + +func (e *GrantDeactivateEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewGrantDeactivateEvent(ctx context.Context, grantID string) *GrantDeactivateEvent { + return &GrantDeactivateEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + GrantDeactivatedType, + ), + GrantID: grantID, + } +} + +func GrantDeactivateEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &GrantDeactivateEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-9o0se", "unable to unmarshal project grant") + } + + return e, nil +} + +type GrantReactivatedEvent struct { + eventstore.BaseEvent `json:"-"` + + GrantID string `json:"grantId,omitempty"` +} + +func (e *GrantReactivatedEvent) Data() interface{} { + return e +} + +func (e *GrantReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewGrantReactivatedEvent(ctx context.Context, grantID string) *GrantReactivatedEvent { + return &GrantReactivatedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + GrantReactivatedType, + ), + GrantID: grantID, + } +} + +func GrantReactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &GrantReactivatedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-78f7D", "unable to unmarshal project grant") + } + + return e, nil +} + +type GrantRemovedEvent struct { + eventstore.BaseEvent `json:"-"` + + GrantID string `json:"grantId,omitempty"` + grantedOrgID string + projectID string +} + +func (e *GrantRemovedEvent) Data() interface{} { + return e +} + +func (e *GrantRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{NewRemoveProjectGrantUniqueConstraint(e.grantedOrgID, e.projectID)} +} + +func NewGrantRemovedEvent(ctx context.Context, grantID, grantedOrgID, projectID string) *GrantRemovedEvent { + return &GrantRemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + GrantRemovedType, + ), + GrantID: grantID, + projectID: projectID, + grantedOrgID: grantedOrgID, + } +} + +func GrantRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &GrantRemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-28jM8", "unable to unmarshal project grant") + } + + return e, nil +} diff --git a/internal/v2/repository/project/grant_member.go b/internal/v2/repository/project/grant_member.go new file mode 100644 index 0000000000..99a65297e8 --- /dev/null +++ b/internal/v2/repository/project/grant_member.go @@ -0,0 +1,173 @@ +package project + +import ( + "context" + "encoding/json" + "fmt" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" + "github.com/caos/zitadel/internal/v2/repository/member" +) + +var ( + uniqueProjectGrantType = "project_grant" + GrantMemberAddedType = grantEventTypePrefix + member.AddedEventType + GrantMemberChangedType = grantEventTypePrefix + member.ChangedEventType + GrantMemberRemovedType = grantEventTypePrefix + member.RemovedEventType +) + +func NewAddProjectGrantMemberUniqueConstraint(projectID, userID, grantID string) *eventstore.EventUniqueConstraint { + return eventstore.NewAddEventUniqueConstraint( + uniqueProjectGrantType, + fmt.Sprintf("%s:%s:%s", projectID, userID, grantID), + "Errors.Project.Member.AlreadyExists") +} + +func NewRemoveProjectGrantMemberUniqueConstraint(projectID, userID, grantID string) *eventstore.EventUniqueConstraint { + return eventstore.NewRemoveEventUniqueConstraint( + uniqueProjectGrantType, + fmt.Sprintf("%s:%s:%s", projectID, userID, grantID), + ) +} + +type GrantMemberAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + Roles []string `json:"roles"` + UserID string `json:"userId"` + GrantID string `json:"grantId"` + projectID string +} + +func (e *GrantMemberAddedEvent) Data() interface{} { + return e +} + +func (e *GrantMemberAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{NewAddProjectGrantMemberUniqueConstraint(e.projectID, e.UserID, e.GrantID)} +} + +func NewProjectGrantMemberAddedEvent( + ctx context.Context, + projectID, + userID, + grantID string, + roles ...string, +) *GrantMemberAddedEvent { + return &GrantMemberAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + GrantMemberAddedType, + ), + projectID: projectID, + UserID: userID, + GrantID: grantID, + Roles: roles, + } +} + +func GrantMemberAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &GrantMemberAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-9f0sf", "unable to unmarshal label policy") + } + + return e, nil +} + +type GrantMemberChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + Roles []string `json:"roles"` + GrantID string `json:"grantId"` + UserID string `json:"userId"` +} + +func (e *GrantMemberChangedEvent) Data() interface{} { + return e +} + +func (e *GrantMemberChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewProjectGrantMemberChangedEvent( + ctx context.Context, + userID, + grantID string, + roles ...string, +) *GrantMemberChangedEvent { + return &GrantMemberChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + GrantMemberAddedType, + ), + UserID: userID, + GrantID: grantID, + Roles: roles, + } +} + +func GrantMemberChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &GrantMemberChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-39fi8", "unable to unmarshal label policy") + } + + return e, nil +} + +type GrantMemberRemovedEvent struct { + eventstore.BaseEvent `json:"-"` + + UserID string `json:"userId"` + GrantID string `json:"grantId"` + projectID string +} + +func (e *GrantMemberRemovedEvent) Data() interface{} { + return e +} + +func (e *GrantMemberRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{NewRemoveProjectGrantMemberUniqueConstraint(e.projectID, e.UserID, e.GrantID)} +} + +func NewProjectGrantMemberRemovedEvent( + ctx context.Context, + projectID, + userID, + grantID string, +) *GrantMemberRemovedEvent { + return &GrantMemberRemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + GrantMemberRemovedType, + ), + UserID: userID, + GrantID: grantID, + projectID: projectID, + } +} + +func GrantMemberRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &GrantMemberRemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-173fM", "unable to unmarshal label policy") + } + + return e, nil +} diff --git a/internal/v2/repository/project/member.go b/internal/v2/repository/project/member.go index ef63ea58b8..447e8a1e91 100644 --- a/internal/v2/repository/project/member.go +++ b/internal/v2/repository/project/member.go @@ -3,20 +3,21 @@ package project import ( "context" "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" "github.com/caos/zitadel/internal/v2/repository/member" ) var ( - MemberAddedEventType = projectEventTypePrefix + member.AddedEventType - MemberChangedEventType = projectEventTypePrefix + member.ChangedEventType - MemberRemovedEventType = projectEventTypePrefix + member.RemovedEventType + MemberAddedType = projectEventTypePrefix + member.AddedEventType + MemberChangedType = projectEventTypePrefix + member.ChangedEventType + MemberRemovedType = projectEventTypePrefix + member.RemovedEventType ) type MemberAddedEvent struct { member.MemberAddedEvent } -func NewMemberAddedEvent( +func NewProjectMemberAddedEvent( ctx context.Context, userID string, roles ...string, @@ -25,7 +26,7 @@ func NewMemberAddedEvent( MemberAddedEvent: *member.NewMemberAddedEvent( eventstore.NewBaseEventForPush( ctx, - MemberAddedEventType, + MemberAddedType, ), userID, roles..., @@ -33,11 +34,20 @@ func NewMemberAddedEvent( } } +func MemberAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e, err := member.MemberAddedEventMapper(event) + if err != nil { + return nil, err + } + + return &MemberAddedEvent{MemberAddedEvent: *e.(*member.MemberAddedEvent)}, nil +} + type MemberChangedEvent struct { member.MemberChangedEvent } -func NewMemberChangedEvent( +func NewProjectMemberChangedEvent( ctx context.Context, userID string, roles ...string, @@ -47,7 +57,7 @@ func NewMemberChangedEvent( MemberChangedEvent: *member.NewMemberChangedEvent( eventstore.NewBaseEventForPush( ctx, - MemberChangedEventType, + MemberChangedType, ), userID, roles..., @@ -55,11 +65,20 @@ func NewMemberChangedEvent( } } +func MemberChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e, err := member.ChangedEventMapper(event) + if err != nil { + return nil, err + } + + return &MemberChangedEvent{MemberChangedEvent: *e.(*member.MemberChangedEvent)}, nil +} + type MemberRemovedEvent struct { member.MemberRemovedEvent } -func NewMemberRemovedEvent( +func NewProjectMemberRemovedEvent( ctx context.Context, userID string, ) *MemberRemovedEvent { @@ -68,9 +87,18 @@ func NewMemberRemovedEvent( MemberRemovedEvent: *member.NewRemovedEvent( eventstore.NewBaseEventForPush( ctx, - MemberRemovedEventType, + MemberRemovedType, ), userID, ), } } + +func MemberRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e, err := member.RemovedEventMapper(event) + if err != nil { + return nil, err + } + + return &MemberRemovedEvent{MemberRemovedEvent: *e.(*member.MemberRemovedEvent)}, nil +} diff --git a/internal/v2/repository/project/oidc_config.go b/internal/v2/repository/project/oidc_config.go index 1b76a0bb25..8591b00249 100644 --- a/internal/v2/repository/project/oidc_config.go +++ b/internal/v2/repository/project/oidc_config.go @@ -13,11 +13,11 @@ import ( ) const ( - OIDCConfigAdded = applicationEventTypePrefix + "config.oidc.added" - OIDCConfigChanged = applicationEventTypePrefix + "config.oidc.changed" - OIDCConfigSecretChanged = applicationEventTypePrefix + "config.oidc.secret.changed" - OIDCClientSecretCheckSucceeded = applicationEventTypePrefix + "oidc.secret.check.succeeded" - OIDCClientSecretCheckFailed = applicationEventTypePrefix + "oidc.secret.check.failed" + OIDCConfigAddedType = applicationEventTypePrefix + "config.oidc.added" + OIDCConfigChangedType = applicationEventTypePrefix + "config.oidc.changed" + OIDCConfigSecretChangedType = applicationEventTypePrefix + "config.oidc.secret.changed" + OIDCClientSecretCheckSucceededType = applicationEventTypePrefix + "oidc.secret.check.succeeded" + OIDCClientSecretCheckFailedType = applicationEventTypePrefix + "oidc.secret.check.failed" ) type OIDCConfigAddedEvent struct { @@ -71,7 +71,7 @@ func NewOIDCConfigAddedEvent( return &OIDCConfigAddedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( ctx, - OIDCConfigAdded, + OIDCConfigAddedType, ), Version: version, AppID: appID, @@ -104,3 +104,276 @@ func OIDCConfigAddedEventMapper(event *repository.Event) (eventstore.EventReader return e, nil } + +type OIDCConfigChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + Version *domain.OIDCVersion `json:"oidcVersion,omitempty"` + AppID string `json:"appId"` + ClientID *string `json:"clientId,omitempty"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` + RedirectUris *[]string `json:"redirectUris,omitempty"` + ResponseTypes *[]domain.OIDCResponseType `json:"responseTypes,omitempty"` + GrantTypes *[]domain.OIDCGrantType `json:"grantTypes,omitempty"` + ApplicationType *domain.OIDCApplicationType `json:"applicationType,omitempty"` + AuthMethodType *domain.OIDCAuthMethodType `json:"authMethodType,omitempty"` + PostLogoutRedirectUris *[]string `json:"postLogoutRedirectUris,omitempty"` + DevMode *bool `json:"devMode,omitempty"` + AccessTokenType *domain.OIDCTokenType `json:"accessTokenType,omitempty"` + AccessTokenRoleAssertion *bool `json:"accessTokenRoleAssertion,omitempty"` + IDTokenRoleAssertion *bool `json:"idTokenRoleAssertion,omitempty"` + IDTokenUserinfoAssertion *bool `json:"idTokenUserinfoAssertion,omitempty"` + ClockSkew *time.Duration `json:"clockSkew,omitempty"` +} + +func (e *OIDCConfigChangedEvent) Data() interface{} { + return e +} + +func (e *OIDCConfigChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewOIDCConfigChangedEvent( + ctx context.Context, + appID string, + changes []OIDCConfigChanges, +) (*OIDCConfigChangedEvent, error) { + if len(changes) == 0 { + return nil, errors.ThrowPreconditionFailed(nil, "OIDC-i8idç", "Errors.NoChangesFound") + } + + changeEvent := &OIDCConfigChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + OIDCConfigChangedType, + ), + AppID: appID, + } + for _, change := range changes { + change(changeEvent) + } + return changeEvent, nil +} + +type OIDCConfigChanges func(event *OIDCConfigChangedEvent) + +func ChangeVersion(version domain.OIDCVersion) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.Version = &version + } +} + +func ChangeClientID(clientID string) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.ClientID = &clientID + } +} + +func ChangeRedirectURIs(uris []string) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.RedirectUris = &uris + } +} + +func ChangeResponseTypes(responseTypes []domain.OIDCResponseType) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.ResponseTypes = &responseTypes + } +} + +func ChangeGrantTypes(grantTypes []domain.OIDCGrantType) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.GrantTypes = &grantTypes + } +} + +func ChangeApplicationType(appType domain.OIDCApplicationType) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.ApplicationType = &appType + } +} + +func ChangeAuthMethodType(authMethodType domain.OIDCAuthMethodType) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.AuthMethodType = &authMethodType + } +} + +func ChangePostLogoutRedirectURIs(logoutRedirects []string) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.PostLogoutRedirectUris = &logoutRedirects + } +} + +func ChangeDevMode(devMode bool) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.DevMode = &devMode + } +} + +func ChangeAccessTokenType(accessTokenType domain.OIDCTokenType) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.AccessTokenType = &accessTokenType + } +} + +func ChangeAccessTokenRoleAssertion(accessTokenRoleAssertion bool) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.AccessTokenRoleAssertion = &accessTokenRoleAssertion + } +} + +func ChangeIDTokenRoleAssertion(idTokenRoleAssertion bool) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.IDTokenRoleAssertion = &idTokenRoleAssertion + } +} + +func ChangeIDTokenUserinfoAssertion(idTokenUserinfoAssertion bool) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.IDTokenUserinfoAssertion = &idTokenUserinfoAssertion + } +} + +func ChangeClockSkew(clockSkew time.Duration) func(event *OIDCConfigChangedEvent) { + return func(e *OIDCConfigChangedEvent) { + e.ClockSkew = &clockSkew + } +} + +func OIDCConfigChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &OIDCConfigChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "OIDC-BFd15", "unable to unmarshal oidc config") + } + + return e, nil +} + +type OIDCConfigSecretChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + AppID string `json:"appId"` + ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"` +} + +func (e *OIDCConfigSecretChangedEvent) Data() interface{} { + return e +} + +func (e *OIDCConfigSecretChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewOIDCConfigSecretChangedEvent( + ctx context.Context, + appID string, + clientSecret *crypto.CryptoValue, +) *OIDCConfigSecretChangedEvent { + return &OIDCConfigSecretChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + OIDCConfigSecretChangedType, + ), + AppID: appID, + ClientSecret: clientSecret, + } +} + +func OIDCConfigSecretChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &OIDCConfigSecretChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "OIDC-M893d", "unable to unmarshal oidc config") + } + + return e, nil +} + +type OIDCConfigSecretCheckSucceededEvent struct { + eventstore.BaseEvent `json:"-"` + + AppID string `json:"appId"` +} + +func (e *OIDCConfigSecretCheckSucceededEvent) Data() interface{} { + return e +} + +func (e *OIDCConfigSecretCheckSucceededEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewOIDCConfigSecretCheckSucceededEvent( + ctx context.Context, + appID string, +) *OIDCConfigSecretCheckSucceededEvent { + return &OIDCConfigSecretCheckSucceededEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + OIDCClientSecretCheckSucceededType, + ), + AppID: appID, + } +} + +func OIDCConfigSecretCheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &OIDCConfigSecretCheckSucceededEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "OIDC-837gV", "unable to unmarshal oidc config") + } + + return e, nil +} + +type OIDCConfigSecretCheckFailedEvent struct { + eventstore.BaseEvent `json:"-"` + + AppID string `json:"appId"` +} + +func (e *OIDCConfigSecretCheckFailedEvent) Data() interface{} { + return e +} + +func (e *OIDCConfigSecretCheckFailedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewOIDCConfigSecretCheckFailedEvent( + ctx context.Context, + appID string, +) *OIDCConfigSecretCheckFailedEvent { + return &OIDCConfigSecretCheckFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + OIDCClientSecretCheckFailedType, + ), + AppID: appID, + } +} + +func OIDCConfigSecretCheckFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &OIDCConfigSecretCheckFailedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "OIDC-987g%", "unable to unmarshal oidc config") + } + + return e, nil +} diff --git a/internal/v2/repository/project/project.go b/internal/v2/repository/project/project.go index 2bf1fb6aa5..5f20563934 100644 --- a/internal/v2/repository/project/project.go +++ b/internal/v2/repository/project/project.go @@ -10,25 +10,25 @@ import ( ) const ( - uniqueProjectnameTable = "project_names" + uniqueProjectnameType = "project_names" projectEventTypePrefix = eventstore.EventType("project.") ProjectAddedType = projectEventTypePrefix + "added" - ProjectChanged = projectEventTypePrefix + "changed" - ProjectDeactivated = projectEventTypePrefix + "deactivated" - ProjectReactivated = projectEventTypePrefix + "reactivated" - ProjectRemoved = projectEventTypePrefix + "removed" + ProjectChangedType = projectEventTypePrefix + "changed" + ProjectDeactivatedType = projectEventTypePrefix + "deactivated" + ProjectReactivatedType = projectEventTypePrefix + "reactivated" + ProjectRemovedType = projectEventTypePrefix + "removed" ) func NewAddProjectNameUniqueConstraint(projectName, resourceOwner string) *eventstore.EventUniqueConstraint { return eventstore.NewAddEventUniqueConstraint( - uniqueProjectnameTable, + uniqueProjectnameType, projectName+resourceOwner, "Errors.Project.AlreadyExists") } func NewRemoveProjectNameUniqueConstraint(projectName, resourceOwner string) *eventstore.EventUniqueConstraint { return eventstore.NewRemoveEventUniqueConstraint( - uniqueProjectnameTable, + uniqueProjectnameType, projectName+resourceOwner) } @@ -71,3 +71,155 @@ func ProjectAddedEventMapper(event *repository.Event) (eventstore.EventReader, e return e, nil } + +type ProjectChangeEvent struct { + eventstore.BaseEvent `json:"-"` + + Name *string `json:"name,omitempty"` + ProjectRoleAssertion *bool `json:"projectRoleAssertion,omitempty"` + ProjectRoleCheck *bool `json:"projectRoleCheck,omitempty"` +} + +func (e *ProjectChangeEvent) Data() interface{} { + return e +} + +func (e *ProjectChangeEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewProjectChangeEvent( + ctx context.Context, + changes []ProjectChanges) (*ProjectChangeEvent, error) { + if len(changes) == 0 { + return nil, errors.ThrowPreconditionFailed(nil, "PROJECT-mV9xc", "Errors.NoChangesFound") + } + changeEvent := &ProjectChangeEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + ProjectChangedType, + ), + } + for _, change := range changes { + change(changeEvent) + } + return changeEvent, nil +} + +type ProjectChanges func(event *ProjectChangeEvent) + +func ChangeName(name string) func(event *ProjectChangeEvent) { + return func(e *ProjectChangeEvent) { + e.Name = &name + } +} + +func ChangeProjectRoleAssertion(projectRoleAssertion bool) func(event *ProjectChangeEvent) { + return func(e *ProjectChangeEvent) { + e.ProjectRoleAssertion = &projectRoleAssertion + } +} + +func ChangeProjectRoleCheck(projectRoleCheck bool) func(event *ProjectChangeEvent) { + return func(e *ProjectChangeEvent) { + e.ProjectRoleCheck = &projectRoleCheck + } +} + +func ProjectChangeEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &ProjectChangeEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-M9osd", "unable to unmarshal project") + } + + return e, nil +} + +type ProjectDeactivatedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *ProjectDeactivatedEvent) Data() interface{} { + return nil +} + +func (e *ProjectDeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewProjectDeactivatedEvent(ctx context.Context) *ProjectDeactivatedEvent { + return &ProjectDeactivatedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + ProjectDeactivatedType, + ), + } +} + +func ProjectDeactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &ProjectDeactivatedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type ProjectReactivatedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *ProjectReactivatedEvent) Data() interface{} { + return nil +} + +func (e *ProjectReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewProjectReactivatedEvent(ctx context.Context) *ProjectReactivatedEvent { + return &ProjectReactivatedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + ProjectReactivatedType, + ), + } +} + +func ProjectReactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &ProjectReactivatedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type ProjectRemovedEvent struct { + eventstore.BaseEvent `json:"-"` + + Name string +} + +func (e *ProjectRemovedEvent) Data() interface{} { + return nil +} + +func (e *ProjectRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{NewRemoveProjectNameUniqueConstraint(e.Name, e.ResourceOwner())} +} + +func NewProjectRemovedEvent(ctx context.Context, name, resourceOwner string) *ProjectRemovedEvent { + return &ProjectRemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPushWithResourceOwner( + ctx, + ProjectRemovedType, + resourceOwner, + ), + Name: name, + } +} + +func ProjectRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &ProjectRemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} diff --git a/internal/v2/repository/project/role.go b/internal/v2/repository/project/role.go new file mode 100644 index 0000000000..7f78bae90a --- /dev/null +++ b/internal/v2/repository/project/role.go @@ -0,0 +1,181 @@ +package project + +import ( + "context" + "encoding/json" + "fmt" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" +) + +var ( + uniqueRoleType = "project_role" + roleEventTypePrefix = projectEventTypePrefix + "role." + RoleAddedType = roleEventTypePrefix + "added" + RoleChangedType = roleEventTypePrefix + "changed" + RoleRemovedType = roleEventTypePrefix + "removed" +) + +func NewAddProjectRoleUniqueConstraint(roleKey, projectID, resourceOwner string) *eventstore.EventUniqueConstraint { + return eventstore.NewAddEventUniqueConstraint( + uniqueRoleType, + fmt.Sprintf("%s:%s:%s", roleKey, projectID, resourceOwner), + "Errors.Project.Role.AlreadyExists") +} + +func NewRemoveProjectRoleUniqueConstraint(roleKey, projectID, resourceOwner string) *eventstore.EventUniqueConstraint { + return eventstore.NewRemoveEventUniqueConstraint( + uniqueRoleType, + fmt.Sprintf("%s:%s:%s", roleKey, projectID, resourceOwner)) +} + +type RoleAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + Key string `json:"key,omitempty"` + DisplayName string `json:"displayName,omitempty"` + Group string `json:"group,omitempty"` + projectID string +} + +func (e *RoleAddedEvent) Data() interface{} { + return e +} + +func (e *RoleAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{NewAddProjectRoleUniqueConstraint(e.Key, e.projectID, e.ResourceOwner())} +} + +func NewRoleAddedEvent(ctx context.Context, key, displayName, group, projectID, resourceOwner string) *RoleAddedEvent { + return &RoleAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPushWithResourceOwner( + ctx, + RoleAddedType, + resourceOwner, + ), + Key: key, + DisplayName: displayName, + Group: group, + projectID: projectID, + } +} + +func RoleAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &RoleAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-2M0xy", "unable to unmarshal project role") + } + + return e, nil +} + +type RoleChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + Key string `json:"key,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + Group *string `json:"group,omitempty"` +} + +func (e *RoleChangedEvent) Data() interface{} { + return e +} + +func (e *RoleChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return nil +} + +func NewRoleChangedEvent( + ctx context.Context, + changes []RoleChanges) (*RoleChangedEvent, error) { + if len(changes) == 0 { + return nil, errors.ThrowPreconditionFailed(nil, "PROJECT-eR9vx", "Errors.NoChangesFound") + } + changeEvent := &RoleChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + RoleChangedType, + ), + } + for _, change := range changes { + change(changeEvent) + } + return changeEvent, nil +} + +type RoleChanges func(event *RoleChangedEvent) + +func ChangeKey(key string) func(event *RoleChangedEvent) { + return func(e *RoleChangedEvent) { + e.Key = key + } +} + +func ChangeDisplayName(displayName string) func(event *RoleChangedEvent) { + return func(e *RoleChangedEvent) { + e.DisplayName = &displayName + } +} + +func ChangeGroup(group string) func(event *RoleChangedEvent) { + return func(e *RoleChangedEvent) { + e.Group = &group + } +} +func RoleChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &RoleChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-3M0vx", "unable to unmarshal project role") + } + + return e, nil +} + +type RoleRemovedEvent struct { + eventstore.BaseEvent `json:"-"` + + Key string `json:"key,omitempty"` + projectID string +} + +func (e *RoleRemovedEvent) Data() interface{} { + return e +} + +func (e *RoleRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{NewRemoveProjectRoleUniqueConstraint(e.Key, e.projectID, e.ResourceOwner())} +} + +func NewRoleRemovedEvent(ctx context.Context, key, projectID, resourceOwner string) *RoleRemovedEvent { + return &RoleRemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPushWithResourceOwner( + ctx, + RoleRemovedType, + resourceOwner, + ), + Key: key, + projectID: projectID, + } +} + +func RoleRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &RoleRemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-1M0xs", "unable to unmarshal project role") + } + + return e, nil +} diff --git a/internal/v2/repository/usergrant/user_grant.go b/internal/v2/repository/usergrant/user_grant.go index ea41e98a67..f799c05060 100644 --- a/internal/v2/repository/usergrant/user_grant.go +++ b/internal/v2/repository/usergrant/user_grant.go @@ -167,7 +167,7 @@ type UserGrantRemovedEvent struct { } func (e *UserGrantRemovedEvent) Data() interface{} { - return e + return nil } func (e *UserGrantRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { @@ -187,16 +187,9 @@ func NewUserGrantRemovedEvent(ctx context.Context, resourceOwner, userID, projec } func UserGrantRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { - e := &UserGrantRemovedEvent{ + return &UserGrantRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), - } - - err := json.Unmarshal(event.Data, e) - if err != nil { - return nil, errors.ThrowInternal(err, "UGRANT-M0sdf", "unable to unmarshal user grant") - } - - return e, nil + }, nil } type UserGrantCascadeRemovedEvent struct { @@ -206,18 +199,18 @@ type UserGrantCascadeRemovedEvent struct { } func (e *UserGrantCascadeRemovedEvent) Data() interface{} { - return e + return nil } func (e *UserGrantCascadeRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { return []*eventstore.EventUniqueConstraint{NewRemoveUserGrantUniqueConstraint(e.ResourceOwner(), e.userID, e.projectID)} } -func NewUserGrantCascadeRemovedEvent(ctx context.Context, resourceOwner, userID, projectID string) *UserGrantRemovedEvent { - return &UserGrantRemovedEvent{ +func NewUserGrantCascadeRemovedEvent(ctx context.Context, resourceOwner, userID, projectID string) *UserGrantCascadeRemovedEvent { + return &UserGrantCascadeRemovedEvent{ BaseEvent: *eventstore.NewBaseEventForPushWithResourceOwner( ctx, - UserGrantRemovedType, + UserGrantCascadeRemovedType, resourceOwner, ), userID: userID, @@ -226,16 +219,9 @@ func NewUserGrantCascadeRemovedEvent(ctx context.Context, resourceOwner, userID, } func UserGrantCascadeRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { - e := &UserGrantRemovedEvent{ + return &UserGrantCascadeRemovedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), - } - - err := json.Unmarshal(event.Data, e) - if err != nil { - return nil, errors.ThrowInternal(err, "UGRANT-E7urs", "unable to unmarshal user grant") - } - - return e, nil + }, nil } type UserGrantDeactivatedEvent struct { @@ -243,7 +229,7 @@ type UserGrantDeactivatedEvent struct { } func (e *UserGrantDeactivatedEvent) Data() interface{} { - return e + return nil } func (e *UserGrantDeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { @@ -260,16 +246,9 @@ func NewUserGrantDeactivatedEvent(ctx context.Context) *UserGrantDeactivatedEven } func UserGrantDeactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) { - e := &UserGrantDeactivatedEvent{ + return &UserGrantDeactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), - } - - err := json.Unmarshal(event.Data, e) - if err != nil { - return nil, errors.ThrowInternal(err, "UGRANT-pL0ds", "unable to unmarshal user grant") - } - - return e, nil + }, nil } type UserGrantReactivatedEvent struct { @@ -277,7 +256,7 @@ type UserGrantReactivatedEvent struct { } func (e *UserGrantReactivatedEvent) Data() interface{} { - return e + return nil } func (e *UserGrantReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { @@ -294,14 +273,7 @@ func NewUserGrantReactivatedEvent(ctx context.Context) *UserGrantReactivatedEven } func UserGrantReactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) { - e := &UserGrantReactivatedEvent{ + return &UserGrantReactivatedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), - } - - err := json.Unmarshal(event.Data, e) - if err != nil { - return nil, errors.ThrowInternal(err, "UGRANT-M0sdf", "unable to unmarshal user grant") - } - - return e, nil + }, nil } diff --git a/pkg/grpc/management/proto/management.proto b/pkg/grpc/management/proto/management.proto index da35048008..db443cc001 100644 --- a/pkg/grpc/management/proto/management.proto +++ b/pkg/grpc/management/proto/management.proto @@ -723,7 +723,7 @@ service ManagementService { }; } - rpc DeactivateProject(ProjectID) returns (Project) { + rpc DeactivateProject(ProjectID) returns (google.protobuf.Empty) { option (google.api.http) = { put: "/projects/{id}/_deactivate" body: "*" @@ -735,7 +735,7 @@ service ManagementService { }; } - rpc ReactivateProject(ProjectID) returns (Project) { + rpc ReactivateProject(ProjectID) returns (google.protobuf.Empty) { option (google.api.http) = { put: "/projects/{id}/_reactivate" body: "*" @@ -957,7 +957,7 @@ service ManagementService { }; } - rpc DeactivateApplication(ApplicationID) returns (Application) { + rpc DeactivateApplication(ApplicationID) returns (google.protobuf.Empty) { option (google.api.http) = { put: "/projects/{project_id}/applications/{id}/_deactivate" body: "*" @@ -969,7 +969,7 @@ service ManagementService { }; } - rpc ReactivateApplication(ApplicationID) returns (Application) { + rpc ReactivateApplication(ApplicationID) returns (google.protobuf.Empty) { option (google.api.http) = { put: "/projects/{project_id}/applications/{id}/_reactivate" body: "*" @@ -1060,7 +1060,7 @@ service ManagementService { }; } - rpc DeactivateProjectGrant(ProjectGrantID) returns (ProjectGrant) { + rpc DeactivateProjectGrant(ProjectGrantID) returns (google.protobuf.Empty) { option (google.api.http) = { put: "/projects/{project_id}/grants/{id}/_deactivate" body: "*" @@ -1071,7 +1071,7 @@ service ManagementService { }; } - rpc ReactivateProjectGrant(ProjectGrantID) returns (ProjectGrant) { + rpc ReactivateProjectGrant(ProjectGrantID) returns (google.protobuf.Empty) { option (google.api.http) = { put: "/projects/{project_id}/grants/{id}/_reactivate" body: "*" @@ -2355,10 +2355,9 @@ message Project { string name = 2; ProjectState state = 3; google.protobuf.Timestamp change_date = 4; - google.protobuf.Timestamp creation_date = 5; - uint64 sequence = 6; - bool project_role_assertion = 7; - bool project_role_check = 8; + uint64 sequence = 5; + bool project_role_assertion = 6; + bool project_role_check = 7; } enum ProjectState { @@ -2398,14 +2397,20 @@ message ProjectMemberRemove { message ProjectRoleAdd { string id = 1 [(validate.rules).string = {min_len: 1}]; - string key = 2; + string key = 2 [(validate.rules).string = {min_len: 1}]; string display_name = 3; string group = 4; } message ProjectRoleAddBulk { string id = 1 [(validate.rules).string = {min_len: 1}]; - repeated ProjectRoleAdd project_roles = 2; + repeated ProjectRoleBulkAdd project_roles = 2; +} + +message ProjectRoleBulkAdd { + string key = 1 [(validate.rules).string = {min_len: 1}]; + string display_name = 2; + string group = 3; } message ProjectRoleChange { @@ -2419,10 +2424,9 @@ message ProjectRole { string project_id = 1; string key = 2; string display_name = 3; - google.protobuf.Timestamp creation_date = 4; - google.protobuf.Timestamp change_date = 5; - string group = 6; - uint64 sequence = 7; + google.protobuf.Timestamp change_date = 4; + string group = 5; + uint64 sequence = 6; } message ProjectRoleView {