mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-09 15:45:12 +00:00
feat: new projects (#1207)
* fix: project events * fix: project events * fix: project events * fix: eventmapper * fix: project commands * fix: project role commands * fix: project command side * fix: oidc application * fix: oidc application * fix: reduce * fix: reduce * fix: project member * fix: project grant command side * fix: application command side * fix: project grant member remove * Update internal/v2/command/project.go Co-authored-by: Livio Amstutz <[email protected]> * Update internal/v2/command/project.go Co-authored-by: Livio Amstutz <[email protected]> * Update internal/v2/command/project_application.go Co-authored-by: Livio Amstutz <[email protected]> * Update internal/v2/command/project_application.go Co-authored-by: Livio Amstutz <[email protected]> * Update internal/v2/command/project_application.go Co-authored-by: Livio Amstutz <[email protected]> * fix: oidc application string pw * fix: project events * fix: project grant member * feat: change application to interface Co-authored-by: Livio Amstutz <[email protected]>
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
//)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -13,13 +13,62 @@ func projectWriteModelToProject(writeModel *ProjectWriteModel) *domain.Project {
|
||||
}
|
||||
}
|
||||
|
||||
func applicationWriteModelToApplication(writeModel *ApplicationWriteModel) *domain.Application {
|
||||
return &domain.Application{
|
||||
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),
|
||||
AppID: writeModel.AggregateID,
|
||||
State: writeModel.State,
|
||||
Name: writeModel.Name,
|
||||
Type: writeModel.Type,
|
||||
//TODO: OIDC Config
|
||||
Roles: writeModel.Roles,
|
||||
GrantID: writeModel.GrantID,
|
||||
UserID: writeModel.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user