From 1143e3773e0b303085c48f1c59ccaea557e9f39d Mon Sep 17 00:00:00 2001 From: Fabi <38692350+fgerschwiler@users.noreply.github.com> Date: Mon, 7 Jun 2021 07:20:47 +0200 Subject: [PATCH] fix: Remove user with cascading memberships (#1811) * fix: remove usermemberships on user remove * fix: text user remove with memberships * fix: translations * Update internal/iam/repository/eventsourcing/model/types.go Co-authored-by: Livio Amstutz * fix: uncomment tests * fix: remove memberships if user removed Co-authored-by: Livio Amstutz --- .../eventsourcing/handler/iam_member.go | 3 +- internal/api/grpc/management/user.go | 6 +- .../api/grpc/management/user_converter.go | 35 +++++ .../eventsourcing/handler/user_grant.go | 17 ++- .../eventsourcing/handler/user_membership.go | 12 +- .../eventsourcing/handler/user_grant.go | 18 ++- .../eventsourcing/handler/user_membership.go | 11 +- internal/command/iam_member.go | 17 ++- internal/command/iam_member_model.go | 8 +- internal/command/org_member.go | 17 ++- internal/command/org_member_model.go | 8 +- internal/command/project_grant_member.go | 16 ++- .../command/project_grant_member_model.go | 8 ++ internal/command/project_member.go | 17 ++- internal/command/project_member_model.go | 8 +- internal/command/unique_constraints_model.go | 12 ++ internal/command/user.go | 10 +- internal/command/user_membership.go | 36 +++++ internal/command/user_test.go | 124 +++++++++++++++++- internal/domain/user_membership.go | 29 ++++ .../iam/repository/eventsourcing/model/iam.go | 2 + .../repository/eventsourcing/model/types.go | 15 ++- .../eventsourcing/eventstore/user.go | 8 ++ .../eventsourcing/handler/org_member.go | 3 +- .../handler/project_grant_member.go | 6 +- .../eventsourcing/handler/project_member.go | 4 +- .../eventsourcing/handler/user_membership.go | 10 +- .../view/project_grant_member.go | 4 + .../eventsourcing/view/project_member.go | 4 + .../eventsourcing/view/user_membership.go | 4 + internal/management/repository/user.go | 1 + .../org/repository/eventsourcing/model/org.go | 3 +- .../repository/eventsourcing/model/types.go | 7 +- .../repository/eventsourcing/model/project.go | 4 + .../repository/eventsourcing/model/types.go | 14 +- .../view/project_grant_member_view.go | 5 + .../repository/view/project_member_view.go | 5 + internal/query/iam_member_model.go | 3 +- internal/query/iam_members_model.go | 2 + internal/query/org_member_model.go | 2 + internal/repository/iam/eventstore.go | 1 + internal/repository/iam/member.go | 38 +++++- internal/repository/member/events.go | 49 ++++++- internal/repository/org/eventstore.go | 1 + internal/repository/org/member.go | 38 +++++- internal/repository/project/eventstore.go | 2 + internal/repository/project/grant_member.go | 54 +++++++- internal/repository/project/member.go | 38 +++++- internal/static/i18n/de.yaml | 8 ++ internal/static/i18n/en.yaml | 8 ++ .../repository/view/model/user_membership.go | 12 +- .../repository/view/usermembership_view.go | 10 ++ 52 files changed, 693 insertions(+), 84 deletions(-) create mode 100644 internal/command/user_membership.go create mode 100644 internal/domain/user_membership.go diff --git a/internal/admin/repository/eventsourcing/handler/iam_member.go b/internal/admin/repository/eventsourcing/handler/iam_member.go index be26a3bcec..a3cb908bba 100644 --- a/internal/admin/repository/eventsourcing/handler/iam_member.go +++ b/internal/admin/repository/eventsourcing/handler/iam_member.go @@ -110,7 +110,8 @@ func (m *IAMMember) processIAMMember(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case model.IAMMemberRemoved: + case model.IAMMemberRemoved, + model.IAMMemberCascadeRemoved: err := member.SetData(event) if err != nil { return err diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go index e77fcee4b0..24e3920b9d 100644 --- a/internal/api/grpc/management/user.go +++ b/internal/api/grpc/management/user.go @@ -166,7 +166,11 @@ func (s *Server) RemoveUser(ctx context.Context, req *mgmt_pb.RemoveUserRequest) if err != nil { return nil, err } - objectDetails, err := s.command.RemoveUser(ctx, req.Id, authz.GetCtxData(ctx).OrgID, userGrantsToIDs(grants)...) + membersShips, err := s.user.UserMembershipsByUserID(ctx, req.Id) + if err != nil { + return nil, err + } + objectDetails, err := s.command.RemoveUser(ctx, req.Id, authz.GetCtxData(ctx).OrgID, UserMembershipViewsToDomain(membersShips), userGrantsToIDs(grants)...) if err != nil { return nil, err } diff --git a/internal/api/grpc/management/user_converter.go b/internal/api/grpc/management/user_converter.go index d38aecb1a4..600702e5d7 100644 --- a/internal/api/grpc/management/user_converter.go +++ b/internal/api/grpc/management/user_converter.go @@ -241,3 +241,38 @@ func ListUserMembershipsRequestToModel(req *mgmt_pb.ListUserMembershipsRequest) Queries: queries, }, nil } + +func UserMembershipViewsToDomain(memberships []*user_model.UserMembershipView) []*domain.UserMembership { + result := make([]*domain.UserMembership, len(memberships)) + for i, membership := range memberships { + result[i] = &domain.UserMembership{ + UserID: membership.UserID, + MemberType: MemberTypeToDomain(membership.MemberType), + AggregateID: membership.AggregateID, + ObjectID: membership.ObjectID, + Roles: membership.Roles, + DisplayName: membership.DisplayName, + CreationDate: membership.CreationDate, + ChangeDate: membership.ChangeDate, + ResourceOwner: membership.ResourceOwner, + ResourceOwnerName: membership.ResourceOwnerName, + Sequence: membership.Sequence, + } + } + return result +} + +func MemberTypeToDomain(mType user_model.MemberType) domain.MemberType { + switch mType { + case user_model.MemberTypeIam: + return domain.MemberTypeIam + case user_model.MemberTypeOrganisation: + return domain.MemberTypeOrganisation + case user_model.MemberTypeProject: + return domain.MemberTypeProject + case user_model.MemberTypeProjectGrant: + return domain.MemberTypeProjectGrant + default: + return domain.MemberTypeUnspecified + } +} diff --git a/internal/auth/repository/eventsourcing/handler/user_grant.go b/internal/auth/repository/eventsourcing/handler/user_grant.go index ba1b70435c..66b5c8242f 100644 --- a/internal/auth/repository/eventsourcing/handler/user_grant.go +++ b/internal/auth/repository/eventsourcing/handler/user_grant.go @@ -185,11 +185,13 @@ func (u *UserGrant) processProject(event *es_models.Event) (err error) { u.fillProjectData(grant, project) } return u.view.PutUserGrants(grants, event) - case proj_es_model.ProjectMemberAdded, proj_es_model.ProjectMemberChanged, proj_es_model.ProjectMemberRemoved: + case proj_es_model.ProjectMemberAdded, proj_es_model.ProjectMemberChanged, + proj_es_model.ProjectMemberRemoved, proj_es_model.ProjectMemberCascadeRemoved: member := new(proj_es_model.ProjectMember) member.SetData(event) return u.processMember(event, "PROJECT", event.AggregateID, member.UserID, member.Roles) - case proj_es_model.ProjectGrantMemberAdded, proj_es_model.ProjectGrantMemberChanged, proj_es_model.ProjectGrantMemberRemoved: + case proj_es_model.ProjectGrantMemberAdded, proj_es_model.ProjectGrantMemberChanged, + proj_es_model.ProjectGrantMemberRemoved, proj_es_model.ProjectGrantMemberCascadeRemoved: member := new(proj_es_model.ProjectGrantMember) member.SetData(event) return u.processMember(event, "PROJECT_GRANT", member.GrantID, member.UserID, member.Roles) @@ -200,7 +202,8 @@ func (u *UserGrant) processProject(event *es_models.Event) (err error) { func (u *UserGrant) processOrg(event *es_models.Event) (err error) { switch event.Type { - case org_es_model.OrgMemberAdded, org_es_model.OrgMemberChanged, org_es_model.OrgMemberRemoved: + case org_es_model.OrgMemberAdded, org_es_model.OrgMemberChanged, + org_es_model.OrgMemberRemoved, org_es_model.OrgMemberCascadeRemoved: member := new(org_es_model.OrgMember) member.SetData(event) return u.processMember(event, "ORG", "", member.UserID, member.Roles) @@ -260,7 +263,8 @@ func (u *UserGrant) processIAMMember(event *es_models.Event, rolePrefix string, grant.Sequence = event.Sequence grant.ChangeDate = event.CreationDate return u.view.PutUserGrant(grant, event) - case iam_es_model.IAMMemberRemoved: + case iam_es_model.IAMMemberRemoved, + iam_es_model.IAMMemberCascadeRemoved: member.SetData(event) grant, err := u.view.UserGrantByIDs(u.iamID, u.iamProjectID, member.UserID) if err != nil { @@ -306,8 +310,11 @@ func (u *UserGrant) processMember(event *es_models.Event, rolePrefix, roleSuffix grant.ChangeDate = event.CreationDate return u.view.PutUserGrant(grant, event) case org_es_model.OrgMemberRemoved, + org_es_model.OrgMemberCascadeRemoved, proj_es_model.ProjectMemberRemoved, - proj_es_model.ProjectGrantMemberRemoved: + proj_es_model.ProjectMemberCascadeRemoved, + proj_es_model.ProjectGrantMemberRemoved, + proj_es_model.ProjectGrantMemberCascadeRemoved: grant, err := u.view.UserGrantByIDs(event.ResourceOwner, u.iamProjectID, userID) if err != nil && !errors.IsNotFound(err) { diff --git a/internal/auth/repository/eventsourcing/handler/user_membership.go b/internal/auth/repository/eventsourcing/handler/user_membership.go index 717988082e..d867b3a97b 100644 --- a/internal/auth/repository/eventsourcing/handler/user_membership.go +++ b/internal/auth/repository/eventsourcing/handler/user_membership.go @@ -108,7 +108,8 @@ func (m *UserMembership) processIAM(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case iam_es_model.IAMMemberRemoved: + case iam_es_model.IAMMemberRemoved, + iam_es_model.IAMMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event) default: return m.view.ProcessedUserMembershipSequence(event) @@ -139,7 +140,8 @@ func (m *UserMembership) processOrg(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case org_es_model.OrgMemberRemoved: + case org_es_model.OrgMemberRemoved, + org_es_model.OrgMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event) case org_es_model.OrgChanged: return m.updateOrgName(event) @@ -202,7 +204,8 @@ func (m *UserMembership) processProject(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case proj_es_model.ProjectMemberRemoved: + case proj_es_model.ProjectMemberRemoved, + proj_es_model.ProjectMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event) case proj_es_model.ProjectGrantMemberChanged: member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant) @@ -210,7 +213,8 @@ func (m *UserMembership) processProject(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case proj_es_model.ProjectGrantMemberRemoved: + case proj_es_model.ProjectGrantMemberRemoved, + proj_es_model.ProjectGrantMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event) case proj_es_model.ProjectChanged: return m.updateProjectDisplayName(event) diff --git a/internal/authz/repository/eventsourcing/handler/user_grant.go b/internal/authz/repository/eventsourcing/handler/user_grant.go index 0bff1bc1fb..e392dcfb35 100644 --- a/internal/authz/repository/eventsourcing/handler/user_grant.go +++ b/internal/authz/repository/eventsourcing/handler/user_grant.go @@ -102,11 +102,14 @@ func (u *UserGrant) Reduce(event *es_models.Event) (err error) { func (u *UserGrant) processProject(event *es_models.Event) (err error) { switch event.Type { - case proj_es_model.ProjectMemberAdded, proj_es_model.ProjectMemberChanged, proj_es_model.ProjectMemberRemoved: + case proj_es_model.ProjectMemberAdded, proj_es_model.ProjectMemberChanged, + proj_es_model.ProjectMemberRemoved, proj_es_model.ProjectMemberCascadeRemoved: member := new(proj_es_model.ProjectMember) member.SetData(event) return u.processMember(event, "PROJECT", event.AggregateID, member.UserID, member.Roles) - case proj_es_model.ProjectGrantMemberAdded, proj_es_model.ProjectGrantMemberChanged, proj_es_model.ProjectGrantMemberRemoved: + case proj_es_model.ProjectGrantMemberAdded, proj_es_model.ProjectGrantMemberChanged, + proj_es_model.ProjectGrantMemberRemoved, + proj_es_model.ProjectGrantMemberCascadeRemoved: member := new(proj_es_model.ProjectGrantMember) member.SetData(event) return u.processMember(event, "PROJECT_GRANT", member.GrantID, member.UserID, member.Roles) @@ -117,7 +120,8 @@ func (u *UserGrant) processProject(event *es_models.Event) (err error) { func (u *UserGrant) processOrg(event *es_models.Event) (err error) { switch event.Type { - case org_es_model.OrgMemberAdded, org_es_model.OrgMemberChanged, org_es_model.OrgMemberRemoved: + case org_es_model.OrgMemberAdded, org_es_model.OrgMemberChanged, + org_es_model.OrgMemberRemoved, org_es_model.OrgMemberCascadeRemoved: member := new(org_es_model.OrgMember) member.SetData(event) return u.processMember(event, "ORG", "", member.UserID, member.Roles) @@ -161,7 +165,8 @@ func (u *UserGrant) processIAMMember(event *es_models.Event, rolePrefix string, grant.Sequence = event.Sequence grant.ChangeDate = event.CreationDate return u.view.PutUserGrant(grant, event) - case iam_es_model.IAMMemberRemoved: + case iam_es_model.IAMMemberRemoved, + iam_es_model.IAMMemberCascadeRemoved: member.SetData(event) grant, err := u.view.UserGrantByIDs(u.iamID, u.iamProjectID, member.UserID) if err != nil { @@ -207,8 +212,11 @@ func (u *UserGrant) processMember(event *es_models.Event, rolePrefix, roleSuffix grant.ChangeDate = event.CreationDate return u.view.PutUserGrant(grant, event) case org_es_model.OrgMemberRemoved, + org_es_model.OrgMemberCascadeRemoved, proj_es_model.ProjectMemberRemoved, - proj_es_model.ProjectGrantMemberRemoved: + proj_es_model.ProjectMemberCascadeRemoved, + proj_es_model.ProjectGrantMemberRemoved, + proj_es_model.ProjectGrantMemberCascadeRemoved: grant, err := u.view.UserGrantByIDs(event.ResourceOwner, u.iamProjectID, userID) if err != nil && !errors.IsNotFound(err) { diff --git a/internal/authz/repository/eventsourcing/handler/user_membership.go b/internal/authz/repository/eventsourcing/handler/user_membership.go index 2da150a14a..2c67623ea3 100644 --- a/internal/authz/repository/eventsourcing/handler/user_membership.go +++ b/internal/authz/repository/eventsourcing/handler/user_membership.go @@ -108,7 +108,8 @@ func (m *UserMembership) processIAM(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case iam_es_model.IAMMemberRemoved: + case iam_es_model.IAMMemberRemoved, + iam_es_model.IAMMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event) default: return m.view.ProcessedUserMembershipSequence(event) @@ -139,7 +140,8 @@ func (m *UserMembership) processOrg(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case org_es_model.OrgMemberRemoved: + case org_es_model.OrgMemberRemoved, + org_es_model.OrgMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event) case org_es_model.OrgChanged: return m.updateOrgName(event) @@ -202,7 +204,7 @@ func (m *UserMembership) processProject(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case proj_es_model.ProjectMemberRemoved: + case proj_es_model.ProjectMemberRemoved, proj_es_model.ProjectMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event) case proj_es_model.ProjectGrantMemberChanged: member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant) @@ -210,7 +212,8 @@ func (m *UserMembership) processProject(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case proj_es_model.ProjectGrantMemberRemoved: + case proj_es_model.ProjectGrantMemberRemoved, + proj_es_model.ProjectGrantMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event) case proj_es_model.ProjectChanged: return m.updateProjectDisplayName(event) diff --git a/internal/command/iam_member.go b/internal/command/iam_member.go index 2f480a003e..540f4bc391 100644 --- a/internal/command/iam_member.go +++ b/internal/command/iam_member.go @@ -2,9 +2,10 @@ package command import ( "context" - "github.com/caos/zitadel/internal/eventstore" "reflect" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors" @@ -99,7 +100,8 @@ func (c *Commands) RemoveIAMMember(ctx context.Context, userID string) (*domain. } iamAgg := IAMAggregateFromWriteModel(&memberWriteModel.MemberWriteModel.WriteModel) - pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewMemberRemovedEvent(ctx, iamAgg, userID)) + removeEvent := c.removeIAMMember(ctx, iamAgg, userID, false) + pushedEvents, err := c.eventstore.PushEvents(ctx, removeEvent) if err != nil { return nil, err } @@ -111,6 +113,17 @@ func (c *Commands) RemoveIAMMember(ctx context.Context, userID string) (*domain. return writeModelToObjectDetails(&memberWriteModel.MemberWriteModel.WriteModel), nil } +func (c *Commands) removeIAMMember(ctx context.Context, iamAgg *eventstore.Aggregate, userID string, cascade bool) eventstore.EventPusher { + if cascade { + return iam_repo.NewMemberCascadeRemovedEvent( + ctx, + iamAgg, + userID) + } else { + return iam_repo.NewMemberRemovedEvent(ctx, iamAgg, userID) + } +} + func (c *Commands) iamMemberWriteModelByID(ctx context.Context, userID string) (member *IAMMemberWriteModel, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() diff --git a/internal/command/iam_member_model.go b/internal/command/iam_member_model.go index b7b41dff7a..c55c778673 100644 --- a/internal/command/iam_member_model.go +++ b/internal/command/iam_member_model.go @@ -40,6 +40,11 @@ func (wm *IAMMemberWriteModel) AppendEvents(events ...eventstore.EventReader) { continue } wm.MemberWriteModel.AppendEvents(&e.MemberRemovedEvent) + case *iam.MemberCascadeRemovedEvent: + if e.UserID != wm.MemberWriteModel.UserID { + continue + } + wm.MemberWriteModel.AppendEvents(&e.MemberCascadeRemovedEvent) } } } @@ -55,5 +60,6 @@ func (wm *IAMMemberWriteModel) Query() *eventstore.SearchQueryBuilder { EventTypes( iam.MemberAddedEventType, iam.MemberChangedEventType, - iam.MemberRemovedEventType) + iam.MemberRemovedEventType, + iam.MemberCascadeRemovedEventType) } diff --git a/internal/command/org_member.go b/internal/command/org_member.go index 8186c5c18d..19aae80fce 100644 --- a/internal/command/org_member.go +++ b/internal/command/org_member.go @@ -2,9 +2,10 @@ package command import ( "context" - "github.com/caos/zitadel/internal/eventstore" "reflect" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors" @@ -92,7 +93,8 @@ func (c *Commands) RemoveOrgMember(ctx context.Context, orgID, userID string) (* } orgAgg := OrgAggregateFromWriteModel(&m.MemberWriteModel.WriteModel) - pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewMemberRemovedEvent(ctx, orgAgg, userID)) + removeEvent := c.removeOrgMember(ctx, orgAgg, userID, false) + pushedEvents, err := c.eventstore.PushEvents(ctx, removeEvent) if err != nil { return nil, err } @@ -103,6 +105,17 @@ func (c *Commands) RemoveOrgMember(ctx context.Context, orgID, userID string) (* return writeModelToObjectDetails(&m.WriteModel), nil } +func (c *Commands) removeOrgMember(ctx context.Context, orgAgg *eventstore.Aggregate, userID string, cascade bool) eventstore.EventPusher { + if cascade { + return org.NewMemberCascadeRemovedEvent( + ctx, + orgAgg, + userID) + } else { + return org.NewMemberRemovedEvent(ctx, orgAgg, userID) + } +} + func (c *Commands) orgMemberWriteModelByID(ctx context.Context, orgID, userID string) (member *OrgMemberWriteModel, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() diff --git a/internal/command/org_member_model.go b/internal/command/org_member_model.go index 5e60331112..e8455aa32d 100644 --- a/internal/command/org_member_model.go +++ b/internal/command/org_member_model.go @@ -39,6 +39,11 @@ func (wm *OrgMemberWriteModel) AppendEvents(events ...eventstore.EventReader) { continue } wm.MemberWriteModel.AppendEvents(&e.MemberRemovedEvent) + case *org.MemberCascadeRemovedEvent: + if e.UserID != wm.MemberWriteModel.UserID { + continue + } + wm.MemberWriteModel.AppendEvents(&e.MemberCascadeRemovedEvent) } } } @@ -54,5 +59,6 @@ func (wm *OrgMemberWriteModel) Query() *eventstore.SearchQueryBuilder { EventTypes( org.MemberAddedEventType, org.MemberChangedEventType, - org.MemberRemovedEventType) + org.MemberRemovedEventType, + org.MemberCascadeRemovedEventType) } diff --git a/internal/command/project_grant_member.go b/internal/command/project_grant_member.go index 013bfd04a5..550238fece 100644 --- a/internal/command/project_grant_member.go +++ b/internal/command/project_grant_member.go @@ -7,6 +7,7 @@ import ( "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/repository/project" "github.com/caos/zitadel/internal/telemetry/tracing" ) @@ -87,7 +88,8 @@ func (c *Commands) RemoveProjectGrantMember(ctx context.Context, projectID, user } projectAgg := ProjectAggregateFromWriteModel(&m.WriteModel) - pushedEvents, err := c.eventstore.PushEvents(ctx, project.NewProjectGrantMemberRemovedEvent(ctx, projectAgg, userID, grantID)) + removeEvent := c.removeProjectGrantMember(ctx, projectAgg, userID, grantID, false) + pushedEvents, err := c.eventstore.PushEvents(ctx, removeEvent) if err != nil { return nil, err } @@ -98,6 +100,18 @@ func (c *Commands) RemoveProjectGrantMember(ctx context.Context, projectID, user return writeModelToObjectDetails(&m.WriteModel), nil } +func (c *Commands) removeProjectGrantMember(ctx context.Context, projectAgg *eventstore.Aggregate, userID, grantID string, cascade bool) eventstore.EventPusher { + if cascade { + return project.NewProjectGrantMemberCascadeRemovedEvent( + ctx, + projectAgg, + userID, + grantID) + } else { + return project.NewProjectGrantMemberRemovedEvent(ctx, projectAgg, userID, grantID) + } +} + func (c *Commands) projectGrantMemberWriteModelByID(ctx context.Context, projectID, userID, grantID string) (member *ProjectGrantMemberWriteModel, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() diff --git a/internal/command/project_grant_member_model.go b/internal/command/project_grant_member_model.go index c0e19ca433..367bcb241b 100644 --- a/internal/command/project_grant_member_model.go +++ b/internal/command/project_grant_member_model.go @@ -44,6 +44,11 @@ func (wm *ProjectGrantMemberWriteModel) AppendEvents(events ...eventstore.EventR continue } wm.WriteModel.AppendEvents(e) + case *project.GrantMemberCascadeRemovedEvent: + if e.UserID != wm.UserID || e.GrantID != wm.GrantID { + continue + } + wm.WriteModel.AppendEvents(e) case *project.GrantRemovedEvent: if e.GrantID != wm.GrantID { continue @@ -65,6 +70,8 @@ func (wm *ProjectGrantMemberWriteModel) Reduce() error { wm.Roles = e.Roles case *project.GrantMemberRemovedEvent: wm.State = domain.MemberStateRemoved + case *project.GrantMemberCascadeRemovedEvent: + wm.State = domain.MemberStateRemoved case *project.GrantRemovedEvent, *project.ProjectRemovedEvent: wm.State = domain.MemberStateRemoved } @@ -79,6 +86,7 @@ func (wm *ProjectGrantMemberWriteModel) Query() *eventstore.SearchQueryBuilder { project.GrantMemberAddedType, project.GrantMemberChangedType, project.GrantMemberRemovedType, + project.GrantMemberCascadeRemovedType, project.GrantRemovedType, project.ProjectRemovedType) } diff --git a/internal/command/project_member.go b/internal/command/project_member.go index 749cb277c2..90c48d507f 100644 --- a/internal/command/project_member.go +++ b/internal/command/project_member.go @@ -2,9 +2,10 @@ package command import ( "context" - "github.com/caos/zitadel/internal/eventstore" "reflect" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors" @@ -99,7 +100,8 @@ func (c *Commands) RemoveProjectMember(ctx context.Context, projectID, userID, r } projectAgg := ProjectAggregateFromWriteModel(&m.MemberWriteModel.WriteModel) - pushedEvents, err := c.eventstore.PushEvents(ctx, project.NewProjectMemberRemovedEvent(ctx, projectAgg, userID)) + removeEvent := c.removeProjectMember(ctx, projectAgg, userID, false) + pushedEvents, err := c.eventstore.PushEvents(ctx, removeEvent) if err != nil { return nil, err } @@ -110,6 +112,17 @@ func (c *Commands) RemoveProjectMember(ctx context.Context, projectID, userID, r return writeModelToObjectDetails(&m.WriteModel), nil } +func (c *Commands) removeProjectMember(ctx context.Context, projectAgg *eventstore.Aggregate, userID string, cascade bool) eventstore.EventPusher { + if cascade { + return project.NewProjectMemberCascadeRemovedEvent( + ctx, + projectAgg, + userID) + } else { + return project.NewProjectMemberRemovedEvent(ctx, projectAgg, userID) + } +} + func (c *Commands) projectMemberWriteModelByID(ctx context.Context, projectID, userID, resourceOwner string) (member *ProjectMemberWriteModel, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() diff --git a/internal/command/project_member_model.go b/internal/command/project_member_model.go index 83b934bb39..27fbaeab12 100644 --- a/internal/command/project_member_model.go +++ b/internal/command/project_member_model.go @@ -39,6 +39,11 @@ func (wm *ProjectMemberWriteModel) AppendEvents(events ...eventstore.EventReader continue } wm.MemberWriteModel.AppendEvents(&e.MemberRemovedEvent) + case *project.MemberCascadeRemovedEvent: + if e.UserID != wm.MemberWriteModel.UserID { + continue + } + wm.MemberWriteModel.AppendEvents(&e.MemberCascadeRemovedEvent) } } } @@ -53,5 +58,6 @@ func (wm *ProjectMemberWriteModel) Query() *eventstore.SearchQueryBuilder { ResourceOwner(wm.ResourceOwner). EventTypes(project.MemberAddedType, project.MemberChangedType, - project.MemberRemovedType) + project.MemberRemovedType, + project.MemberCascadeRemovedType) } diff --git a/internal/command/unique_constraints_model.go b/internal/command/unique_constraints_model.go index 5165a64066..d10429a638 100644 --- a/internal/command/unique_constraints_model.go +++ b/internal/command/unique_constraints_model.go @@ -101,6 +101,8 @@ func (rm *UniqueConstraintReadModel) Reduce() error { rm.addUniqueConstraint(e.Aggregate().ID, e.GrantID+e.UserID, project.NewAddProjectGrantMemberUniqueConstraint(e.Aggregate().ID, e.UserID, e.GrantID)) case *project.GrantMemberRemovedEvent: rm.removeUniqueConstraint(e.Aggregate().ID, e.GrantID+e.UserID, project.UniqueProjectGrantMemberType) + case *project.GrantMemberCascadeRemovedEvent: + rm.removeUniqueConstraint(e.Aggregate().ID, e.GrantID+e.UserID, project.UniqueProjectGrantMemberType) case *project.RoleAddedEvent: rm.addUniqueConstraint(e.Aggregate().ID, e.Key, project.NewAddProjectRoleUniqueConstraint(e.Key, e.Aggregate().ID)) case *project.RoleRemovedEvent: @@ -159,14 +161,20 @@ func (rm *UniqueConstraintReadModel) Reduce() error { rm.addUniqueConstraint(e.Aggregate().ID, e.UserID, member.NewAddMemberUniqueConstraint(e.Aggregate().ID, e.UserID)) case *iam.MemberRemovedEvent: rm.removeUniqueConstraint(e.Aggregate().ID, e.UserID, member.UniqueMember) + case *iam.MemberCascadeRemovedEvent: + rm.removeUniqueConstraint(e.Aggregate().ID, e.UserID, member.UniqueMember) case *org.MemberAddedEvent: rm.addUniqueConstraint(e.Aggregate().ID, e.UserID, member.NewAddMemberUniqueConstraint(e.Aggregate().ID, e.UserID)) case *org.MemberRemovedEvent: rm.removeUniqueConstraint(e.Aggregate().ID, e.UserID, member.UniqueMember) + case *org.MemberCascadeRemovedEvent: + rm.removeUniqueConstraint(e.Aggregate().ID, e.UserID, member.UniqueMember) case *project.MemberAddedEvent: rm.addUniqueConstraint(e.Aggregate().ID, e.UserID, member.NewAddMemberUniqueConstraint(e.Aggregate().ID, e.UserID)) case *project.MemberRemovedEvent: rm.removeUniqueConstraint(e.Aggregate().ID, e.UserID, member.UniqueMember) + case *project.MemberCascadeRemovedEvent: + rm.removeUniqueConstraint(e.Aggregate().ID, e.UserID, member.UniqueMember) } } return nil @@ -204,6 +212,7 @@ func (rm *UniqueConstraintReadModel) Query() *eventstore.SearchQueryBuilder { project.GrantRemovedType, project.GrantMemberAddedType, project.GrantMemberRemovedType, + project.GrantMemberCascadeRemovedType, project.RoleAddedType, project.RoleRemovedType, user.UserV1AddedType, @@ -222,10 +231,13 @@ func (rm *UniqueConstraintReadModel) Query() *eventstore.SearchQueryBuilder { usergrant.UserGrantCascadeRemovedType, iam.MemberAddedEventType, iam.MemberRemovedEventType, + iam.MemberCascadeRemovedEventType, org.MemberAddedEventType, org.MemberRemovedEventType, + org.MemberCascadeRemovedEventType, project.MemberAddedType, project.MemberRemovedType, + project.MemberCascadeRemovedType, ) } diff --git a/internal/command/user.go b/internal/command/user.go index f28ddc2877..07531a8005 100644 --- a/internal/command/user.go +++ b/internal/command/user.go @@ -169,7 +169,7 @@ func (c *Commands) UnlockUser(ctx context.Context, userID, resourceOwner string) return writeModelToObjectDetails(&existingUser.WriteModel), nil } -func (c *Commands) RemoveUser(ctx context.Context, userID, resourceOwner string, cascadingGrantIDs ...string) (*domain.ObjectDetails, error) { +func (c *Commands) RemoveUser(ctx context.Context, userID, resourceOwner string, cascadingUserMemberships []*domain.UserMembership, cascadingGrantIDs ...string) (*domain.ObjectDetails, error) { if userID == "" { return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing") } @@ -199,6 +199,14 @@ func (c *Commands) RemoveUser(ctx context.Context, userID, resourceOwner string, events = append(events, removeEvent) } + if len(cascadingUserMemberships) > 0 { + membershipEvents, err := c.removeUserMemberships(ctx, cascadingUserMemberships, true) + if err != nil { + return nil, err + } + events = append(events, membershipEvents...) + } + pushedEvents, err := c.eventstore.PushEvents(ctx, events...) if err != nil { return nil, err diff --git a/internal/command/user_membership.go b/internal/command/user_membership.go new file mode 100644 index 0000000000..5058ce66e5 --- /dev/null +++ b/internal/command/user_membership.go @@ -0,0 +1,36 @@ +package command + +import ( + "context" + + "github.com/caos/zitadel/internal/domain" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/repository/iam" + "github.com/caos/zitadel/internal/repository/org" + "github.com/caos/zitadel/internal/repository/project" +) + +func (c *Commands) removeUserMemberships(ctx context.Context, memberships []*domain.UserMembership, cascade bool) (_ []eventstore.EventPusher, err error) { + events := make([]eventstore.EventPusher, 0) + for _, membership := range memberships { + switch membership.MemberType { + case domain.MemberTypeIam: + iamAgg := iam.NewAggregate() + removeEvent := c.removeIAMMember(ctx, &iamAgg.Aggregate, membership.UserID, true) + events = append(events, removeEvent) + case domain.MemberTypeOrganisation: + iamAgg := org.NewAggregate(membership.AggregateID, membership.ResourceOwner) + removeEvent := c.removeOrgMember(ctx, &iamAgg.Aggregate, membership.UserID, true) + events = append(events, removeEvent) + case domain.MemberTypeProject: + projectAgg := project.NewAggregate(membership.AggregateID, membership.ResourceOwner) + removeEvent := c.removeProjectMember(ctx, &projectAgg.Aggregate, membership.UserID, true) + events = append(events, removeEvent) + case domain.MemberTypeProjectGrant: + projectAgg := project.NewAggregate(membership.AggregateID, membership.ResourceOwner) + removeEvent := c.removeProjectGrantMember(ctx, &projectAgg.Aggregate, membership.UserID, membership.ObjectID, true) + events = append(events, removeEvent) + } + } + return events, nil +} diff --git a/internal/command/user_test.go b/internal/command/user_test.go index 5431f9265d..a7bec73bbf 100644 --- a/internal/command/user_test.go +++ b/internal/command/user_test.go @@ -14,6 +14,9 @@ import ( "github.com/caos/zitadel/internal/eventstore/repository" "github.com/caos/zitadel/internal/id" "github.com/caos/zitadel/internal/repository/iam" + "github.com/caos/zitadel/internal/repository/member" + "github.com/caos/zitadel/internal/repository/org" + "github.com/caos/zitadel/internal/repository/project" "github.com/caos/zitadel/internal/repository/user" ) @@ -914,9 +917,11 @@ func TestCommandSide_RemoveUser(t *testing.T) { } type ( args struct { - ctx context.Context - orgID string - userID string + ctx context.Context + orgID string + userID string + cascadeUserMemberships []*domain.UserMembership + cascadeUserGrants []string } ) type res struct { @@ -1051,13 +1056,124 @@ func TestCommandSide_RemoveUser(t *testing.T) { }, }, }, + { + name: "remove user with user memberships, ok", + fields: fields{ + eventstore: eventstoreExpect( + t, + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + ), + ), + expectFilter(), + expectFilter( + eventFromEventPusher( + iam.NewOrgIAMPolicyAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + true, + ), + ), + ), + expectPush( + []*repository.Event{ + eventFromEventPusher( + user.NewUserRemovedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + true, + ), + ), + eventFromEventPusher( + iam.NewMemberCascadeRemovedEvent(context.Background(), + &iam.NewAggregate().Aggregate, + "user1", + ), + ), + eventFromEventPusher( + org.NewMemberCascadeRemovedEvent(context.Background(), + &org.NewAggregate("org1", "org1").Aggregate, + "user1", + ), + ), + eventFromEventPusher( + project.NewProjectMemberCascadeRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + ), + ), + eventFromEventPusher( + project.NewProjectGrantMemberCascadeRemovedEvent(context.Background(), + &project.NewAggregate("project1", "org1").Aggregate, + "user1", + "grant1", + ), + ), + }, + uniqueConstraintsFromEventConstraint(user.NewRemoveUsernameUniqueConstraint("username", "org1", true)), + uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint(domain.IAMID, "user1")), + uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint("org1", "user1")), + uniqueConstraintsFromEventConstraint(member.NewRemoveMemberUniqueConstraint("project1", "user1")), + uniqueConstraintsFromEventConstraint(project.NewRemoveProjectGrantMemberUniqueConstraint("project1", "user1", "grant1")), + ), + ), + }, + args: args{ + ctx: context.Background(), + orgID: "org1", + userID: "user1", + cascadeUserMemberships: []*domain.UserMembership{ + { + MemberType: domain.MemberTypeIam, + UserID: "user1", + AggregateID: "IAM", + ResourceOwner: "org1", + }, + { + MemberType: domain.MemberTypeOrganisation, + UserID: "user1", + ResourceOwner: "org1", + AggregateID: "org1", + }, + { + MemberType: domain.MemberTypeProject, + UserID: "user1", + ResourceOwner: "org1", + AggregateID: "project1", + }, + { + MemberType: domain.MemberTypeProjectGrant, + UserID: "user1", + ResourceOwner: "org1", + AggregateID: "project1", + ObjectID: "grant1", + }, + }, + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "org1", + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } - got, err := r.RemoveUser(tt.args.ctx, tt.args.userID, tt.args.orgID) + got, err := r.RemoveUser(tt.args.ctx, tt.args.userID, tt.args.orgID, tt.args.cascadeUserMemberships, tt.args.cascadeUserGrants...) if tt.res.err == nil { assert.NoError(t, err) } diff --git a/internal/domain/user_membership.go b/internal/domain/user_membership.go new file mode 100644 index 0000000000..ae786237ea --- /dev/null +++ b/internal/domain/user_membership.go @@ -0,0 +1,29 @@ +package domain + +import "time" + +type UserMembership struct { + UserID string + MemberType MemberType + AggregateID string + //ObjectID differs from aggregate id if obejct is sub of an aggregate + ObjectID string + + Roles []string + DisplayName string + CreationDate time.Time + ChangeDate time.Time + ResourceOwner string + ResourceOwnerName string + Sequence uint64 +} + +type MemberType int32 + +const ( + MemberTypeUnspecified MemberType = iota + MemberTypeOrganisation + MemberTypeProject + MemberTypeProjectGrant + MemberTypeIam +) diff --git a/internal/iam/repository/eventsourcing/model/iam.go b/internal/iam/repository/eventsourcing/model/iam.go index b172b088d9..2d14dd37f2 100644 --- a/internal/iam/repository/eventsourcing/model/iam.go +++ b/internal/iam/repository/eventsourcing/model/iam.go @@ -159,6 +159,8 @@ func (i *IAM) AppendEvent(event *es_models.Event) (err error) { err = i.appendChangeMemberEvent(event) case IAMMemberRemoved: err = i.appendRemoveMemberEvent(event) + case IAMMemberCascadeRemoved: + err = i.appendRemoveMemberEvent(event) case IDPConfigAdded: return i.appendAddIDPConfigEvent(event) case IDPConfigChanged: diff --git a/internal/iam/repository/eventsourcing/model/types.go b/internal/iam/repository/eventsourcing/model/types.go index 12289c9587..46a0cc4af4 100644 --- a/internal/iam/repository/eventsourcing/model/types.go +++ b/internal/iam/repository/eventsourcing/model/types.go @@ -5,13 +5,14 @@ import "github.com/caos/zitadel/internal/eventstore/v1/models" const ( IAMAggregate models.AggregateType = "iam" - IAMSetupStarted models.EventType = "iam.setup.started" - IAMSetupDone models.EventType = "iam.setup.done" - GlobalOrgSet models.EventType = "iam.global.org.set" - IAMProjectSet models.EventType = "iam.project.iam.set" - IAMMemberAdded models.EventType = "iam.member.added" - IAMMemberChanged models.EventType = "iam.member.changed" - IAMMemberRemoved models.EventType = "iam.member.removed" + IAMSetupStarted models.EventType = "iam.setup.started" + IAMSetupDone models.EventType = "iam.setup.done" + GlobalOrgSet models.EventType = "iam.global.org.set" + IAMProjectSet models.EventType = "iam.project.iam.set" + IAMMemberAdded models.EventType = "iam.member.added" + IAMMemberChanged models.EventType = "iam.member.changed" + IAMMemberRemoved models.EventType = "iam.member.removed" + IAMMemberCascadeRemoved models.EventType = "iam.member.cascade.removed" IDPConfigAdded models.EventType = "iam.idp.config.added" IDPConfigChanged models.EventType = "iam.idp.config.changed" diff --git a/internal/management/repository/eventsourcing/eventstore/user.go b/internal/management/repository/eventsourcing/eventstore/user.go index f0ba0ff418..9d384d444b 100644 --- a/internal/management/repository/eventsourcing/eventstore/user.go +++ b/internal/management/repository/eventsourcing/eventstore/user.go @@ -298,6 +298,14 @@ func (repo *UserRepo) SearchUserMemberships(ctx context.Context, request *usr_mo return result, nil } +func (repo *UserRepo) UserMembershipsByUserID(ctx context.Context, userID string) ([]*usr_model.UserMembershipView, error) { + memberships, err := repo.View.UserMembershipsByUserID(userID) + if err != nil { + return nil, err + } + return model.UserMembershipsToModel(memberships), nil +} + func (r *UserRepo) getUserChanges(ctx context.Context, userID string, lastSequence uint64, limit uint64, sortAscending bool, retention time.Duration) (*usr_model.UserChanges, error) { query := usr_view.ChangesQuery(userID, lastSequence, limit, sortAscending, retention) diff --git a/internal/management/repository/eventsourcing/handler/org_member.go b/internal/management/repository/eventsourcing/handler/org_member.go index 3ae0b73287..9433b72b06 100644 --- a/internal/management/repository/eventsourcing/handler/org_member.go +++ b/internal/management/repository/eventsourcing/handler/org_member.go @@ -109,7 +109,8 @@ func (m *OrgMember) processOrgMember(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case model.OrgMemberRemoved: + case model.OrgMemberRemoved, + model.OrgMemberCascadeRemoved: err = member.SetData(event) if err != nil { return err diff --git a/internal/management/repository/eventsourcing/handler/project_grant_member.go b/internal/management/repository/eventsourcing/handler/project_grant_member.go index b9af309f6b..af21ecf1e4 100644 --- a/internal/management/repository/eventsourcing/handler/project_grant_member.go +++ b/internal/management/repository/eventsourcing/handler/project_grant_member.go @@ -111,7 +111,8 @@ func (p *ProjectGrantMember) processProjectGrantMember(event *es_models.Event) ( return err } err = member.AppendEvent(event) - case proj_es_model.ProjectGrantMemberRemoved: + case proj_es_model.ProjectGrantMemberRemoved, + proj_es_model.ProjectGrantMemberCascadeRemoved: err = member.SetData(event) if err != nil { return err @@ -154,9 +155,12 @@ func (p *ProjectGrantMember) processUser(event *es_models.Event) (err error) { p.fillUserData(member, user) } return p.view.PutProjectGrantMembers(members, event) + case usr_es_model.UserRemoved: + p.view.DeleteProjectGrantMembersByUserID(event.AggregateID) default: return p.view.ProcessedProjectGrantMemberSequence(event) } + return nil } func (p *ProjectGrantMember) fillData(member *view_model.ProjectGrantMemberView) (err error) { diff --git a/internal/management/repository/eventsourcing/handler/project_member.go b/internal/management/repository/eventsourcing/handler/project_member.go index c9592bb0ac..fc2573d414 100644 --- a/internal/management/repository/eventsourcing/handler/project_member.go +++ b/internal/management/repository/eventsourcing/handler/project_member.go @@ -111,7 +111,7 @@ func (p *ProjectMember) processProjectMember(event *es_models.Event) (err error) return err } err = member.AppendEvent(event) - case proj_es_model.ProjectMemberRemoved: + case proj_es_model.ProjectMemberRemoved, proj_es_model.ProjectMemberCascadeRemoved: err = member.SetData(event) if err != nil { return err @@ -150,6 +150,8 @@ func (p *ProjectMember) processUser(event *es_models.Event) (err error) { p.fillUserData(member, user) } return p.view.PutProjectMembers(members, event) + case usr_es_model.UserRemoved: + p.view.DeleteProjectMembersByUserID(event.AggregateID) default: return p.view.ProcessedProjectMemberSequence(event) } diff --git a/internal/management/repository/eventsourcing/handler/user_membership.go b/internal/management/repository/eventsourcing/handler/user_membership.go index 164af8e3e5..f28737c3cb 100644 --- a/internal/management/repository/eventsourcing/handler/user_membership.go +++ b/internal/management/repository/eventsourcing/handler/user_membership.go @@ -107,7 +107,8 @@ func (m *UserMembership) processIAM(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case iam_es_model.IAMMemberRemoved: + case iam_es_model.IAMMemberRemoved, + iam_es_model.IAMMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event) default: return m.view.ProcessedUserMembershipSequence(event) @@ -137,7 +138,7 @@ func (m *UserMembership) processOrg(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case org_es_model.OrgMemberRemoved: + case org_es_model.OrgMemberRemoved, org_es_model.OrgMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event) case org_es_model.OrgChanged: return m.updateOrgDisplayName(event) @@ -190,7 +191,7 @@ func (m *UserMembership) processProject(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case proj_es_model.ProjectMemberRemoved: + case proj_es_model.ProjectMemberRemoved, proj_es_model.ProjectMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event) case proj_es_model.ProjectGrantMemberChanged: member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant) @@ -198,7 +199,8 @@ func (m *UserMembership) processProject(event *es_models.Event) (err error) { return err } err = member.AppendEvent(event) - case proj_es_model.ProjectGrantMemberRemoved: + case proj_es_model.ProjectGrantMemberRemoved, + proj_es_model.ProjectGrantMemberCascadeRemoved: return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event) case proj_es_model.ProjectChanged: return m.updateProjectDisplayName(event) diff --git a/internal/management/repository/eventsourcing/view/project_grant_member.go b/internal/management/repository/eventsourcing/view/project_grant_member.go index 263cd51a28..b12da68849 100644 --- a/internal/management/repository/eventsourcing/view/project_grant_member.go +++ b/internal/management/repository/eventsourcing/view/project_grant_member.go @@ -57,6 +57,10 @@ func (v *View) DeleteProjectGrantMembersByProjectID(projectID string) error { return view.DeleteProjectGrantMembersByProjectID(v.Db, projectGrantMemberTable, projectID) } +func (v *View) DeleteProjectGrantMembersByUserID(userID string) error { + return view.DeleteProjectGrantMembersByUserID(v.Db, projectGrantMemberTable, userID) +} + func (v *View) GetLatestProjectGrantMemberSequence() (*repository.CurrentSequence, error) { return v.latestSequence(projectGrantMemberTable) } diff --git a/internal/management/repository/eventsourcing/view/project_member.go b/internal/management/repository/eventsourcing/view/project_member.go index 97de82dcd1..78d4fcb793 100644 --- a/internal/management/repository/eventsourcing/view/project_member.go +++ b/internal/management/repository/eventsourcing/view/project_member.go @@ -57,6 +57,10 @@ func (v *View) DeleteProjectMembersByProjectID(projectID string) error { return view.DeleteProjectMembersByProjectID(v.Db, projectMemberTable, projectID) } +func (v *View) DeleteProjectMembersByUserID(userID string) error { + return view.DeleteProjectMembersByUserID(v.Db, projectMemberTable, userID) +} + func (v *View) GetLatestProjectMemberSequence() (*repository.CurrentSequence, error) { return v.latestSequence(projectMemberTable) } diff --git a/internal/management/repository/eventsourcing/view/user_membership.go b/internal/management/repository/eventsourcing/view/user_membership.go index dd1f39d2ba..8d08b2f528 100644 --- a/internal/management/repository/eventsourcing/view/user_membership.go +++ b/internal/management/repository/eventsourcing/view/user_membership.go @@ -21,6 +21,10 @@ func (v *View) UserMembershipsByAggregateID(aggregateID string) ([]*model.UserMe return view.UserMembershipsByAggregateID(v.Db, userMembershipTable, aggregateID) } +func (v *View) UserMembershipsByUserID(userID string) ([]*model.UserMembershipView, error) { + return view.UserMembershipsByUserID(v.Db, userMembershipTable, userID) +} + func (v *View) SearchUserMemberships(request *usr_model.UserMembershipSearchRequest) ([]*model.UserMembershipView, uint64, error) { return view.SearchUserMemberships(v.Db, userMembershipTable, request) } diff --git a/internal/management/repository/user.go b/internal/management/repository/user.go index dce5636ed0..b6bf9e832a 100644 --- a/internal/management/repository/user.go +++ b/internal/management/repository/user.go @@ -38,4 +38,5 @@ type UserRepository interface { AddressByID(ctx context.Context, userID string) (*model.Address, error) SearchUserMemberships(ctx context.Context, request *model.UserMembershipSearchRequest) (*model.UserMembershipSearchResponse, error) + UserMembershipsByUserID(ctx context.Context, userID string) ([]*model.UserMembershipView, error) } diff --git a/internal/org/repository/eventsourcing/model/org.go b/internal/org/repository/eventsourcing/model/org.go index 0880add1ce..499e44a64e 100644 --- a/internal/org/repository/eventsourcing/model/org.go +++ b/internal/org/repository/eventsourcing/model/org.go @@ -157,7 +157,8 @@ func (o *Org) AppendEvent(event *es_models.Event) (err error) { member.CreationDate = existingMember.CreationDate o.setMember(member) - case OrgMemberRemoved: + case OrgMemberRemoved, + OrgMemberCascadeRemoved: member, err := OrgMemberFromEvent(nil, event) if err != nil { return err diff --git a/internal/org/repository/eventsourcing/model/types.go b/internal/org/repository/eventsourcing/model/types.go index d4ed82aacb..4a00b147da 100644 --- a/internal/org/repository/eventsourcing/model/types.go +++ b/internal/org/repository/eventsourcing/model/types.go @@ -25,9 +25,10 @@ const ( OrgDomainReserved models.EventType = "org.domain.reserved" OrgDomainReleased models.EventType = "org.domain.released" - OrgMemberAdded models.EventType = "org.member.added" - OrgMemberChanged models.EventType = "org.member.changed" - OrgMemberRemoved models.EventType = "org.member.removed" + OrgMemberAdded models.EventType = "org.member.added" + OrgMemberChanged models.EventType = "org.member.changed" + OrgMemberRemoved models.EventType = "org.member.removed" + OrgMemberCascadeRemoved models.EventType = "org.member.cascade.removed" OrgIAMPolicyAdded models.EventType = "org.iam.policy.added" OrgIAMPolicyChanged models.EventType = "org.iam.policy.changed" diff --git a/internal/project/repository/eventsourcing/model/project.go b/internal/project/repository/eventsourcing/model/project.go index 99765c8e3c..6eb1b6bb2e 100644 --- a/internal/project/repository/eventsourcing/model/project.go +++ b/internal/project/repository/eventsourcing/model/project.go @@ -119,6 +119,8 @@ func (p *Project) AppendEvent(event *es_models.Event) error { return p.appendChangeMemberEvent(event) case ProjectMemberRemoved: return p.appendRemoveMemberEvent(event) + case ProjectMemberCascadeRemoved: + return p.appendRemoveMemberEvent(event) case ProjectRoleAdded: return p.appendAddRoleEvent(event) case ProjectRoleChanged: @@ -163,6 +165,8 @@ func (p *Project) AppendEvent(event *es_models.Event) error { return p.appendChangeGrantMemberEvent(event) case ProjectGrantMemberRemoved: return p.appendRemoveGrantMemberEvent(event) + case ProjectGrantMemberCascadeRemoved: + return p.appendRemoveGrantMemberEvent(event) } return nil } diff --git a/internal/project/repository/eventsourcing/model/types.go b/internal/project/repository/eventsourcing/model/types.go index 1828c5b84a..40dd7229a5 100644 --- a/internal/project/repository/eventsourcing/model/types.go +++ b/internal/project/repository/eventsourcing/model/types.go @@ -11,9 +11,10 @@ const ( ProjectReactivated models.EventType = "project.reactivated" ProjectRemoved models.EventType = "project.removed" - ProjectMemberAdded models.EventType = "project.member.added" - ProjectMemberChanged models.EventType = "project.member.changed" - ProjectMemberRemoved models.EventType = "project.member.removed" + ProjectMemberAdded models.EventType = "project.member.added" + ProjectMemberChanged models.EventType = "project.member.changed" + ProjectMemberRemoved models.EventType = "project.member.removed" + ProjectMemberCascadeRemoved models.EventType = "project.member.cascade.removed" ProjectRoleAdded models.EventType = "project.role.added" ProjectRoleChanged models.EventType = "project.role.changed" @@ -26,9 +27,10 @@ const ( ProjectGrantReactivated models.EventType = "project.grant.reactivated" ProjectGrantCascadeChanged models.EventType = "project.grant.cascade.changed" - ProjectGrantMemberAdded models.EventType = "project.grant.member.added" - ProjectGrantMemberChanged models.EventType = "project.grant.member.changed" - ProjectGrantMemberRemoved models.EventType = "project.grant.member.removed" + ProjectGrantMemberAdded models.EventType = "project.grant.member.added" + ProjectGrantMemberChanged models.EventType = "project.grant.member.changed" + ProjectGrantMemberRemoved models.EventType = "project.grant.member.removed" + ProjectGrantMemberCascadeRemoved models.EventType = "project.grant.member.cascade.removed" ApplicationAdded models.EventType = "project.application.added" ApplicationChanged models.EventType = "project.application.changed" diff --git a/internal/project/repository/view/project_grant_member_view.go b/internal/project/repository/view/project_grant_member_view.go index b98e13bdf9..6c8db141be 100644 --- a/internal/project/repository/view/project_grant_member_view.go +++ b/internal/project/repository/view/project_grant_member_view.go @@ -85,3 +85,8 @@ func DeleteProjectGrantMembersByProjectID(db *gorm.DB, table, projectID string) delete := repository.PrepareDeleteByKey(table, model.ProjectGrantMemberSearchKey(proj_model.ProjectGrantMemberSearchKeyProjectID), projectID) return delete(db) } + +func DeleteProjectGrantMembersByUserID(db *gorm.DB, table, userID string) error { + delete := repository.PrepareDeleteByKey(table, model.ProjectGrantMemberSearchKey(proj_model.ProjectGrantMemberSearchKeyUserID), userID) + return delete(db) +} diff --git a/internal/project/repository/view/project_member_view.go b/internal/project/repository/view/project_member_view.go index 248b1a37ed..cc1dec40f6 100644 --- a/internal/project/repository/view/project_member_view.go +++ b/internal/project/repository/view/project_member_view.go @@ -84,3 +84,8 @@ func DeleteProjectMembersByProjectID(db *gorm.DB, table, projectID string) error delete := repository.PrepareDeleteByKey(table, model.ProjectMemberSearchKey(proj_model.ProjectMemberSearchKeyProjectID), projectID) return delete(db) } + +func DeleteProjectMembersByUserID(db *gorm.DB, table, userID string) error { + delete := repository.PrepareDeleteByKey(table, model.ProjectMemberSearchKey(proj_model.ProjectMemberSearchKeyUserID), userID) + return delete(db) +} diff --git a/internal/query/iam_member_model.go b/internal/query/iam_member_model.go index febb461be3..bf59ab4a1f 100644 --- a/internal/query/iam_member_model.go +++ b/internal/query/iam_member_model.go @@ -27,7 +27,8 @@ func (rm *IAMMemberReadModel) AppendEvents(events ...eventstore.EventReader) { rm.MemberReadModel.AppendEvents(&e.MemberAddedEvent) case *iam.MemberChangedEvent: rm.MemberReadModel.AppendEvents(&e.MemberChangedEvent) - case *member.MemberAddedEvent, *member.MemberChangedEvent, *iam.MemberRemovedEvent: + case *member.MemberAddedEvent, *member.MemberChangedEvent, + *iam.MemberRemovedEvent, *iam.MemberCascadeRemovedEvent: rm.MemberReadModel.AppendEvents(e) } } diff --git a/internal/query/iam_members_model.go b/internal/query/iam_members_model.go index c28c0fa557..c13c5cf705 100644 --- a/internal/query/iam_members_model.go +++ b/internal/query/iam_members_model.go @@ -18,6 +18,8 @@ func (rm *IAMMembersReadModel) AppendEvents(events ...eventstore.EventReader) { rm.MembersReadModel.AppendEvents(&e.MemberChangedEvent) case *iam.MemberRemovedEvent: rm.MembersReadModel.AppendEvents(&e.MemberRemovedEvent) + case *iam.MemberCascadeRemovedEvent: + rm.MembersReadModel.AppendEvents(&e.MemberCascadeRemovedEvent) } } } diff --git a/internal/query/org_member_model.go b/internal/query/org_member_model.go index eaf0757d83..b942cfe679 100644 --- a/internal/query/org_member_model.go +++ b/internal/query/org_member_model.go @@ -18,6 +18,8 @@ func (rm *OrgMembersReadModel) AppendEvents(events ...eventstore.EventReader) { rm.MembersReadModel.AppendEvents(&e.MemberChangedEvent) case *org.MemberRemovedEvent: rm.MembersReadModel.AppendEvents(&e.MemberRemovedEvent) + case *org.MemberCascadeRemovedEvent: + rm.MembersReadModel.AppendEvents(&e.MemberCascadeRemovedEvent) } } } diff --git a/internal/repository/iam/eventstore.go b/internal/repository/iam/eventstore.go index 0e06b86e94..61b9d8a6ef 100644 --- a/internal/repository/iam/eventstore.go +++ b/internal/repository/iam/eventstore.go @@ -37,6 +37,7 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(MemberAddedEventType, MemberAddedEventMapper). RegisterFilterEventMapper(MemberChangedEventType, MemberChangedEventMapper). RegisterFilterEventMapper(MemberRemovedEventType, MemberRemovedEventMapper). + RegisterFilterEventMapper(MemberCascadeRemovedEventType, MemberCascadeRemovedEventMapper). RegisterFilterEventMapper(IDPConfigAddedEventType, IDPConfigAddedEventMapper). RegisterFilterEventMapper(IDPConfigChangedEventType, IDPConfigChangedEventMapper). RegisterFilterEventMapper(IDPConfigRemovedEventType, IDPConfigRemovedEventMapper). diff --git a/internal/repository/iam/member.go b/internal/repository/iam/member.go index 2a2487af96..b7a5d281c8 100644 --- a/internal/repository/iam/member.go +++ b/internal/repository/iam/member.go @@ -9,9 +9,10 @@ import ( ) var ( - MemberAddedEventType = iamEventTypePrefix + member.AddedEventType - MemberChangedEventType = iamEventTypePrefix + member.ChangedEventType - MemberRemovedEventType = iamEventTypePrefix + member.RemovedEventType + MemberAddedEventType = iamEventTypePrefix + member.AddedEventType + MemberChangedEventType = iamEventTypePrefix + member.ChangedEventType + MemberRemovedEventType = iamEventTypePrefix + member.RemovedEventType + MemberCascadeRemovedEventType = iamEventTypePrefix + member.CascadeRemovedEventType ) type MemberAddedEvent struct { @@ -88,7 +89,6 @@ func NewMemberRemovedEvent( aggregate *eventstore.Aggregate, userID string, ) *MemberRemovedEvent { - return &MemberRemovedEvent{ MemberRemovedEvent: *member.NewRemovedEvent( eventstore.NewBaseEventForPush( @@ -109,3 +109,33 @@ func MemberRemovedEventMapper(event *repository.Event) (eventstore.EventReader, return &MemberRemovedEvent{MemberRemovedEvent: *e.(*member.MemberRemovedEvent)}, nil } + +type MemberCascadeRemovedEvent struct { + member.MemberCascadeRemovedEvent +} + +func NewMemberCascadeRemovedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + userID string, +) *MemberCascadeRemovedEvent { + return &MemberCascadeRemovedEvent{ + MemberCascadeRemovedEvent: *member.NewCascadeRemovedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + MemberCascadeRemovedEventType, + ), + userID, + ), + } +} + +func MemberCascadeRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e, err := member.CascadeRemovedEventMapper(event) + if err != nil { + return nil, err + } + + return &MemberCascadeRemovedEvent{MemberCascadeRemovedEvent: *e.(*member.MemberCascadeRemovedEvent)}, nil +} diff --git a/internal/repository/member/events.go b/internal/repository/member/events.go index ba591c9e13..bb829afd59 100644 --- a/internal/repository/member/events.go +++ b/internal/repository/member/events.go @@ -9,10 +9,11 @@ import ( ) const ( - UniqueMember = "member" - AddedEventType = "member.added" - ChangedEventType = "member.changed" - RemovedEventType = "member.removed" + UniqueMember = "member" + AddedEventType = "member.added" + ChangedEventType = "member.changed" + RemovedEventType = "member.removed" + CascadeRemovedEventType = "member.cascade.removed" ) func NewAddMemberUniqueConstraint(aggregateID, userID string) *eventstore.EventUniqueConstraint { @@ -142,7 +143,45 @@ func RemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) err := json.Unmarshal(event.Data, e) if err != nil { - return nil, errors.ThrowInternal(err, "POLIC-Ep4ip", "unable to unmarshal label policy") + return nil, errors.ThrowInternal(err, "MEMBER-Ep4ip", "unable to unmarshal label policy") + } + + return e, nil +} + +type MemberCascadeRemovedEvent struct { + eventstore.BaseEvent `json:"-"` + + UserID string `json:"userId"` +} + +func (e *MemberCascadeRemovedEvent) Data() interface{} { + return e +} + +func (e *MemberCascadeRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{NewRemoveMemberUniqueConstraint(e.Aggregate().ID, e.UserID)} +} + +func NewCascadeRemovedEvent( + base *eventstore.BaseEvent, + userID string, +) *MemberCascadeRemovedEvent { + + return &MemberCascadeRemovedEvent{ + BaseEvent: *base, + UserID: userID, + } +} + +func CascadeRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &MemberCascadeRemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "MEMBER-3j9sf", "unable to unmarshal label policy") } return e, nil diff --git a/internal/repository/org/eventstore.go b/internal/repository/org/eventstore.go index 1deed8d412..e5df74644a 100644 --- a/internal/repository/org/eventstore.go +++ b/internal/repository/org/eventstore.go @@ -18,6 +18,7 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(MemberAddedEventType, MemberAddedEventMapper). RegisterFilterEventMapper(MemberChangedEventType, MemberChangedEventMapper). RegisterFilterEventMapper(MemberRemovedEventType, MemberRemovedEventMapper). + RegisterFilterEventMapper(MemberCascadeRemovedEventType, MemberCascadeRemovedEventMapper). RegisterFilterEventMapper(LabelPolicyAddedEventType, LabelPolicyAddedEventMapper). RegisterFilterEventMapper(LabelPolicyChangedEventType, LabelPolicyChangedEventMapper). RegisterFilterEventMapper(LabelPolicyActivatedEventType, LabelPolicyActivatedEventMapper). diff --git a/internal/repository/org/member.go b/internal/repository/org/member.go index 7c6e21c856..2ca03747bd 100644 --- a/internal/repository/org/member.go +++ b/internal/repository/org/member.go @@ -9,9 +9,10 @@ import ( ) var ( - MemberAddedEventType = orgEventTypePrefix + member.AddedEventType - MemberChangedEventType = orgEventTypePrefix + member.ChangedEventType - MemberRemovedEventType = orgEventTypePrefix + member.RemovedEventType + MemberAddedEventType = orgEventTypePrefix + member.AddedEventType + MemberChangedEventType = orgEventTypePrefix + member.ChangedEventType + MemberRemovedEventType = orgEventTypePrefix + member.RemovedEventType + MemberCascadeRemovedEventType = orgEventTypePrefix + member.CascadeRemovedEventType ) type MemberAddedEvent struct { @@ -88,7 +89,6 @@ func NewMemberRemovedEvent( aggregate *eventstore.Aggregate, userID string, ) *MemberRemovedEvent { - return &MemberRemovedEvent{ MemberRemovedEvent: *member.NewRemovedEvent( eventstore.NewBaseEventForPush( @@ -109,3 +109,33 @@ func MemberRemovedEventMapper(event *repository.Event) (eventstore.EventReader, return &MemberRemovedEvent{MemberRemovedEvent: *e.(*member.MemberRemovedEvent)}, nil } + +type MemberCascadeRemovedEvent struct { + member.MemberCascadeRemovedEvent +} + +func NewMemberCascadeRemovedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + userID string, +) *MemberCascadeRemovedEvent { + return &MemberCascadeRemovedEvent{ + MemberCascadeRemovedEvent: *member.NewCascadeRemovedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + MemberCascadeRemovedEventType, + ), + userID, + ), + } +} + +func MemberCascadeRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e, err := member.CascadeRemovedEventMapper(event) + if err != nil { + return nil, err + } + + return &MemberCascadeRemovedEvent{MemberCascadeRemovedEvent: *e.(*member.MemberCascadeRemovedEvent)}, nil +} diff --git a/internal/repository/project/eventstore.go b/internal/repository/project/eventstore.go index e57910e2a9..5876a535c1 100644 --- a/internal/repository/project/eventstore.go +++ b/internal/repository/project/eventstore.go @@ -13,6 +13,7 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(MemberAddedType, MemberAddedEventMapper). RegisterFilterEventMapper(MemberChangedType, MemberChangedEventMapper). RegisterFilterEventMapper(MemberRemovedType, MemberRemovedEventMapper). + RegisterFilterEventMapper(MemberCascadeRemovedType, MemberCascadeRemovedEventMapper). RegisterFilterEventMapper(RoleAddedType, RoleAddedEventMapper). RegisterFilterEventMapper(RoleChangedType, RoleChangedEventMapper). RegisterFilterEventMapper(RoleRemovedType, RoleRemovedEventMapper). @@ -25,6 +26,7 @@ func RegisterEventMappers(es *eventstore.Eventstore) { RegisterFilterEventMapper(GrantMemberAddedType, GrantMemberAddedEventMapper). RegisterFilterEventMapper(GrantMemberChangedType, GrantMemberChangedEventMapper). RegisterFilterEventMapper(GrantMemberRemovedType, GrantMemberRemovedEventMapper). + RegisterFilterEventMapper(GrantMemberCascadeRemovedType, GrantMemberCascadeRemovedEventMapper). RegisterFilterEventMapper(ApplicationAddedType, ApplicationAddedEventMapper). RegisterFilterEventMapper(ApplicationChangedType, ApplicationChangedEventMapper). RegisterFilterEventMapper(ApplicationRemovedType, ApplicationRemovedEventMapper). diff --git a/internal/repository/project/grant_member.go b/internal/repository/project/grant_member.go index 30582d2fd7..b13461415e 100644 --- a/internal/repository/project/grant_member.go +++ b/internal/repository/project/grant_member.go @@ -12,10 +12,11 @@ import ( ) var ( - UniqueProjectGrantMemberType = "project_grant_member" - GrantMemberAddedType = grantEventTypePrefix + member.AddedEventType - GrantMemberChangedType = grantEventTypePrefix + member.ChangedEventType - GrantMemberRemovedType = grantEventTypePrefix + member.RemovedEventType + UniqueProjectGrantMemberType = "project_grant_member" + GrantMemberAddedType = grantEventTypePrefix + member.AddedEventType + GrantMemberChangedType = grantEventTypePrefix + member.ChangedEventType + GrantMemberRemovedType = grantEventTypePrefix + member.RemovedEventType + GrantMemberCascadeRemovedType = grantEventTypePrefix + member.CascadeRemovedEventType ) func NewAddProjectGrantMemberUniqueConstraint(projectID, userID, grantID string) *eventstore.EventUniqueConstraint { @@ -172,3 +173,48 @@ func GrantMemberRemovedEventMapper(event *repository.Event) (eventstore.EventRea return e, nil } + +type GrantMemberCascadeRemovedEvent struct { + eventstore.BaseEvent `json:"-"` + + UserID string `json:"userId"` + GrantID string `json:"grantId"` +} + +func (e *GrantMemberCascadeRemovedEvent) Data() interface{} { + return e +} + +func (e *GrantMemberCascadeRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { + return []*eventstore.EventUniqueConstraint{NewRemoveProjectGrantMemberUniqueConstraint(e.Aggregate().ID, e.UserID, e.GrantID)} +} + +func NewProjectGrantMemberCascadeRemovedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + userID, + grantID string, +) *GrantMemberCascadeRemovedEvent { + return &GrantMemberCascadeRemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + aggregate, + GrantMemberCascadeRemovedType, + ), + UserID: userID, + GrantID: grantID, + } +} + +func GrantMemberCascadeRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &GrantMemberCascadeRemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "PROJECT-3kfs3", "unable to unmarshal label policy") + } + + return e, nil +} diff --git a/internal/repository/project/member.go b/internal/repository/project/member.go index 03bb6031e0..8e9849e8cc 100644 --- a/internal/repository/project/member.go +++ b/internal/repository/project/member.go @@ -9,9 +9,10 @@ import ( ) var ( - MemberAddedType = projectEventTypePrefix + member.AddedEventType - MemberChangedType = projectEventTypePrefix + member.ChangedEventType - MemberRemovedType = projectEventTypePrefix + member.RemovedEventType + MemberAddedType = projectEventTypePrefix + member.AddedEventType + MemberChangedType = projectEventTypePrefix + member.ChangedEventType + MemberRemovedType = projectEventTypePrefix + member.RemovedEventType + MemberCascadeRemovedType = projectEventTypePrefix + member.CascadeRemovedEventType ) type MemberAddedEvent struct { @@ -109,3 +110,34 @@ func MemberRemovedEventMapper(event *repository.Event) (eventstore.EventReader, return &MemberRemovedEvent{MemberRemovedEvent: *e.(*member.MemberRemovedEvent)}, nil } + +type MemberCascadeRemovedEvent struct { + member.MemberCascadeRemovedEvent +} + +func NewProjectMemberCascadeRemovedEvent( + ctx context.Context, + aggregate *eventstore.Aggregate, + userID string, +) *MemberCascadeRemovedEvent { + + return &MemberCascadeRemovedEvent{ + MemberCascadeRemovedEvent: *member.NewCascadeRemovedEvent( + eventstore.NewBaseEventForPush( + ctx, + aggregate, + MemberCascadeRemovedType, + ), + userID, + ), + } +} + +func MemberCascadeRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e, err := member.CascadeRemovedEventMapper(event) + if err != nil { + return nil, err + } + + return &MemberCascadeRemovedEvent{MemberCascadeRemovedEvent: *e.(*member.MemberCascadeRemovedEvent)}, nil +} diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index bbe9c94bfc..3e7e80d8f2 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -547,6 +547,8 @@ EventTypes: added: Organisationsmitglied hinzugefügt changed: Organisationsmitglied geändert removed: Organisationsmitglied entfernt + cascade: + removed: Organisationsmitglied kaskadiert entfernt iam: policy: added: System Richtlinie der Organisation hinzugefügt @@ -626,6 +628,8 @@ EventTypes: added: Projektmitglied hinzugefügt changed: Projektmitglied geändert removed: Projektmitglied entfernt + cascade: + removed: Projektmitglied kaskadiert entfernt role: added: Projektrolle hinzugefügt changed: Projektrolle geändert @@ -642,6 +646,8 @@ EventTypes: added: Verwaltungszugriffsmitglied hinzugefügt changed: Verwaltungszugriffsmitglied geändert removed: Verwaltungszugriffsmitglied entfernt + cascade: + removed: Verwaltungszugriffsmitglied kaskadiert entfernt application: added: Applikation hinzugefügt changed: Applikation geändert @@ -692,6 +698,8 @@ EventTypes: added: ZITADEL Mitglied hinzugefügt changed: ZITADEL Mitglied geändert removed: ZITADEL Mitglied entfernt + cascade: + removed: ZITADEL Mitglied kaskadiert entfernt idp: config: added: IDP Konfiguration hinzugefügt diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index cf03f83e47..ac59f983b7 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -547,6 +547,8 @@ EventTypes: added: Organization member added changed: Organization member changed removed: Organization member removed + cascade: + removeD: Organization member cascade removed iam: policy: added: System policy added @@ -626,6 +628,8 @@ EventTypes: added: Project member added changed: Project member changed removed: Project member removed + cascade: + removeD: Project member cascade removed role: added: Project role added changed: Project role changed @@ -642,6 +646,8 @@ EventTypes: added: Management access member added changed: Management access member changed removed: Management access member removed + cascade: + removeD: Management access cascade removed application: added: Application added changed: Application changed @@ -692,6 +698,8 @@ EventTypes: added: ZITADEL member added changed: ZITADEL member changed removed: ZITADEL member removed + cascade: + removeD: ZITADEL member cascade removed idp: config: added: IDP configuration added diff --git a/internal/user/repository/view/model/user_membership.go b/internal/user/repository/view/model/user_membership.go index 9db1512a14..7d90ea01ad 100644 --- a/internal/user/repository/view/model/user_membership.go +++ b/internal/user/repository/view/model/user_membership.go @@ -71,25 +71,29 @@ func (u *UserMembershipView) AppendEvent(event *models.Event) (err error) { u.setRootData(event, model.MemberTypeIam) err = u.setIamMemberData(event) case iam_es_model.IAMMemberChanged, - iam_es_model.IAMMemberRemoved: + iam_es_model.IAMMemberRemoved, + iam_es_model.IAMMemberCascadeRemoved: err = u.setIamMemberData(event) case org_es_model.OrgMemberAdded: u.setRootData(event, model.MemberTypeOrganisation) err = u.setOrgMemberData(event) case org_es_model.OrgMemberChanged, - org_es_model.OrgMemberRemoved: + org_es_model.OrgMemberRemoved, + org_es_model.OrgMemberCascadeRemoved: err = u.setOrgMemberData(event) case proj_es_model.ProjectMemberAdded: u.setRootData(event, model.MemberTypeProject) err = u.setProjectMemberData(event) case proj_es_model.ProjectMemberChanged, - proj_es_model.ProjectMemberRemoved: + proj_es_model.ProjectMemberRemoved, + proj_es_model.ProjectMemberCascadeRemoved: err = u.setProjectMemberData(event) case proj_es_model.ProjectGrantMemberAdded: u.setRootData(event, model.MemberTypeProjectGrant) err = u.setProjectGrantMemberData(event) case proj_es_model.ProjectGrantMemberChanged, - proj_es_model.ProjectGrantMemberRemoved: + proj_es_model.ProjectGrantMemberRemoved, + proj_es_model.ProjectGrantMemberCascadeRemoved: err = u.setProjectGrantMemberData(event) } return err diff --git a/internal/user/repository/view/usermembership_view.go b/internal/user/repository/view/usermembership_view.go index 47e3e95d40..111d717bff 100644 --- a/internal/user/repository/view/usermembership_view.go +++ b/internal/user/repository/view/usermembership_view.go @@ -35,6 +35,16 @@ func UserMembershipsByAggregateID(db *gorm.DB, table, aggregateID string) ([]*mo return memberships, err } +func UserMembershipsByUserID(db *gorm.DB, table, userID string) ([]*model.UserMembershipView, error) { + memberships := make([]*model.UserMembershipView, 0) + aggregateIDQuery := &usr_model.UserMembershipSearchQuery{Key: usr_model.UserMembershipSearchKeyUserID, Value: userID, Method: domain.SearchMethodEquals} + query := repository.PrepareSearchQuery(table, model.UserMembershipSearchRequest{ + Queries: []*usr_model.UserMembershipSearchQuery{aggregateIDQuery}, + }) + _, err := query(db, &memberships) + return memberships, err +} + func UserMembershipsByResourceOwner(db *gorm.DB, table, resourceOwner string) ([]*model.UserMembershipView, error) { memberships := make([]*model.UserMembershipView, 0) aggregateIDQuery := &usr_model.UserMembershipSearchQuery{Key: usr_model.UserMembershipSearchKeyResourceOwner, Value: resourceOwner, Method: domain.SearchMethodEquals}