From f05c5bae24ec483da824030c4d3cca3726a392dd Mon Sep 17 00:00:00 2001 From: Fabi <38692350+fgerschwiler@users.noreply.github.com> Date: Tue, 25 Aug 2020 16:08:51 +0200 Subject: [PATCH] fix: memberships (#633) * feat: add iam members to memberships * fix: search project grants * fix: rename --- .../project_grant_member_converter.go | 14 +++++--- internal/api/grpc/management/user.go | 2 -- .../eventsourcing/eventstore/permissions.go | 1 + .../eventsourcing/eventstore/user.go | 22 +++++++----- .../eventsourcing/handler/user_membership.go | 35 ++++++++++++++++++- .../repository/eventsourcing/repository.go | 2 +- .../view/project_grant_member_view.go | 14 ++++---- internal/user/model/user_membership_view.go | 5 +-- .../repository/view/model/user_membership.go | 17 +++++++++ 9 files changed, 86 insertions(+), 26 deletions(-) diff --git a/internal/api/grpc/management/project_grant_member_converter.go b/internal/api/grpc/management/project_grant_member_converter.go index 1a3098f7a6..87fa6a2fef 100644 --- a/internal/api/grpc/management/project_grant_member_converter.go +++ b/internal/api/grpc/management/project_grant_member_converter.go @@ -2,6 +2,7 @@ package management import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/model" "github.com/golang/protobuf/ptypes" "github.com/caos/zitadel/internal/eventstore/models" @@ -47,12 +48,15 @@ func projectGrantMemberChangeToModel(member *management.ProjectGrantMemberChange } } -func projectGrantMemberSearchRequestsToModel(role *management.ProjectGrantMemberSearchRequest) *proj_model.ProjectGrantMemberSearchRequest { - return &proj_model.ProjectGrantMemberSearchRequest{ - Offset: role.Offset, - Limit: role.Limit, - Queries: projectGrantMemberSearchQueriesToModel(role.Queries), +func projectGrantMemberSearchRequestsToModel(memberSearch *management.ProjectGrantMemberSearchRequest) *proj_model.ProjectGrantMemberSearchRequest { + request := &proj_model.ProjectGrantMemberSearchRequest{ + Offset: memberSearch.Offset, + Limit: memberSearch.Limit, + Queries: projectGrantMemberSearchQueriesToModel(memberSearch.Queries), } + request.Queries = append(request.Queries, &proj_model.ProjectGrantMemberSearchQuery{Key: proj_model.ProjectGrantMemberSearchKeyProjectID, Method: model.SearchMethodEquals, Value: memberSearch.ProjectId}) + request.Queries = append(request.Queries, &proj_model.ProjectGrantMemberSearchQuery{Key: proj_model.ProjectGrantMemberSearchKeyGrantID, Method: model.SearchMethodEquals, Value: memberSearch.GrantId}) + return request } func projectGrantMemberSearchQueriesToModel(queries []*management.ProjectGrantMemberSearchQuery) []*proj_model.ProjectGrantMemberSearchQuery { diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go index 5aaccb8aeb..ad6174704f 100644 --- a/internal/api/grpc/management/user.go +++ b/internal/api/grpc/management/user.go @@ -2,7 +2,6 @@ package management import ( "context" - "github.com/caos/zitadel/internal/api/authz" "github.com/golang/protobuf/ptypes/empty" @@ -198,7 +197,6 @@ func (s *Server) GetUserMfas(ctx context.Context, userID *management.UserID) (*m func (s *Server) SearchUserMemberships(ctx context.Context, in *management.UserMembershipSearchRequest) (*management.UserMembershipSearchResponse, error) { request := userMembershipSearchRequestsToModel(in) - request.AppendResourceOwnerQuery(authz.GetCtxData(ctx).OrgID) request.AppendUserIDQuery(in.UserId) response, err := s.user.SearchUserMemberships(ctx, request) if err != nil { diff --git a/internal/management/repository/eventsourcing/eventstore/permissions.go b/internal/management/repository/eventsourcing/eventstore/permissions.go index e111d038de..14f3981e46 100644 --- a/internal/management/repository/eventsourcing/eventstore/permissions.go +++ b/internal/management/repository/eventsourcing/eventstore/permissions.go @@ -3,6 +3,7 @@ package eventstore const ( projectReadPerm = "project.read" orgMemberReadPerm = "org.member.read" + iamMemberReadPerm = "iam.member.read" projectMemberReadPerm = "project.member.read" projectGrantMemberReadPerm = "project.member.read" ) diff --git a/internal/management/repository/eventsourcing/eventstore/user.go b/internal/management/repository/eventsourcing/eventstore/user.go index d962fa9a16..765444888b 100644 --- a/internal/management/repository/eventsourcing/eventstore/user.go +++ b/internal/management/repository/eventsourcing/eventstore/user.go @@ -2,6 +2,7 @@ package eventstore import ( "context" + "github.com/caos/zitadel/internal/config/systemdefaults" caos_errs "github.com/caos/zitadel/internal/errors" global_model "github.com/caos/zitadel/internal/model" "github.com/caos/zitadel/internal/view/repository" @@ -18,11 +19,12 @@ import ( ) type UserRepo struct { - SearchLimit uint64 - UserEvents *usr_event.UserEventstore - PolicyEvents *policy_event.PolicyEventstore - OrgEvents *org_event.OrgEventstore - View *view.View + SearchLimit uint64 + UserEvents *usr_event.UserEventstore + PolicyEvents *policy_event.PolicyEventstore + OrgEvents *org_event.OrgEventstore + View *view.View + SystemDefaults systemdefaults.SystemDefaults } func (repo *UserRepo) UserByID(ctx context.Context, id string) (*usr_model.UserView, error) { @@ -220,6 +222,7 @@ func (repo *UserRepo) ChangeAddress(ctx context.Context, address *usr_model.Addr func (repo *UserRepo) SearchUserMemberships(ctx context.Context, request *usr_model.UserMembershipSearchRequest) (*usr_model.UserMembershipSearchResponse, error) { request.EnsureLimit(repo.SearchLimit) + request.AppendResourceOwnerAndIamQuery(authz.GetCtxData(ctx).OrgID, repo.SystemDefaults.IamID) sequence, err := repo.View.GetLatestUserMembershipSequence() logging.Log("EVENT-Dn7sf").OnError(err).Warn("could not read latest user sequence") @@ -235,7 +238,7 @@ func (repo *UserRepo) SearchUserMemberships(ctx context.Context, request *usr_mo result = &usr_model.UserMembershipSearchResponse{ Offset: request.Offset, Limit: request.Limit, - TotalResult: uint64(count), + TotalResult: count, Result: model.UserMembershipsToModel(memberships), } if err == nil { @@ -247,13 +250,16 @@ func (repo *UserRepo) SearchUserMemberships(ctx context.Context, request *usr_mo func handleSearchUserMembershipsPermissions(ctx context.Context, request *usr_model.UserMembershipSearchRequest, sequence *repository.CurrentSequence) *usr_model.UserMembershipSearchResponse { permissions := authz.GetAllPermissionsFromCtx(ctx) + iamPerm := authz.HasGlobalExplicitPermission(permissions, iamMemberReadPerm) orgPerm := authz.HasGlobalExplicitPermission(permissions, orgMemberReadPerm) projectPerm := authz.HasGlobalExplicitPermission(permissions, projectMemberReadPerm) projectGrantPerm := authz.HasGlobalExplicitPermission(permissions, projectGrantMemberReadPerm) - if orgPerm && projectPerm && projectGrantPerm { + if iamPerm && orgPerm && projectPerm && projectGrantPerm { return nil } - + if !iamPerm { + request.Queries = append(request.Queries, &usr_model.UserMembershipSearchQuery{Key: usr_model.UserMembershipSearchKeyMemberType, Method: global_model.SearchMethodNotEquals, Value: usr_model.MemberTypeIam}) + } if !orgPerm { request.Queries = append(request.Queries, &usr_model.UserMembershipSearchQuery{Key: usr_model.UserMembershipSearchKeyMemberType, Method: global_model.SearchMethodNotEquals, Value: usr_model.MemberTypeOrganisation}) } diff --git a/internal/management/repository/eventsourcing/handler/user_membership.go b/internal/management/repository/eventsourcing/handler/user_membership.go index 10c2668ce4..dcb127197c 100644 --- a/internal/management/repository/eventsourcing/handler/user_membership.go +++ b/internal/management/repository/eventsourcing/handler/user_membership.go @@ -2,6 +2,7 @@ package handler import ( "context" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" org_model "github.com/caos/zitadel/internal/org/model" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" @@ -37,12 +38,14 @@ func (m *UserMembership) EventQuery() (*models.SearchQuery, error) { return nil, err } return es_models.NewSearchQuery(). - AggregateTypeFilter(org_es_model.OrgAggregate, proj_es_model.ProjectAggregate). + AggregateTypeFilter(iam_es_model.IamAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate). LatestSequenceFilter(sequence.CurrentSequence), nil } func (m *UserMembership) Reduce(event *models.Event) (err error) { switch event.AggregateType { + case iam_es_model.IamAggregate: + err = m.processIam(event) case org_es_model.OrgAggregate: err = m.processOrg(event) case proj_es_model.ProjectAggregate: @@ -51,6 +54,36 @@ func (m *UserMembership) Reduce(event *models.Event) (err error) { return err } +func (m *UserMembership) processIam(event *models.Event) (err error) { + member := new(usr_es_model.UserMembershipView) + err = member.AppendEvent(event) + if err != nil { + return err + } + switch event.Type { + case iam_es_model.IamMemberAdded: + m.fillIamDisplayName(member) + case iam_es_model.IamMemberChanged: + member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam) + if err != nil { + return err + } + err = member.AppendEvent(event) + case iam_es_model.IamMemberRemoved: + return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event.Sequence) + default: + return m.view.ProcessedUserMembershipSequence(event.Sequence) + } + if err != nil { + return err + } + return m.view.PutUserMembership(member, event.Sequence) +} + +func (m *UserMembership) fillIamDisplayName(member *usr_es_model.UserMembershipView) { + member.DisplayName = member.AggregateID +} + func (m *UserMembership) processOrg(event *models.Event) (err error) { member := new(usr_es_model.UserMembershipView) err = member.AppendEvent(event) diff --git a/internal/management/repository/eventsourcing/repository.go b/internal/management/repository/eventsourcing/repository.go index a1c323a129..36a8fe05d9 100644 --- a/internal/management/repository/eventsourcing/repository.go +++ b/internal/management/repository/eventsourcing/repository.go @@ -97,7 +97,7 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, roles []string) (*EsRe spooler: spool, OrgRepository: eventstore.OrgRepository{conf.SearchLimit, org, user, view, roles}, ProjectRepo: eventstore.ProjectRepo{es, conf.SearchLimit, project, usergrant, user, view, roles}, - UserRepo: eventstore.UserRepo{conf.SearchLimit, user, policy, org, view}, + UserRepo: eventstore.UserRepo{conf.SearchLimit, user, policy, org, view, systemDefaults}, UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, usergrant, view}, PolicyRepo: eventstore.PolicyRepo{policy}, IamRepository: eventstore.IamRepository{iam}, diff --git a/internal/project/repository/view/project_grant_member_view.go b/internal/project/repository/view/project_grant_member_view.go index 15ce6cda03..b78dc0c35d 100644 --- a/internal/project/repository/view/project_grant_member_view.go +++ b/internal/project/repository/view/project_grant_member_view.go @@ -10,22 +10,22 @@ import ( ) func ProjectGrantMemberByIDs(db *gorm.DB, table, grantID, userID string) (*model.ProjectGrantMemberView, error) { - role := new(model.ProjectGrantMemberView) + grant := new(model.ProjectGrantMemberView) grantIDQuery := model.ProjectGrantMemberSearchQuery{Key: proj_model.ProjectGrantMemberSearchKeyGrantID, Value: grantID, Method: global_model.SearchMethodEquals} userIDQuery := model.ProjectGrantMemberSearchQuery{Key: proj_model.ProjectGrantMemberSearchKeyUserID, Value: userID, Method: global_model.SearchMethodEquals} query := repository.PrepareGetByQuery(table, grantIDQuery, userIDQuery) - err := query(db, role) + err := query(db, grant) if caos_errs.IsNotFound(err) { return nil, caos_errs.ThrowNotFound(nil, "VIEW-Sgr32", "Errors.Project.MemberNotExisting") } - return role, err + return grant, err } func ProjectGrantMembersByProjectID(db *gorm.DB, table, projectID string) ([]*model.ProjectGrantMemberView, error) { members := make([]*model.ProjectGrantMemberView, 0) queries := []*proj_model.ProjectGrantMemberSearchQuery{ - &proj_model.ProjectGrantMemberSearchQuery{Key: proj_model.ProjectGrantMemberSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectGrantMemberSearchKeyProjectID, Value: projectID, Method: global_model.SearchMethodEquals}, } query := repository.PrepareSearchQuery(table, model.ProjectGrantMemberSearchRequest{Queries: queries}) _, err := query(db, &members) @@ -48,7 +48,7 @@ func SearchProjectGrantMembers(db *gorm.DB, table string, req *proj_model.Projec func ProjectGrantMembersByUserID(db *gorm.DB, table, userID string) ([]*model.ProjectGrantMemberView, error) { members := make([]*model.ProjectGrantMemberView, 0) queries := []*proj_model.ProjectGrantMemberSearchQuery{ - &proj_model.ProjectGrantMemberSearchQuery{Key: proj_model.ProjectGrantMemberSearchKeyUserID, Value: userID, Method: global_model.SearchMethodEquals}, + {Key: proj_model.ProjectGrantMemberSearchKeyUserID, Value: userID, Method: global_model.SearchMethodEquals}, } query := repository.PrepareSearchQuery(table, model.ProjectGrantMemberSearchRequest{Queries: queries}) _, err := query(db, &members) @@ -64,11 +64,11 @@ func PutProjectGrantMember(db *gorm.DB, table string, role *model.ProjectGrantMe } func DeleteProjectGrantMember(db *gorm.DB, table, grantID, userID string) error { - role, err := ProjectGrantMemberByIDs(db, table, grantID, userID) + grant, err := ProjectGrantMemberByIDs(db, table, grantID, userID) if err != nil { return err } - delete := repository.PrepareDeleteByObject(table, role) + delete := repository.PrepareDeleteByObject(table, grant) return delete(db) } diff --git a/internal/user/model/user_membership_view.go b/internal/user/model/user_membership_view.go index 03d8485ed7..1c86ed8928 100644 --- a/internal/user/model/user_membership_view.go +++ b/internal/user/model/user_membership_view.go @@ -28,6 +28,7 @@ const ( MemberTypeOrganisation MemberTypeProject MemberTypeProjectGrant + MemberTypeIam ) type UserMembershipSearchRequest struct { @@ -79,8 +80,8 @@ func (r *UserMembershipSearchRequest) GetSearchQuery(key UserMembershipSearchKey return -1, nil } -func (r *UserMembershipSearchRequest) AppendResourceOwnerQuery(orgID string) { - r.Queries = append(r.Queries, &UserMembershipSearchQuery{Key: UserMembershipSearchKeyResourceOwner, Method: model.SearchMethodEquals, Value: orgID}) +func (r *UserMembershipSearchRequest) AppendResourceOwnerAndIamQuery(orgID, iamID string) { + r.Queries = append(r.Queries, &UserMembershipSearchQuery{Key: UserMembershipSearchKeyResourceOwner, Method: model.SearchMethodIsOneOf, Value: []string{orgID, iamID}}) } func (r *UserMembershipSearchRequest) AppendUserIDQuery(userID string) { diff --git a/internal/user/repository/view/model/user_membership.go b/internal/user/repository/view/model/user_membership.go index ab6ad06f7d..a0383b1cdd 100644 --- a/internal/user/repository/view/model/user_membership.go +++ b/internal/user/repository/view/model/user_membership.go @@ -5,6 +5,7 @@ import ( "github.com/caos/logging" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" + iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" "github.com/caos/zitadel/internal/user/model" @@ -77,6 +78,11 @@ func (u *UserMembershipView) AppendEvent(event *models.Event) (err error) { u.Sequence = event.Sequence switch event.Type { + case iam_es_model.IamMemberAdded: + u.setRootData(event, model.MemberTypeIam) + err = u.setIamMemberData(event) + case iam_es_model.IamMemberChanged: + err = u.setIamMemberData(event) case org_es_model.OrgMemberAdded: u.setRootData(event, model.MemberTypeOrganisation) err = u.setOrgMemberData(event) @@ -104,6 +110,17 @@ func (u *UserMembershipView) setRootData(event *models.Event, memberType model.M u.MemberType = int32(memberType) } +func (u *UserMembershipView) setIamMemberData(event *models.Event) error { + member := new(iam_es_model.IamMember) + if err := json.Unmarshal(event.Data, member); err != nil { + logging.Log("MODEL-Ec9sf").WithError(err).Error("could not unmarshal event data") + return caos_errs.ThrowInternal(nil, "MODEL-6jhsw", "could not unmarshal data") + } + u.UserID = member.UserID + u.Roles = member.Roles + return nil +} + func (u *UserMembershipView) setOrgMemberData(event *models.Event) error { member := new(org_es_model.OrgMember) if err := json.Unmarshal(event.Data, member); err != nil {