feat: delete (#243)

* feat: project role remove

* feat: search queries

* feat: search queries

* feat: cascade remove/change project role

* fix: comment in project grant

* fix: remove projecr grant

* fix: only search usergrants of my org

* fix: delete usergrants

* fix: delete usergrants

* fix: check if role exists on project grant

* feat: bulk add project role

* fix: tests

* fix: update user grants on project update

* fix: return roles

* feat: add resourceowner name on project grants

* fix: migration number

* fix: tests

* fix: generate protos

* fix: some unnecessary code
This commit is contained in:
Fabi 2020-06-19 15:32:03 +02:00 committed by GitHub
parent 8f49f2c2d8
commit 710652ef24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 4404 additions and 2668 deletions

View File

@ -14,11 +14,13 @@ InternalAuthZ:
- "user.delete" - "user.delete"
- "user.grant.read" - "user.grant.read"
- "user.grant.write" - "user.grant.write"
- "user.grant.delete"
- "policy.read" - "policy.read"
- "policy.write" - "policy.write"
- "policy.delete" - "policy.delete"
- "project.read" - "project.read"
- "project.write" - "project.write"
- "project.delete"
- "project.member.read" - "project.member.read"
- "project.member.write" - "project.member.write"
- "project.member.delete" - "project.member.delete"
@ -27,8 +29,10 @@ InternalAuthZ:
- "project.role.delete" - "project.role.delete"
- "project.app.read" - "project.app.read"
- "project.app.write" - "project.app.write"
- "project.app.delete"
- "project.grant.read" - "project.grant.read"
- "project.grant.write" - "project.grant.write"
- "project.grant.delete"
- "project.grant.member.read" - "project.grant.member.read"
- "project.grant.member.write" - "project.grant.member.write"
- "project.grant.member.delete" - "project.grant.member.delete"
@ -47,11 +51,13 @@ InternalAuthZ:
- "user.delete" - "user.delete"
- "user.grant.read" - "user.grant.read"
- "user.grant.write" - "user.grant.write"
- "user.grant.delete"
- "policy.read" - "policy.read"
- "policy.write" - "policy.write"
- "policy.delete" - "policy.delete"
- "project.read" - "project.read"
- "project.write" - "project.write"
- "project.delete"
- "project.member.read" - "project.member.read"
- "project.member.write" - "project.member.write"
- "project.member.delete" - "project.member.delete"
@ -62,6 +68,7 @@ InternalAuthZ:
- "project.app.write" - "project.app.write"
- "project.grant.read" - "project.grant.read"
- "project.grant.write" - "project.grant.write"
- "project.grant.delete"
- "project.grant.member.read" - "project.grant.member.read"
- "project.grant.member.write" - "project.grant.member.write"
- "project.grant.member.delete" - "project.grant.member.delete"
@ -98,8 +105,10 @@ InternalAuthZ:
- "project.role.delete" - "project.role.delete"
- "project.app.read" - "project.app.read"
- "project.app.write" - "project.app.write"
- "project.app.delete"
- "project.grant.read" - "project.grant.read"
- "project.grant.write" - "project.grant.write"
- "project.grant.delete"
- "project.grant.member.read" - "project.grant.member.read"
- "project.grant.member.write" - "project.grant.member.write"
- "project.grant.member.delete" - "project.grant.member.delete"
@ -116,6 +125,7 @@ InternalAuthZ:
- "project.read" - "project.read"
- "project.member.read" - "project.member.read"
- "project.member.write" - "project.member.write"
- "project.member.delete"
- "project.grant.member.delete" - "project.grant.member.delete"
- Role: 'ORG_PROJECT_MEMBER_VIEWER' - Role: 'ORG_PROJECT_MEMBER_VIEWER'
Permissions: Permissions:
@ -136,6 +146,7 @@ InternalAuthZ:
- "project.read" - "project.read"
- "project.app.read" - "project.app.read"
- "project.app.write" - "project.app.write"
- "project.app.delete"
- Role: 'ORG_PROJECT_APP_VIEWER' - Role: 'ORG_PROJECT_APP_VIEWER'
Permissions: Permissions:
- "project.read" - "project.read"
@ -177,6 +188,7 @@ InternalAuthZ:
- "user.read" - "user.read"
- "user.grant.read" - "user.grant.read"
- "user.grant.write" - "user.grant.write"
- "user.grant.delete"
- "project.read" - "project.read"
- Role: 'ORG_USER_GRANT_VIEWER' - Role: 'ORG_USER_GRANT_VIEWER'
Permissions: Permissions:
@ -194,6 +206,7 @@ InternalAuthZ:
Permissions: Permissions:
- "project.read" - "project.read"
- "project.write" - "project.write"
- "project.delete"
- "project.member.read" - "project.member.read"
- "project.member.write" - "project.member.write"
- "project.member.delete" - "project.member.delete"
@ -202,8 +215,10 @@ InternalAuthZ:
- "project.role.delete" - "project.role.delete"
- "project.app.read" - "project.app.read"
- "project.app.write" - "project.app.write"
- "project.app.delete"
- "project.grant.read" - "project.grant.read"
- "project.grant.write" - "project.grant.write"
- "project.grant.delete"
- "project.grant.member.read" - "project.grant.member.read"
- "project.grant.member.write" - "project.grant.member.write"
- "project.grant.member.delete" - "project.grant.member.delete"
@ -240,6 +255,7 @@ InternalAuthZ:
- "project.read" - "project.read"
- "project.grant.read" - "project.grant.read"
- "project.grant.write" - "project.grant.write"
- "project.grant.delete"
- Role: 'PROJECT_GRANT_VIEWER' - Role: 'PROJECT_GRANT_VIEWER'
Permissions: Permissions:
- "project.read" - "project.read"

View File

@ -89,6 +89,7 @@ func (u *UserGrant) processUserGrant(event *models.Event) (err error) {
} }
err = u.fillData(grant, event.ResourceOwner) err = u.fillData(grant, event.ResourceOwner)
case grant_es_model.UserGrantChanged, case grant_es_model.UserGrantChanged,
grant_es_model.UserGrantCascadeChanged,
grant_es_model.UserGrantDeactivated, grant_es_model.UserGrantDeactivated,
grant_es_model.UserGrantReactivated: grant_es_model.UserGrantReactivated:
grant, err = u.view.UserGrantByID(event.AggregateID) grant, err = u.view.UserGrantByID(event.AggregateID)
@ -96,8 +97,8 @@ func (u *UserGrant) processUserGrant(event *models.Event) (err error) {
return err return err
} }
err = grant.AppendEvent(event) err = grant.AppendEvent(event)
case grant_es_model.UserGrantRemoved: case grant_es_model.UserGrantRemoved, grant_es_model.UserGrantCascadeRemoved:
err = u.view.DeleteUserGrant(event.AggregateID, event.Sequence) return u.view.DeleteUserGrant(event.AggregateID, event.Sequence)
default: default:
return u.view.ProcessedUserGrantSequence(event.Sequence) return u.view.ProcessedUserGrantSequence(event.Sequence)
} }
@ -204,7 +205,6 @@ func (u *UserGrant) processIamMember(event *models.Event, rolePrefix string, suf
} else { } else {
grant.RoleKeys = newRoles grant.RoleKeys = newRoles
} }
} }
grant.Sequence = event.Sequence grant.Sequence = event.Sequence
grant.ChangeDate = event.CreationDate grant.ChangeDate = event.CreationDate

View File

@ -123,7 +123,6 @@ func (u *UserGrant) processIamMember(event *models.Event, rolePrefix string, suf
} else { } else {
grant.RoleKeys = newRoles grant.RoleKeys = newRoles
} }
} }
grant.Sequence = event.Sequence grant.Sequence = event.Sequence
grant.ChangeDate = event.CreationDate grant.ChangeDate = event.CreationDate

View File

@ -59,6 +59,9 @@ func PushAggregates(ctx context.Context, push pushFunc, appender appendFunc, agg
if err != nil { if err != nil {
return err return err
} }
if appender == nil {
return nil
}
return appendAggregates(appender, aggregates) return appendAggregates(appender, aggregates)
} }

View File

@ -2,6 +2,14 @@ package eventstore
import ( import (
"context" "context"
caos_errs "github.com/caos/zitadel/internal/errors"
es_int "github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
es_proj_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
usr_grant_model "github.com/caos/zitadel/internal/usergrant/model"
usr_grant_event "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing"
"strings" "strings"
"github.com/caos/zitadel/internal/api/auth" "github.com/caos/zitadel/internal/api/auth"
@ -15,10 +23,12 @@ import (
) )
type ProjectRepo struct { type ProjectRepo struct {
SearchLimit uint64 es_int.Eventstore
ProjectEvents *proj_event.ProjectEventstore SearchLimit uint64
View *view.View ProjectEvents *proj_event.ProjectEventstore
Roles []string UserGrantEvents *usr_grant_event.UserGrantEventStore
View *view.View
Roles []string
} }
func (repo *ProjectRepo) ProjectByID(ctx context.Context, id string) (project *proj_model.Project, err error) { func (repo *ProjectRepo) ProjectByID(ctx context.Context, id string) (project *proj_model.Project, err error) {
@ -48,7 +58,7 @@ func (repo *ProjectRepo) SearchProjects(ctx context.Context, request *proj_model
permissions := auth.GetPermissionsFromCtx(ctx) permissions := auth.GetPermissionsFromCtx(ctx)
if !auth.HasGlobalPermission(permissions) { if !auth.HasGlobalPermission(permissions) {
ids := auth.GetPermissionCtxIDs(permissions) ids := auth.GetPermissionCtxIDs(permissions)
request.Queries = append(request.Queries, &proj_model.ProjectViewSearchQuery{Key: proj_model.PROJECTSEARCHKEY_PROJECTID, Method: global_model.SEARCHMETHOD_IN, Value: ids}) request.Queries = append(request.Queries, &proj_model.ProjectViewSearchQuery{Key: proj_model.PROJECTSEARCHKEY_PROJECTID, Method: global_model.SEARCHMETHOD_IS_ONE_OF, Value: ids})
} }
projects, count, err := repo.View.SearchProjects(request) projects, count, err := repo.View.SearchProjects(request)
@ -103,8 +113,13 @@ func (repo *ProjectRepo) SearchProjectMembers(ctx context.Context, request *proj
}, nil }, nil
} }
func (repo *ProjectRepo) AddProjectRole(ctx context.Context, member *proj_model.ProjectRole) (*proj_model.ProjectRole, error) { func (repo *ProjectRepo) AddProjectRole(ctx context.Context, role *proj_model.ProjectRole) (*proj_model.ProjectRole, error) {
return repo.ProjectEvents.AddProjectRole(ctx, member) return repo.ProjectEvents.AddProjectRoles(ctx, role)
}
func (repo *ProjectRepo) BulkAddProjectRole(ctx context.Context, roles []*proj_model.ProjectRole) error {
_, err := repo.ProjectEvents.AddProjectRoles(ctx, roles...)
return err
} }
func (repo *ProjectRepo) ChangeProjectRole(ctx context.Context, member *proj_model.ProjectRole) (*proj_model.ProjectRole, error) { func (repo *ProjectRepo) ChangeProjectRole(ctx context.Context, member *proj_model.ProjectRole) (*proj_model.ProjectRole, error) {
@ -112,8 +127,40 @@ func (repo *ProjectRepo) ChangeProjectRole(ctx context.Context, member *proj_mod
} }
func (repo *ProjectRepo) RemoveProjectRole(ctx context.Context, projectID, key string) error { func (repo *ProjectRepo) RemoveProjectRole(ctx context.Context, projectID, key string) error {
member := proj_model.NewProjectRole(projectID, key) role := proj_model.NewProjectRole(projectID, key)
return repo.ProjectEvents.RemoveProjectRole(ctx, member) aggregates := make([]*es_models.Aggregate, 0)
project, agg, err := repo.ProjectEvents.PrepareRemoveProjectRole(ctx, role)
if err != nil {
return err
}
aggregates = append(aggregates, agg)
usergrants, err := repo.View.UserGrantsByProjectIDAndRoleKey(projectID, key)
if err != nil {
return err
}
for _, grant := range usergrants {
changed := &usr_grant_model.UserGrant{
ObjectRoot: models.ObjectRoot{AggregateID: grant.ID, Sequence: grant.Sequence, ResourceOwner: grant.ResourceOwner},
RoleKeys: grant.RoleKeys,
ProjectID: grant.ProjectID,
UserID: grant.UserID,
}
changed.RemoveRoleKeyIfExisting(key)
_, agg, err := repo.UserGrantEvents.PrepareChangeUserGrant(ctx, changed, true)
if err != nil {
return err
}
aggregates = append(aggregates, agg)
}
if err != nil {
return err
}
err = es_sdk.PushAggregates(ctx, repo.Eventstore.PushAggregates, project.AppendEvents, aggregates...)
if err != nil {
return err
}
return nil
} }
func (repo *ProjectRepo) SearchProjectRoles(ctx context.Context, request *proj_model.ProjectRoleSearchRequest) (*proj_model.ProjectRoleSearchResponse, error) { func (repo *ProjectRepo) SearchProjectRoles(ctx context.Context, request *proj_model.ProjectRoleSearchRequest) (*proj_model.ProjectRoleSearchResponse, error) {
@ -211,25 +258,100 @@ func (repo *ProjectRepo) SearchProjectGrants(ctx context.Context, request *proj_
}, nil }, nil
} }
func (repo *ProjectRepo) AddProjectGrant(ctx context.Context, app *proj_model.ProjectGrant) (*proj_model.ProjectGrant, error) { func (repo *ProjectRepo) AddProjectGrant(ctx context.Context, grant *proj_model.ProjectGrant) (*proj_model.ProjectGrant, error) {
return repo.ProjectEvents.AddProjectGrant(ctx, app) return repo.ProjectEvents.AddProjectGrant(ctx, grant)
} }
func (repo *ProjectRepo) ChangeProjectGrant(ctx context.Context, app *proj_model.ProjectGrant) (*proj_model.ProjectGrant, error) { func (repo *ProjectRepo) ChangeProjectGrant(ctx context.Context, grant *proj_model.ProjectGrant) (*proj_model.ProjectGrant, error) {
return repo.ProjectEvents.ChangeProjectGrant(ctx, app) project, aggFunc, removedRoles, err := repo.ProjectEvents.PrepareChangeProjectGrant(ctx, grant)
if err != nil {
return nil, err
}
agg, err := aggFunc(ctx)
if err != nil {
return nil, err
}
aggregates := make([]*es_models.Aggregate, 0)
aggregates = append(aggregates, agg)
usergrants, err := repo.View.UserGrantsByProjectID(grant.AggregateID)
if err != nil {
return nil, err
}
for _, grant := range usergrants {
changed := &usr_grant_model.UserGrant{
ObjectRoot: models.ObjectRoot{AggregateID: grant.ID, Sequence: grant.Sequence, ResourceOwner: grant.ResourceOwner},
RoleKeys: grant.RoleKeys,
ProjectID: grant.ProjectID,
UserID: grant.UserID,
}
existing := changed.RemoveRoleKeysIfExisting(removedRoles)
if existing {
_, agg, err := repo.UserGrantEvents.PrepareChangeUserGrant(ctx, changed, true)
if err != nil {
return nil, err
}
aggregates = append(aggregates, agg)
}
}
if err != nil {
return nil, err
}
err = es_sdk.PushAggregates(ctx, repo.Eventstore.PushAggregates, project.AppendEvents, aggregates...)
if err != nil {
return nil, err
}
if _, g := es_proj_model.GetProjectGrant(project.Grants, grant.GrantID); g != nil {
return es_proj_model.GrantToModel(g), nil
}
return nil, caos_errs.ThrowInternal(nil, "EVENT-dksi8", "Could not find app in list")
} }
func (repo *ProjectRepo) DeactivateProjectGrant(ctx context.Context, projectID, appID string) (*proj_model.ProjectGrant, error) { func (repo *ProjectRepo) DeactivateProjectGrant(ctx context.Context, projectID, grantID string) (*proj_model.ProjectGrant, error) {
return repo.ProjectEvents.DeactivateProjectGrant(ctx, projectID, appID) return repo.ProjectEvents.DeactivateProjectGrant(ctx, projectID, grantID)
} }
func (repo *ProjectRepo) ReactivateProjectGrant(ctx context.Context, projectID, appID string) (*proj_model.ProjectGrant, error) { func (repo *ProjectRepo) ReactivateProjectGrant(ctx context.Context, projectID, grantID string) (*proj_model.ProjectGrant, error) {
return repo.ProjectEvents.ReactivateProjectGrant(ctx, projectID, appID) return repo.ProjectEvents.ReactivateProjectGrant(ctx, projectID, grantID)
} }
func (repo *ProjectRepo) RemoveProjectGrant(ctx context.Context, projectID, appID string) error { func (repo *ProjectRepo) RemoveProjectGrant(ctx context.Context, projectID, grantID string) error {
app := proj_model.NewProjectGrant(projectID, appID) grant, err := repo.ProjectEvents.ProjectGrantByIDs(ctx, projectID, grantID)
return repo.ProjectEvents.RemoveProjectGrant(ctx, app) if err != nil {
return err
}
aggregates := make([]*es_models.Aggregate, 0)
project, aggFunc, err := repo.ProjectEvents.PrepareRemoveProjectGrant(ctx, grant)
if err != nil {
return err
}
agg, err := aggFunc(ctx)
if err != nil {
return err
}
aggregates = append(aggregates, agg)
usergrants, err := repo.View.UserGrantsByOrgIDAndProjectID(grant.GrantedOrgID, projectID)
if err != nil {
return err
}
for _, grant := range usergrants {
_, grantAggregates, err := repo.UserGrantEvents.PrepareRemoveUserGrant(ctx, grant.ID, true)
if err != nil {
return err
}
for _, agg := range grantAggregates {
aggregates = append(aggregates, agg)
}
}
if err != nil {
return err
}
err = es_sdk.PushAggregates(ctx, repo.Eventstore.PushAggregates, project.AppendEvents, aggregates...)
if err != nil {
return err
}
return nil
} }
func (repo *ProjectRepo) ProjectGrantMemberByID(ctx context.Context, projectID, grantID, userID string) (member *proj_model.ProjectGrantMember, err error) { func (repo *ProjectRepo) ProjectGrantMemberByID(ctx context.Context, projectID, grantID, userID string) (member *proj_model.ProjectGrantMember, err error) {

View File

@ -38,6 +38,18 @@ func (repo *UserGrantRepo) RemoveUserGrant(ctx context.Context, grantID string)
return repo.UserGrantEvents.RemoveUserGrant(ctx, grantID) return repo.UserGrantEvents.RemoveUserGrant(ctx, grantID)
} }
func (repo *UserGrantRepo) BulkAddUserGrant(ctx context.Context, grants ...*grant_model.UserGrant) error {
return repo.UserGrantEvents.AddUserGrants(ctx, grants...)
}
func (repo *UserGrantRepo) BulkChangeUserGrant(ctx context.Context, grants ...*grant_model.UserGrant) error {
return repo.UserGrantEvents.ChangeUserGrants(ctx, grants...)
}
func (repo *UserGrantRepo) BulkRemoveUserGrant(ctx context.Context, grantIDs ...string) error {
return repo.UserGrantEvents.RemoveUserGrants(ctx, grantIDs...)
}
func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) { func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) {
request.EnsureLimit(repo.SearchLimit) request.EnsureLimit(repo.SearchLimit)
grants, count, err := repo.View.SearchUserGrants(request) grants, count, err := repo.View.SearchUserGrants(request)

View File

@ -66,8 +66,12 @@ func (p *ProjectGrant) Process(event *models.Event) (err error) {
if err != nil { if err != nil {
return err return err
} }
p.fillOrgData(grantedProject, org) resourceOwner, err := p.orgEvents.OrgByID(context.TODO(), org_model.NewOrg(grantedProject.ResourceOwner))
case es_model.ProjectGrantChanged: if err != nil {
return err
}
p.fillOrgData(grantedProject, org, resourceOwner)
case es_model.ProjectGrantChanged, es_model.ProjectGrantCascadeChanged:
grant := new(view_model.ProjectGrant) grant := new(view_model.ProjectGrant)
err := grant.SetData(event) err := grant.SetData(event)
if err != nil { if err != nil {
@ -94,8 +98,9 @@ func (p *ProjectGrant) Process(event *models.Event) (err error) {
return p.view.PutProjectGrant(grantedProject) return p.view.PutProjectGrant(grantedProject)
} }
func (p *ProjectGrant) fillOrgData(grantedProject *view_model.ProjectGrantView, org *org_model.Org) { func (p *ProjectGrant) fillOrgData(grantedProject *view_model.ProjectGrantView, org, resourceOwner *org_model.Org) {
grantedProject.OrgName = org.Name grantedProject.OrgName = org.Name
grantedProject.ResourceOwnerName = resourceOwner.Name
} }
func (p *ProjectGrant) getProject(projectID string) (*proj_model.Project, error) { func (p *ProjectGrant) getProject(projectID string) (*proj_model.Project, error) {

View File

@ -56,7 +56,7 @@ func (p *ProjectRole) Process(event *models.Event) (err error) {
if err != nil { if err != nil {
return err return err
} }
err = p.removeRoleFromAllResourceowners(event, role) return p.view.DeleteProjectRole(event.AggregateID, event.ResourceOwner, role.Key, event.Sequence)
case es_model.ProjectGrantAdded: case es_model.ProjectGrantAdded:
return p.addGrantRoles(event) return p.addGrantRoles(event)
case es_model.ProjectGrantChanged: case es_model.ProjectGrantChanged:

View File

@ -73,6 +73,7 @@ func (u *UserGrant) processUserGrant(event *models.Event) (err error) {
} }
err = u.fillData(grant, event.ResourceOwner) err = u.fillData(grant, event.ResourceOwner)
case grant_es_model.UserGrantChanged, case grant_es_model.UserGrantChanged,
grant_es_model.UserGrantCascadeChanged,
grant_es_model.UserGrantDeactivated, grant_es_model.UserGrantDeactivated,
grant_es_model.UserGrantReactivated: grant_es_model.UserGrantReactivated:
grant, err = u.view.UserGrantByID(event.AggregateID) grant, err = u.view.UserGrantByID(event.AggregateID)
@ -80,8 +81,8 @@ func (u *UserGrant) processUserGrant(event *models.Event) (err error) {
return err return err
} }
err = grant.AppendEvent(event) err = grant.AppendEvent(event)
case grant_es_model.UserGrantRemoved: case grant_es_model.UserGrantRemoved, grant_es_model.UserGrantCascadeRemoved:
err = u.view.DeleteUserGrant(event.AggregateID, event.Sequence) return u.view.DeleteUserGrant(event.AggregateID, event.Sequence)
default: default:
return u.view.ProcessedUserGrantSequence(event.Sequence) return u.view.ProcessedUserGrantSequence(event.Sequence)
} }

View File

@ -96,7 +96,7 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, roles []string) (*EsRe
return &EsRepository{ return &EsRepository{
spooler: spool, spooler: spool,
OrgRepository: eventstore.OrgRepository{conf.SearchLimit, org, view, roles}, OrgRepository: eventstore.OrgRepository{conf.SearchLimit, org, view, roles},
ProjectRepo: eventstore.ProjectRepo{conf.SearchLimit, project, view, roles}, ProjectRepo: eventstore.ProjectRepo{es, conf.SearchLimit, project, usergrant, view, roles},
UserRepo: eventstore.UserRepo{conf.SearchLimit, user, policy, org, view}, UserRepo: eventstore.UserRepo{conf.SearchLimit, user, policy, org, view},
UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, usergrant, view}, UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, usergrant, view},
PolicyRepo: eventstore.PolicyRepo{policy}, PolicyRepo: eventstore.PolicyRepo{policy},

View File

@ -23,6 +23,10 @@ func (v *View) ProjectGrantsByProjectID(projectID string) ([]*model.ProjectGrant
return view.ProjectGrantsByProjectID(v.Db, grantedProjectTable, projectID) return view.ProjectGrantsByProjectID(v.Db, grantedProjectTable, projectID)
} }
func (v *View) ProjectGrantsByProjectIDAndRoleKey(projectID, key string) ([]*model.ProjectGrantView, error) {
return view.ProjectGrantsByProjectIDAndRoleKey(v.Db, grantedProjectTable, projectID, key)
}
func (v *View) SearchProjectGrants(request *proj_model.ProjectGrantViewSearchRequest) ([]*model.ProjectGrantView, int, error) { func (v *View) SearchProjectGrants(request *proj_model.ProjectGrantViewSearchRequest) ([]*model.ProjectGrantView, int, error) {
return view.SearchProjectGrants(v.Db, grantedProjectTable, request) return view.SearchProjectGrants(v.Db, grantedProjectTable, request)
} }
@ -38,7 +42,7 @@ func (v *View) PutProjectGrant(project *model.ProjectGrantView) error {
func (v *View) DeleteProjectGrant(grantID string, eventSequence uint64) error { func (v *View) DeleteProjectGrant(grantID string, eventSequence uint64) error {
err := view.DeleteProjectGrant(v.Db, grantedProjectTable, grantID) err := view.DeleteProjectGrant(v.Db, grantedProjectTable, grantID)
if err != nil { if err != nil {
return nil return err
} }
return v.ProcessedProjectGrantSequence(eventSequence) return v.ProcessedProjectGrantSequence(eventSequence)
} }

View File

@ -31,6 +31,14 @@ func (v *View) UserGrantsByOrgID(orgID string) ([]*model.UserGrantView, error) {
return view.UserGrantsByOrgID(v.Db, userGrantTable, orgID) return view.UserGrantsByOrgID(v.Db, userGrantTable, orgID)
} }
func (v *View) UserGrantsByProjectIDAndRoleKey(projectID, roleKey string) ([]*model.UserGrantView, error) {
return view.UserGrantsByProjectIDAndRole(v.Db, userGrantTable, projectID, roleKey)
}
func (v *View) UserGrantsByOrgIDAndProjectID(orgID, projectID string) ([]*model.UserGrantView, error) {
return view.UserGrantsByOrgIDAndProjectID(v.Db, userGrantTable, orgID, projectID)
}
func (v *View) PutUserGrant(grant *model.UserGrantView, sequence uint64) error { func (v *View) PutUserGrant(grant *model.UserGrantView, sequence uint64) error {
err := view.PutUserGrant(v.Db, userGrantTable, grant) err := view.PutUserGrant(v.Db, userGrantTable, grant)
if err != nil { if err != nil {

View File

@ -28,6 +28,7 @@ type ProjectRepository interface {
RemoveProjectRole(ctx context.Context, projectID, key string) error RemoveProjectRole(ctx context.Context, projectID, key string) error
SearchProjectRoles(ctx context.Context, request *model.ProjectRoleSearchRequest) (*model.ProjectRoleSearchResponse, error) SearchProjectRoles(ctx context.Context, request *model.ProjectRoleSearchRequest) (*model.ProjectRoleSearchResponse, error)
ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*model.ProjectChanges, error) ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*model.ProjectChanges, error)
BulkAddProjectRole(ctx context.Context, role []*model.ProjectRole) error
ApplicationByID(ctx context.Context, projectID, appID string) (*model.Application, error) ApplicationByID(ctx context.Context, projectID, appID string) (*model.Application, error)
AddApplication(ctx context.Context, app *model.Application) (*model.Application, error) AddApplication(ctx context.Context, app *model.Application) (*model.Application, error)
@ -41,8 +42,8 @@ type ProjectRepository interface {
ApplicationChanges(ctx context.Context, id string, secId string, lastSequence uint64, limit uint64) (*model.ApplicationChanges, error) ApplicationChanges(ctx context.Context, id string, secId string, lastSequence uint64, limit uint64) (*model.ApplicationChanges, error)
ProjectGrantByID(ctx context.Context, projectID, grantID string) (*model.ProjectGrant, error) ProjectGrantByID(ctx context.Context, projectID, grantID string) (*model.ProjectGrant, error)
AddProjectGrant(ctx context.Context, app *model.ProjectGrant) (*model.ProjectGrant, error) AddProjectGrant(ctx context.Context, grant *model.ProjectGrant) (*model.ProjectGrant, error)
ChangeProjectGrant(ctx context.Context, app *model.ProjectGrant) (*model.ProjectGrant, error) ChangeProjectGrant(ctx context.Context, grant *model.ProjectGrant) (*model.ProjectGrant, error)
DeactivateProjectGrant(ctx context.Context, projectID, grantID string) (*model.ProjectGrant, error) DeactivateProjectGrant(ctx context.Context, projectID, grantID string) (*model.ProjectGrant, error)
ReactivateProjectGrant(ctx context.Context, projectID, grantID string) (*model.ProjectGrant, error) ReactivateProjectGrant(ctx context.Context, projectID, grantID string) (*model.ProjectGrant, error)
RemoveProjectGrant(ctx context.Context, projectID, grantID string) error RemoveProjectGrant(ctx context.Context, projectID, grantID string) error

View File

@ -13,4 +13,8 @@ type UserGrantRepository interface {
ReactivateUserGrant(ctx context.Context, grantID string) (*model.UserGrant, error) ReactivateUserGrant(ctx context.Context, grantID string) (*model.UserGrant, error)
RemoveUserGrant(ctx context.Context, grantID string) error RemoveUserGrant(ctx context.Context, grantID string) error
SearchUserGrants(ctx context.Context, request *model.UserGrantSearchRequest) (*model.UserGrantSearchResponse, error) SearchUserGrants(ctx context.Context, request *model.UserGrantSearchRequest) (*model.UserGrantSearchResponse, error)
BulkAddUserGrant(ctx context.Context, grant ...*model.UserGrant) error
BulkChangeUserGrant(ctx context.Context, grant ...*model.UserGrant) error
BulkRemoveUserGrant(ctx context.Context, grantIDs ...string) error
} }

View File

@ -12,6 +12,6 @@ const (
SEARCHMETHOD_NOT_EQUALS SEARCHMETHOD_NOT_EQUALS
SEARCHMETHOD_GREATER_THAN SEARCHMETHOD_GREATER_THAN
SEARCHMETHOD_LESS_THAN SEARCHMETHOD_LESS_THAN
SEARCHMETHOD_IN SEARCHMETHOD_IS_ONE_OF
SEARCHMETHOD_EQUALS_IN_ARRAY SEARCHMETHOD_LIST_CONTAINS
) )

View File

@ -14,6 +14,11 @@ type ProjectGrant struct {
Members []*ProjectGrantMember Members []*ProjectGrantMember
} }
type ProjectGrantIDs struct {
ProjectID string
GrantID string
}
type ProjectGrantState int32 type ProjectGrantState int32
const ( const (
@ -41,3 +46,22 @@ func (p *ProjectGrant) GetMember(userID string) (int, *ProjectGrantMember) {
} }
return -1, nil return -1, nil
} }
func (p *ProjectGrant) GetRemovedRoles(roleKeys []string) []string {
removed := make([]string, 0)
for _, role := range p.RoleKeys {
if !containsKey(roleKeys, role) {
removed = append(removed, role)
}
}
return removed
}
func containsKey(roles []string, key string) bool {
for _, role := range roles {
if role == key {
return true
}
}
return false
}

View File

@ -6,18 +6,19 @@ import (
) )
type ProjectGrantView struct { type ProjectGrantView struct {
ProjectID string ProjectID string
Name string Name string
CreationDate time.Time CreationDate time.Time
ChangeDate time.Time ChangeDate time.Time
State ProjectState State ProjectState
ResourceOwner string ResourceOwner string
OrgID string ResourceOwnerName string
OrgName string OrgID string
OrgDomain string OrgName string
Sequence uint64 OrgDomain string
GrantID string Sequence uint64
GrantedRoleKeys []string GrantID string
GrantedRoleKeys []string
} }
type ProjectGrantViewSearchRequest struct { type ProjectGrantViewSearchRequest struct {
@ -37,6 +38,7 @@ const (
GRANTEDPROJECTSEARCHKEY_GRANTID GRANTEDPROJECTSEARCHKEY_GRANTID
GRANTEDPROJECTSEARCHKEY_ORGID GRANTEDPROJECTSEARCHKEY_ORGID
GRANTEDPROJECTSEARCHKEY_RESOURCE_OWNER GRANTEDPROJECTSEARCHKEY_RESOURCE_OWNER
GRANTEDPROJECTSEARCHKEY_ROLE_KEYS
) )
type ProjectGrantViewSearchQuery struct { type ProjectGrantViewSearchQuery struct {

View File

@ -248,28 +248,37 @@ func (es *ProjectEventstore) RemoveProjectMember(ctx context.Context, member *pr
return err return err
} }
func (es *ProjectEventstore) AddProjectRole(ctx context.Context, role *proj_model.ProjectRole) (*proj_model.ProjectRole, error) { func (es *ProjectEventstore) AddProjectRoles(ctx context.Context, roles ...*proj_model.ProjectRole) (*proj_model.ProjectRole, error) {
if !role.IsValid() { if roles == nil || len(roles) == 0 {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-idue3", "Key is required") return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-idue3", "must be at least one role")
} }
existing, err := es.ProjectByID(ctx, role.AggregateID) for _, role := range roles {
if !role.IsValid() {
return nil, caos_errs.ThrowPreconditionFailedf(nil, "EVENT-idue3", "role is invalid %v", role)
}
}
existing, err := es.ProjectByID(ctx, roles[0].AggregateID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if existing.ContainsRole(role) { for _, role := range roles {
return nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-sk35t", "Project contains role with same key") if existing.ContainsRole(role) {
return nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-sk35t", "Project contains role with same key")
}
} }
repoProject := model.ProjectFromModel(existing) repoProject := model.ProjectFromModel(existing)
repoRole := model.ProjectRoleFromModel(role) repoRoles := model.ProjectRolesFromModel(roles)
projectAggregate := ProjectRoleAddedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoRole) projectAggregate := ProjectRoleAddedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoRoles...)
err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate) err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(repoRoles) > 1 {
return nil, nil
}
es.projectCache.cacheProject(repoProject) es.projectCache.cacheProject(repoProject)
if _, r := model.GetProjectRole(repoProject.Roles, repoRoles[0].Key); r != nil {
if _, r := model.GetProjectRole(repoProject.Roles, role.Key); r != nil {
return model.ProjectRoleToModel(r), nil return model.ProjectRoleToModel(r), nil
} }
return nil, caos_errs.ThrowInternal(nil, "EVENT-sie83", "Could not find role in list") return nil, caos_errs.ThrowInternal(nil, "EVENT-sie83", "Could not find role in list")
@ -302,25 +311,52 @@ func (es *ProjectEventstore) ChangeProjectRole(ctx context.Context, role *proj_m
return nil, caos_errs.ThrowInternal(nil, "EVENT-sl1or", "Could not find role in list") return nil, caos_errs.ThrowInternal(nil, "EVENT-sl1or", "Could not find role in list")
} }
func (es *ProjectEventstore) RemoveProjectRole(ctx context.Context, role *proj_model.ProjectRole) error { func (es *ProjectEventstore) PrepareRemoveProjectRole(ctx context.Context, role *proj_model.ProjectRole) (*model.Project, *es_models.Aggregate, error) {
if role.Key == "" { if role.Key == "" {
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-id823", "Key is required") return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-id823", "Key is required")
} }
existing, err := es.ProjectByID(ctx, role.AggregateID) existing, err := es.ProjectByID(ctx, role.AggregateID)
if err != nil { if err != nil {
return err return nil, nil, err
} }
if !existing.ContainsRole(role) { if !existing.ContainsRole(role) {
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-oe823", "Role doesn't exist on project") return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-oe823", "Role doesn't exist on project")
} }
repoProject := model.ProjectFromModel(existing) repoProject := model.ProjectFromModel(existing)
repoRole := model.ProjectRoleFromModel(role) repoRole := model.ProjectRoleFromModel(role)
projectAggregate := ProjectRoleRemovedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoRole) grants := es.RemoveRoleFromGrants(repoProject, role.Key)
err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate) projectAggregate, err := ProjectRoleRemovedAggregate(ctx, es.Eventstore.AggregateCreator(), repoProject, repoRole, grants)
if err != nil {
return nil, nil, err
}
return repoProject, projectAggregate, nil
}
func (es *ProjectEventstore) RemoveRoleFromGrants(existing *model.Project, roleKey string) []*model.ProjectGrant {
grants := make([]*model.ProjectGrant, 0)
for _, grant := range existing.Grants {
for i, role := range grant.RoleKeys {
if role == roleKey {
grant.RoleKeys[i] = grant.RoleKeys[len(grant.RoleKeys)-1]
grant.RoleKeys[len(grant.RoleKeys)-1] = ""
grant.RoleKeys = grant.RoleKeys[:len(grant.RoleKeys)-1]
grants = append(grants, grant)
}
}
}
return grants
}
func (es *ProjectEventstore) RemoveProjectRole(ctx context.Context, role *proj_model.ProjectRole) error {
project, aggregate, err := es.PrepareRemoveProjectRole(ctx, role)
if err != nil { if err != nil {
return err return err
} }
es.projectCache.cacheProject(repoProject) err = es_sdk.PushAggregates(ctx, es.PushAggregates, project.AppendEvents, aggregate)
if err != nil {
return err
}
es.projectCache.cacheProject(project)
return nil return nil
} }
@ -766,50 +802,34 @@ func (es *ProjectEventstore) AddProjectGrant(ctx context.Context, grant *proj_mo
return nil, caos_errs.ThrowInternal(nil, "EVENT-sk3t5", "Could not find grant in list") return nil, caos_errs.ThrowInternal(nil, "EVENT-sk3t5", "Could not find grant in list")
} }
func (es *ProjectEventstore) ChangeProjectGrant(ctx context.Context, grant *proj_model.ProjectGrant) (*proj_model.ProjectGrant, error) { func (es *ProjectEventstore) PrepareChangeProjectGrant(ctx context.Context, grant *proj_model.ProjectGrant) (*model.Project, func(ctx context.Context) (*es_models.Aggregate, error), []string, error) {
if grant == nil && grant.GrantID == "" { if grant == nil && grant.GrantID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-8sie3", "invalid grant") return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-8sie3", "invalid grant")
} }
existing, err := es.ProjectByID(ctx, grant.AggregateID) existing, err := es.ProjectByID(ctx, grant.AggregateID)
if err != nil { if err != nil {
return nil, err return nil, nil, nil, err
} }
if _, g := existing.GetGrant(grant.GrantID); g == nil { _, existingGrant := existing.GetGrant(grant.GrantID)
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die83", "Grant not existing on project") if existingGrant == nil {
return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die83", "Grant not existing on project")
} }
if !existing.ContainsRoles(grant.RoleKeys) { if !existing.ContainsRoles(grant.RoleKeys) {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-di83d", "One role doesnt exist in Project") return nil, nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-di83d", "One role doesnt exist in Project")
} }
removedRoles := existingGrant.GetRemovedRoles(grant.RoleKeys)
repoProject := model.ProjectFromModel(existing) repoProject := model.ProjectFromModel(existing)
repoGrant := model.GrantFromModel(grant) repoGrant := model.GrantFromModel(grant)
projectAggregate := ProjectGrantChangedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoGrant) projectAggregate := ProjectGrantChangedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoGrant)
err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate) return repoProject, projectAggregate, removedRoles, nil
if err != nil {
return nil, err
}
es.projectCache.cacheProject(repoProject)
if _, g := model.GetProjectGrant(repoProject.Grants, grant.GrantID); g != nil {
return model.GrantToModel(g), nil
}
return nil, caos_errs.ThrowInternal(nil, "EVENT-dksi8", "Could not find app in list")
} }
func (es *ProjectEventstore) RemoveProjectGrant(ctx context.Context, grant *proj_model.ProjectGrant) error { func (es *ProjectEventstore) RemoveProjectGrant(ctx context.Context, grant *proj_model.ProjectGrant) error {
if grant.GrantID == "" { repoProject, projectAggregate, err := es.PrepareRemoveProjectGrant(ctx, grant)
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-8eud6", "GrantId is required")
}
existing, err := es.ProjectByID(ctx, grant.AggregateID)
if err != nil { if err != nil {
return err return err
} }
if _, g := existing.GetGrant(grant.GrantID); g == nil {
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-9ie3s", "Grant doesn't exist on project")
}
repoProject := model.ProjectFromModel(existing)
grantRepo := model.GrantFromModel(grant)
projectAggregate := ProjectGrantRemovedAggregate(es.Eventstore.AggregateCreator(), repoProject, grantRepo)
err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate) err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate)
if err != nil { if err != nil {
return err return err
@ -818,6 +838,39 @@ func (es *ProjectEventstore) RemoveProjectGrant(ctx context.Context, grant *proj
return nil return nil
} }
func (es *ProjectEventstore) RemoveProjectGrants(ctx context.Context, grants ...*proj_model.ProjectGrant) error {
aggregates := make([]*es_models.Aggregate, len(grants))
for i, grant := range grants {
_, projectAggregate, err := es.PrepareRemoveProjectGrant(ctx, grant)
if err != nil {
return err
}
agg, err := projectAggregate(ctx)
if err != nil {
return err
}
aggregates[i] = agg
}
return es_sdk.PushAggregates(ctx, es.PushAggregates, nil, aggregates...)
}
func (es *ProjectEventstore) PrepareRemoveProjectGrant(ctx context.Context, grant *proj_model.ProjectGrant) (*model.Project, func(ctx context.Context) (*es_models.Aggregate, error), error) {
if grant.GrantID == "" {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-8eud6", "GrantId is required")
}
existing, err := es.ProjectByID(ctx, grant.AggregateID)
if err != nil {
return nil, nil, err
}
if _, g := existing.GetGrant(grant.GrantID); g == nil {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9ie3s", "Grant doesn't exist on project")
}
repoProject := model.ProjectFromModel(existing)
grantRepo := model.GrantFromModel(grant)
projectAggregate := ProjectGrantRemovedAggregate(es.Eventstore.AggregateCreator(), repoProject, grantRepo)
return repoProject, projectAggregate, nil
}
func (es *ProjectEventstore) DeactivateProjectGrant(ctx context.Context, projectID, grantID string) (*proj_model.ProjectGrant, error) { func (es *ProjectEventstore) DeactivateProjectGrant(ctx context.Context, projectID, grantID string) (*proj_model.ProjectGrant, error) {
if grantID == "" { if grantID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-7due2", "grantID missing") return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-7due2", "grantID missing")

View File

@ -3,7 +3,6 @@ package eventsourcing
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"reflect"
"testing" "testing"
"github.com/caos/zitadel/internal/api/auth" "github.com/caos/zitadel/internal/api/auth"
@ -707,9 +706,9 @@ func TestRemoveProjectMember(t *testing.T) {
func TestAddProjectRole(t *testing.T) { func TestAddProjectRole(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
type args struct { type args struct {
es *ProjectEventstore es *ProjectEventstore
ctx context.Context ctx context.Context
role *model.ProjectRole roles []*model.ProjectRole
} }
type res struct { type res struct {
result *model.ProjectRole result *model.ProjectRole
@ -724,9 +723,9 @@ func TestAddProjectRole(t *testing.T) {
{ {
name: "add project role, ok", name: "add project role, ok",
args: args{ args: args{
es: GetMockManipulateProject(ctrl), es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
role: &model.ProjectRole{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Key: "Key", DisplayName: "DisplayName", Group: "Group"}, roles: []*model.ProjectRole{&model.ProjectRole{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Key: "Key", DisplayName: "DisplayName", Group: "Group"}},
}, },
res: res{ res: res{
result: &model.ProjectRole{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Key: "Key", DisplayName: "DisplayName", Group: "Group"}, result: &model.ProjectRole{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Key: "Key", DisplayName: "DisplayName", Group: "Group"},
@ -735,9 +734,9 @@ func TestAddProjectRole(t *testing.T) {
{ {
name: "no key", name: "no key",
args: args{ args: args{
es: GetMockManipulateProject(ctrl), es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
role: &model.ProjectRole{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, DisplayName: "DisplayName", Group: "Group"}, roles: []*model.ProjectRole{&model.ProjectRole{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, DisplayName: "DisplayName", Group: "Group"}},
}, },
res: res{ res: res{
wantErr: true, wantErr: true,
@ -747,9 +746,9 @@ func TestAddProjectRole(t *testing.T) {
{ {
name: "role already existing", name: "role already existing",
args: args{ args: args{
es: GetMockManipulateProjectWithRole(ctrl), es: GetMockManipulateProjectWithRole(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
role: &model.ProjectRole{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Key: "Key", DisplayName: "DisplayName", Group: "Group"}, roles: []*model.ProjectRole{&model.ProjectRole{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Key: "Key", DisplayName: "DisplayName", Group: "Group"}},
}, },
res: res{ res: res{
wantErr: true, wantErr: true,
@ -759,9 +758,9 @@ func TestAddProjectRole(t *testing.T) {
{ {
name: "existing project not found", name: "existing project not found",
args: args{ args: args{
es: GetMockManipulateProjectNoEvents(ctrl), es: GetMockManipulateProjectNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
role: &model.ProjectRole{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Key: "Key", DisplayName: "DisplayName", Group: "Group"}, roles: []*model.ProjectRole{&model.ProjectRole{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Key: "Key", DisplayName: "DisplayName", Group: "Group"}},
}, },
res: res{ res: res{
wantErr: true, wantErr: true,
@ -771,7 +770,7 @@ func TestAddProjectRole(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.AddProjectRole(tt.args.ctx, tt.args.role) result, err := tt.args.es.AddProjectRoles(tt.args.ctx, tt.args.roles...)
if !tt.res.wantErr && result.AggregateID == "" { if !tt.res.wantErr && result.AggregateID == "" {
t.Errorf("result has no id") t.Errorf("result has no id")
@ -1950,126 +1949,6 @@ func TestAddProjectGrant(t *testing.T) {
} }
} }
func TestChangeProjectGrant(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *ProjectEventstore
ctx context.Context
grant *model.ProjectGrant
}
type res struct {
result *model.ProjectGrant
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "change grant, ok",
args: args{
es: GetMockManipulateProjectWithGrantExistingRole(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
grant: &model.ProjectGrant{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID", Sequence: 1},
GrantID: "GrantID",
GrantedOrgID: "GrantedOrgID",
RoleKeys: []string{"KeyChanged"},
},
},
res: res{
result: &model.ProjectGrant{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID", Sequence: 1},
GrantID: "GrantID",
GrantedOrgID: "GrantedOrgID",
RoleKeys: []string{"KeyChanged"},
},
},
},
{
name: "invalid grant",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
grant: &model.ProjectGrant{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID", Sequence: 1},
GrantID: "GrantID",
RoleKeys: []string{"KeyChanged"},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "grant not existing",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
grant: &model.ProjectGrant{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID", Sequence: 1},
GrantID: "GrantID",
GrantedOrgID: "GrantedOrgID",
RoleKeys: []string{"KeyChanged"},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "role not existing",
args: args{
es: GetMockManipulateProjectWithGrant(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
grant: &model.ProjectGrant{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID", Sequence: 1},
GrantID: "GrantID",
GrantedOrgID: "GrantedOrgID",
RoleKeys: []string{"KeyChanged"},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing project not found",
args: args{
es: GetMockManipulateProjectNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
grant: &model.ProjectGrant{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID", Sequence: 1},
GrantID: "GrantID",
GrantedOrgID: "GrantedOrgID",
RoleKeys: []string{"KeyChanged"},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.ChangeProjectGrant(tt.args.ctx, tt.args.grant)
if !tt.res.wantErr && result.AggregateID == "" {
t.Errorf("result has no id")
}
if !tt.res.wantErr && result.GrantID != tt.res.result.GrantID {
t.Errorf("got wrong result GrantID: expected: %v, actual: %v ", tt.res.result.GrantID, result.GrantID)
}
if !tt.res.wantErr && !reflect.DeepEqual(result.RoleKeys, tt.res.result.RoleKeys) {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.result.RoleKeys, result.GrantID)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestRemoveProjectGrant(t *testing.T) { func TestRemoveProjectGrant(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
type args struct { type args struct {

View File

@ -127,7 +127,7 @@ func (p *Project) AppendEvent(event *es_models.Event) error {
return p.appendChangeOIDCConfigEvent(event) return p.appendChangeOIDCConfigEvent(event)
case ProjectGrantAdded: case ProjectGrantAdded:
return p.appendAddGrantEvent(event) return p.appendAddGrantEvent(event)
case ProjectGrantChanged: case ProjectGrantChanged, ProjectGrantCascadeChanged:
return p.appendChangeGrantEvent(event) return p.appendChangeGrantEvent(event)
case ProjectGrantDeactivated: case ProjectGrantDeactivated:
return p.appendGrantStateEvent(event, model.PROJECTGRANTSTATE_INACTIVE) return p.appendGrantStateEvent(event, model.PROJECTGRANTSTATE_INACTIVE)

View File

@ -31,9 +31,9 @@ func GetProjectGrant(grants []*ProjectGrant, id string) (int, *ProjectGrant) {
return -1, nil return -1, nil
} }
func GetProjectGrantByResourceOwner(grants []*ProjectGrant, resourceOwner string) (int, *ProjectGrant) { func GetProjectGrantByOrgID(grants []*ProjectGrant, resourceOwner string) (int, *ProjectGrant) {
for i, g := range grants { for i, g := range grants {
if g.ResourceOwner == resourceOwner { if g.GrantedOrgID == resourceOwner {
return i, g return i, g
} }
} }

View File

@ -19,11 +19,12 @@ const (
ProjectRoleChanged models.EventType = "project.role.changed" ProjectRoleChanged models.EventType = "project.role.changed"
ProjectRoleRemoved models.EventType = "project.role.removed" ProjectRoleRemoved models.EventType = "project.role.removed"
ProjectGrantAdded models.EventType = "project.grant.added" ProjectGrantAdded models.EventType = "project.grant.added"
ProjectGrantChanged models.EventType = "project.grant.changed" ProjectGrantChanged models.EventType = "project.grant.changed"
ProjectGrantRemoved models.EventType = "project.grant.removed" ProjectGrantRemoved models.EventType = "project.grant.removed"
ProjectGrantDeactivated models.EventType = "project.grant.deactivated" ProjectGrantDeactivated models.EventType = "project.grant.deactivated"
ProjectGrantReactivated models.EventType = "project.grant.reactivated" ProjectGrantReactivated models.EventType = "project.grant.reactivated"
ProjectGrantCascadeChanged models.EventType = "project.grant.cascade.changed"
ProjectGrantMemberAdded models.EventType = "project.grant.member.added" ProjectGrantMemberAdded models.EventType = "project.grant.member.added"
ProjectGrantMemberChanged models.EventType = "project.grant.member.changed" ProjectGrantMemberChanged models.EventType = "project.grant.member.changed"

View File

@ -144,16 +144,22 @@ func ProjectMemberRemovedAggregate(aggCreator *es_models.AggregateCreator, exist
} }
} }
func ProjectRoleAddedAggregate(aggCreator *es_models.AggregateCreator, existing *model.Project, role *model.ProjectRole) func(ctx context.Context) (*es_models.Aggregate, error) { func ProjectRoleAddedAggregate(aggCreator *es_models.AggregateCreator, existing *model.Project, roles ...*model.ProjectRole) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) { return func(ctx context.Context) (*es_models.Aggregate, error) {
if role == nil { if roles == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-sleo9", "role should not be nil") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-sleo9", "roles should not be nil")
} }
agg, err := ProjectAggregate(ctx, aggCreator, existing) agg, err := ProjectAggregate(ctx, aggCreator, existing)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return agg.AppendEvent(model.ProjectRoleAdded, role) for _, role := range roles {
agg, err = agg.AppendEvent(model.ProjectRoleAdded, role)
if err != nil {
return nil, err
}
}
return agg, nil
} }
} }
@ -170,17 +176,29 @@ func ProjectRoleChangedAggregate(aggCreator *es_models.AggregateCreator, existin
} }
} }
func ProjectRoleRemovedAggregate(aggCreator *es_models.AggregateCreator, existing *model.Project, role *model.ProjectRole) func(ctx context.Context) (*es_models.Aggregate, error) { func ProjectRoleRemovedAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *model.Project, role *model.ProjectRole, grants []*model.ProjectGrant) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) { if role == nil {
if role == nil { return nil, errors.ThrowPreconditionFailed(nil, "EVENT-d8eis", "member should not be nil")
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-d8eis", "member should not be nil")
}
agg, err := ProjectAggregate(ctx, aggCreator, existing)
if err != nil {
return nil, err
}
return agg.AppendEvent(model.ProjectRoleRemoved, role)
} }
agg, err := ProjectAggregate(ctx, aggCreator, existing)
if err != nil {
return nil, err
}
agg, err = agg.AppendEvent(model.ProjectRoleRemoved, role)
if err != nil {
return nil, err
}
for _, grant := range grants {
var changes map[string]interface{}
if _, g := model.GetProjectGrant(existing.Grants, grant.GrantID); grant != nil {
changes = g.Changes(grant)
agg, err = agg.AppendEvent(model.ProjectGrantCascadeChanged, changes)
if err != nil {
return nil, err
}
}
}
return agg, nil
} }
func ApplicationAddedAggregate(aggCreator *es_models.AggregateCreator, existing *model.Project, app *model.Application) func(ctx context.Context) (*es_models.Aggregate, error) { func ApplicationAddedAggregate(aggCreator *es_models.AggregateCreator, existing *model.Project, app *model.Application) func(ctx context.Context) (*es_models.Aggregate, error) {

View File

@ -694,7 +694,7 @@ func TestProjectRoleAddedAggregate(t *testing.T) {
type args struct { type args struct {
ctx context.Context ctx context.Context
existing *model.Project existing *model.Project
new *model.ProjectRole new []*model.ProjectRole
aggCreator *models.AggregateCreator aggCreator *models.AggregateCreator
} }
type res struct { type res struct {
@ -713,7 +713,7 @@ func TestProjectRoleAddedAggregate(t *testing.T) {
args: args{ args: args{
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Name: "ProjectName", State: int32(proj_model.PROJECTSTATE_ACTIVE)}, existing: &model.Project{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Name: "ProjectName", State: int32(proj_model.PROJECTSTATE_ACTIVE)},
new: &model.ProjectRole{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Key: "Key"}, new: []*model.ProjectRole{&model.ProjectRole{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Key: "Key"}},
aggCreator: models.NewAggregateCreator("Test"), aggCreator: models.NewAggregateCreator("Test"),
}, },
res: res{ res: res{
@ -721,6 +721,22 @@ func TestProjectRoleAddedAggregate(t *testing.T) {
eventType: model.ProjectRoleAdded, eventType: model.ProjectRoleAdded,
}, },
}, },
{
name: "projectrole multiple added ok",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Name: "ProjectName", State: int32(proj_model.PROJECTSTATE_ACTIVE)},
new: []*model.ProjectRole{
&model.ProjectRole{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Key: "Key"},
&model.ProjectRole{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Key: "Key2"},
},
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 2,
eventType: model.ProjectRoleAdded,
},
},
{ {
name: "existing project nil", name: "existing project nil",
args: args{ args: args{
@ -753,7 +769,7 @@ func TestProjectRoleAddedAggregate(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
agg, err := ProjectRoleAddedAggregate(tt.args.aggCreator, tt.args.existing, tt.args.new)(tt.args.ctx) agg, err := ProjectRoleAddedAggregate(tt.args.aggCreator, tt.args.existing, tt.args.new...)(tt.args.ctx)
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen { if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events)) t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
@ -857,13 +873,14 @@ func TestProjectRoleRemovedAggregate(t *testing.T) {
ctx context.Context ctx context.Context
existing *model.Project existing *model.Project
new *model.ProjectRole new *model.ProjectRole
grants []*model.ProjectGrant
aggCreator *models.AggregateCreator aggCreator *models.AggregateCreator
} }
type res struct { type res struct {
eventLen int eventLen int
eventType models.EventType eventTypes []models.EventType
wantErr bool wantErr bool
errFunc func(err error) bool errFunc func(err error) bool
} }
tests := []struct { tests := []struct {
name string name string
@ -879,8 +896,27 @@ func TestProjectRoleRemovedAggregate(t *testing.T) {
aggCreator: models.NewAggregateCreator("Test"), aggCreator: models.NewAggregateCreator("Test"),
}, },
res: res{ res: res{
eventLen: 1, eventLen: 1,
eventType: model.ProjectRoleRemoved, eventTypes: []models.EventType{model.ProjectRoleRemoved},
},
},
{
name: "projectrole changed with grant",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{
ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"},
Name: "ProjectName",
State: int32(proj_model.PROJECTSTATE_ACTIVE),
Grants: []*model.ProjectGrant{&model.ProjectGrant{ObjectRoot: models.ObjectRoot{AggregateID: "ID"}, GrantID: "GrantID", GrantedOrgID: "OrgID", RoleKeys: []string{"ROLE"}}},
},
new: &model.ProjectRole{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Key: "Key"},
grants: []*model.ProjectGrant{&model.ProjectGrant{ObjectRoot: models.ObjectRoot{AggregateID: "ID"}, GrantID: "GrantID", GrantedOrgID: "OrgID", RoleKeys: []string{}}},
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 2,
eventTypes: []models.EventType{model.ProjectRoleRemoved, model.ProjectGrantCascadeChanged},
}, },
}, },
{ {
@ -891,10 +927,8 @@ func TestProjectRoleRemovedAggregate(t *testing.T) {
aggCreator: models.NewAggregateCreator("Test"), aggCreator: models.NewAggregateCreator("Test"),
}, },
res: res{ res: res{
eventLen: 1, wantErr: true,
eventType: model.ProjectRoleRemoved, errFunc: caos_errs.IsPreconditionFailed,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
}, },
}, },
{ {
@ -906,26 +940,29 @@ func TestProjectRoleRemovedAggregate(t *testing.T) {
aggCreator: models.NewAggregateCreator("Test"), aggCreator: models.NewAggregateCreator("Test"),
}, },
res: res{ res: res{
eventLen: 1, wantErr: true,
eventType: model.ProjectRoleRemoved, errFunc: caos_errs.IsPreconditionFailed,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
}, },
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
agg, err := ProjectRoleRemovedAggregate(tt.args.aggCreator, tt.args.existing, tt.args.new)(tt.args.ctx) agg, err := ProjectRoleRemovedAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.existing, tt.args.new, tt.args.grants)
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen { if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events)) t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
} }
if !tt.res.wantErr && agg.Events[0].Type != tt.res.eventType { if agg != nil {
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String()) for i, _ := range agg.Events {
} if !tt.res.wantErr && agg.Events[i].Type != tt.res.eventTypes[i] {
if !tt.res.wantErr && agg.Events[0].Data == nil { t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventTypes[i], agg.Events[i].Type.String())
t.Errorf("should have data in event") }
if !tt.res.wantErr && agg.Events[i].Data == nil {
t.Errorf("should have data in event")
}
}
} }
if tt.res.wantErr && !tt.res.errFunc(err) { if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err) t.Errorf("got wrong err: %v ", err)
} }

View File

@ -17,20 +17,22 @@ const (
ProjectGrantKeyOrgID = "org_id" ProjectGrantKeyOrgID = "org_id"
ProjectGrantKeyResourceOwner = "resource_owner" ProjectGrantKeyResourceOwner = "resource_owner"
ProjectGrantKeyName = "project_name" ProjectGrantKeyName = "project_name"
ProjectGrantKeyRoleKeys = "granted_role_keys"
) )
type ProjectGrantView struct { type ProjectGrantView struct {
GrantID string `json:"-" gorm:"column:grant_id;primary_key"` GrantID string `json:"-" gorm:"column:grant_id;primary_key"`
ProjectID string `json:"-" gorm:"column:project_id"` ProjectID string `json:"-" gorm:"column:project_id"`
OrgID string `json:"-" gorm:"column:org_id"` OrgID string `json:"-" gorm:"column:org_id"`
Name string `json:"name" gorm:"column:project_name"` Name string `json:"name" gorm:"column:project_name"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`
State int32 `json:"-" gorm:"column:project_state"` State int32 `json:"-" gorm:"column:project_state"`
ResourceOwner string `json:"-" gorm:"column:resource_owner"` ResourceOwner string `json:"-" gorm:"column:resource_owner"`
OrgName string `json:"-" gorm:"column:org_name"` ResourceOwnerName string `json:"-" gorm:"column:resource_owner_name"`
Sequence uint64 `json:"-" gorm:"column:sequence"` OrgName string `json:"-" gorm:"column:org_name"`
GrantedRoleKeys pq.StringArray `json:"-" gorm:"column:granted_role_keys"` Sequence uint64 `json:"-" gorm:"column:sequence"`
GrantedRoleKeys pq.StringArray `json:"-" gorm:"column:granted_role_keys"`
} }
type ProjectGrant struct { type ProjectGrant struct {
@ -41,32 +43,35 @@ type ProjectGrant struct {
func ProjectGrantFromModel(project *model.ProjectGrantView) *ProjectGrantView { func ProjectGrantFromModel(project *model.ProjectGrantView) *ProjectGrantView {
return &ProjectGrantView{ return &ProjectGrantView{
ProjectID: project.ProjectID, ProjectID: project.ProjectID,
OrgID: project.OrgID, OrgID: project.OrgID,
Name: project.Name, Name: project.Name,
ChangeDate: project.ChangeDate, ChangeDate: project.ChangeDate,
CreationDate: project.CreationDate, CreationDate: project.CreationDate,
State: int32(project.State), State: int32(project.State),
ResourceOwner: project.ResourceOwner, ResourceOwner: project.ResourceOwner,
OrgName: project.OrgName, ResourceOwnerName: project.ResourceOwnerName,
GrantID: project.GrantID, OrgName: project.OrgName,
GrantedRoleKeys: project.GrantedRoleKeys, GrantID: project.GrantID,
Sequence: project.Sequence, GrantedRoleKeys: project.GrantedRoleKeys,
Sequence: project.Sequence,
} }
} }
func ProjectGrantToModel(project *ProjectGrantView) *model.ProjectGrantView { func ProjectGrantToModel(project *ProjectGrantView) *model.ProjectGrantView {
return &model.ProjectGrantView{ return &model.ProjectGrantView{
ProjectID: project.ProjectID, ProjectID: project.ProjectID,
OrgID: project.OrgID, OrgID: project.OrgID,
Name: project.Name, Name: project.Name,
ChangeDate: project.ChangeDate, ChangeDate: project.ChangeDate,
CreationDate: project.CreationDate, CreationDate: project.CreationDate,
State: model.ProjectState(project.State), State: model.ProjectState(project.State),
ResourceOwner: project.ResourceOwner, ResourceOwner: project.ResourceOwner,
OrgName: project.OrgName, ResourceOwnerName: project.ResourceOwnerName,
GrantID: project.GrantID, OrgName: project.OrgName,
Sequence: project.Sequence, GrantID: project.GrantID,
Sequence: project.Sequence,
GrantedRoleKeys: project.GrantedRoleKeys,
} }
} }
@ -87,7 +92,7 @@ func (p *ProjectGrantView) AppendEvent(event *models.Event) (err error) {
p.CreationDate = event.CreationDate p.CreationDate = event.CreationDate
p.setRootData(event) p.setRootData(event)
err = p.setProjectGrantData(event) err = p.setProjectGrantData(event)
case es_model.ProjectGrantChanged: case es_model.ProjectGrantChanged, es_model.ProjectGrantCascadeChanged:
err = p.setProjectGrantData(event) err = p.setProjectGrantData(event)
case es_model.ProjectGrantDeactivated: case es_model.ProjectGrantDeactivated:
p.State = int32(model.PROJECTSTATE_INACTIVE) p.State = int32(model.PROJECTSTATE_INACTIVE)

View File

@ -61,6 +61,8 @@ func (key ProjectGrantSearchKey) ToColumnName() string {
return ProjectGrantKeyProjectID return ProjectGrantKeyProjectID
case proj_model.GRANTEDPROJECTSEARCHKEY_RESOURCE_OWNER: case proj_model.GRANTEDPROJECTSEARCHKEY_RESOURCE_OWNER:
return ProjectGrantKeyResourceOwner return ProjectGrantKeyResourceOwner
case proj_model.GRANTEDPROJECTSEARCHKEY_ROLE_KEYS:
return ProjectGrantKeyRoleKeys
default: default:
return "" return ""
} }

View File

@ -39,6 +39,20 @@ func ProjectGrantsByProjectID(db *gorm.DB, table, projectID string) ([]*model.Pr
return projects, nil return projects, nil
} }
func ProjectGrantsByProjectIDAndRoleKey(db *gorm.DB, table, projectID, roleKey string) ([]*model.ProjectGrantView, error) {
projects := make([]*model.ProjectGrantView, 0)
queries := []*proj_model.ProjectGrantViewSearchQuery{
&proj_model.ProjectGrantViewSearchQuery{Key: proj_model.GRANTEDPROJECTSEARCHKEY_PROJECTID, Value: projectID, Method: global_model.SEARCHMETHOD_EQUALS},
&proj_model.ProjectGrantViewSearchQuery{Key: proj_model.GRANTEDPROJECTSEARCHKEY_ROLE_KEYS, Value: roleKey, Method: global_model.SEARCHMETHOD_LIST_CONTAINS},
}
query := view.PrepareSearchQuery(table, model.ProjectGrantSearchRequest{Queries: queries})
_, err := query(db, &projects)
if err != nil {
return nil, err
}
return projects, nil
}
func SearchProjectGrants(db *gorm.DB, table string, req *proj_model.ProjectGrantViewSearchRequest) ([]*model.ProjectGrantView, int, error) { func SearchProjectGrants(db *gorm.DB, table string, req *proj_model.ProjectGrantViewSearchRequest) ([]*model.ProjectGrantView, int, error) {
projects := make([]*model.ProjectGrantView, 0) projects := make([]*model.ProjectGrantView, 0)
query := view.PrepareSearchQuery(table, model.ProjectGrantSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries}) query := view.PrepareSearchQuery(table, model.ProjectGrantSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})
@ -55,6 +69,6 @@ func PutProjectGrant(db *gorm.DB, table string, project *model.ProjectGrantView)
} }
func DeleteProjectGrant(db *gorm.DB, table, grantID string) error { func DeleteProjectGrant(db *gorm.DB, table, grantID string) error {
delete := view.PrepareDeleteByKey(table, model.ProjectSearchKey(proj_model.PROJECTGRANTMEMBERSEARCHKEY_GRANT_ID), grantID) delete := view.PrepareDeleteByKey(table, model.ProjectSearchKey(proj_model.GRANTEDPROJECTSEARCHKEY_GRANTID), grantID)
return delete(db) return delete(db)
} }

View File

@ -65,6 +65,7 @@ func Run(generator ProtocGenerator) {
} }
registry := descriptor.NewRegistry() registry := descriptor.NewRegistry()
registry.SetAllowDeleteBody(true)
if err = registry.Load(req); err != nil { if err = registry.Load(req); err != nil {
glog.Fatal(err) glog.Fatal(err)
} }

View File

@ -83,7 +83,7 @@ func templatesAuth_method_mappingGoTmpl() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "templates/auth_method_mapping.go.tmpl", size: 1013, mode: os.FileMode(420), modTime: time.Unix(1586333177, 0)} info := bindataFileInfo{name: "templates/auth_method_mapping.go.tmpl", size: 1013, mode: os.FileMode(420), modTime: time.Unix(1585815980, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -182,6 +182,7 @@ type bintree struct {
Func func() (*asset, error) Func func() (*asset, error)
Children map[string]*bintree Children map[string]*bintree
} }
var _bintree = &bintree{nil, map[string]*bintree{ var _bintree = &bintree{nil, map[string]*bintree{
"templates": &bintree{nil, map[string]*bintree{ "templates": &bintree{nil, map[string]*bintree{
"auth_method_mapping.go.tmpl": &bintree{templatesAuth_method_mappingGoTmpl, map[string]*bintree{}}, "auth_method_mapping.go.tmpl": &bintree{templatesAuth_method_mappingGoTmpl, map[string]*bintree{}},
@ -234,4 +235,3 @@ func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1) cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
} }

View File

@ -34,7 +34,7 @@ func UserByLoginName(db *gorm.DB, table, loginName string) (*model.UserView, err
user := new(model.UserView) user := new(model.UserView)
loginNameQuery := &model.UserSearchQuery{ loginNameQuery := &model.UserSearchQuery{
Key: usr_model.USERSEARCHKEY_LOGIN_NAMES, Key: usr_model.USERSEARCHKEY_LOGIN_NAMES,
Method: global_model.SEARCHMETHOD_EQUALS_IN_ARRAY, Method: global_model.SEARCHMETHOD_LIST_CONTAINS,
Value: pq.Array([]string{loginName}), Value: pq.Array([]string{loginName}),
} }
query := view.PrepareGetByQuery(table, loginNameQuery) query := view.PrepareGetByQuery(table, loginNameQuery)

View File

@ -30,3 +30,26 @@ func (u *UserGrant) IsActive() bool {
func (u *UserGrant) IsInactive() bool { func (u *UserGrant) IsInactive() bool {
return u.State == USERGRANTSTATE_INACTIVE return u.State == USERGRANTSTATE_INACTIVE
} }
func (u *UserGrant) RemoveRoleKeyIfExisting(key string) bool {
for i, role := range u.RoleKeys {
if role == key {
u.RoleKeys[i] = u.RoleKeys[len(u.RoleKeys)-1]
u.RoleKeys[len(u.RoleKeys)-1] = ""
u.RoleKeys = u.RoleKeys[:len(u.RoleKeys)-1]
return true
}
}
return false
}
func (u *UserGrant) RemoveRoleKeysIfExisting(keys []string) bool {
exists := false
for _, key := range keys {
keyExists := u.RemoveRoleKeyIfExisting(key)
if keyExists {
exists = true
}
}
return exists
}

View File

@ -44,12 +44,13 @@ const (
USERGRANTSEARCHKEY_STATE USERGRANTSEARCHKEY_STATE
USERGRANTSEARCHKEY_GRANT_ID USERGRANTSEARCHKEY_GRANT_ID
USERGRANTSEARCHKEY_ORG_NAME USERGRANTSEARCHKEY_ORG_NAME
USERGRANTSEARCHKEY_ROLE_KEY
) )
type UserGrantSearchQuery struct { type UserGrantSearchQuery struct {
Key UserGrantSearchKey Key UserGrantSearchKey
Method model.SearchMethod Method model.SearchMethod
Value string Value interface{}
} }
type UserGrantSearchResponse struct { type UserGrantSearchResponse struct {

View File

@ -6,6 +6,7 @@ import (
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
es_int "github.com/caos/zitadel/internal/eventstore" es_int "github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
"github.com/caos/zitadel/internal/id" "github.com/caos/zitadel/internal/id"
grant_model "github.com/caos/zitadel/internal/usergrant/model" grant_model "github.com/caos/zitadel/internal/usergrant/model"
@ -54,18 +55,7 @@ func (es *UserGrantEventStore) UserGrantByID(ctx context.Context, id string) (*g
} }
func (es *UserGrantEventStore) AddUserGrant(ctx context.Context, grant *grant_model.UserGrant) (*grant_model.UserGrant, error) { func (es *UserGrantEventStore) AddUserGrant(ctx context.Context, grant *grant_model.UserGrant) (*grant_model.UserGrant, error) {
if grant == nil || !grant.IsValid() { repoGrant, addAggregates, err := es.PrepareAddUserGrant(ctx, grant)
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-sdiw3", "User grant invalid")
}
id, err := es.idGenerator.Next()
if err != nil {
return nil, err
}
grant.AggregateID = id
repoGrant := model.UserGrantFromModel(grant)
addAggregates, err := UserGrantAddedAggregate(ctx, es.Eventstore.AggregateCreator(), repoGrant)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -76,19 +66,65 @@ func (es *UserGrantEventStore) AddUserGrant(ctx context.Context, grant *grant_mo
return model.UserGrantToModel(repoGrant), nil return model.UserGrantToModel(repoGrant), nil
} }
func (es *UserGrantEventStore) ChangeUserGrant(ctx context.Context, grant *grant_model.UserGrant) (*grant_model.UserGrant, error) { func (es *UserGrantEventStore) AddUserGrants(ctx context.Context, grants ...*grant_model.UserGrant) error {
aggregates := make([]*es_models.Aggregate, 0)
for _, grant := range grants {
_, addAggregates, err := es.PrepareAddUserGrant(ctx, grant)
if err != nil {
return err
}
for _, agg := range addAggregates {
aggregates = append(aggregates, agg)
}
}
return es_sdk.PushAggregates(ctx, es.PushAggregates, nil, aggregates...)
}
func (es *UserGrantEventStore) PrepareAddUserGrant(ctx context.Context, grant *grant_model.UserGrant) (*model.UserGrant, []*es_models.Aggregate, error) {
if grant == nil || !grant.IsValid() {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-sdiw3", "User grant invalid")
}
id, err := es.idGenerator.Next()
if err != nil {
return nil, nil, err
}
grant.AggregateID = id
repoGrant := model.UserGrantFromModel(grant)
addAggregates, err := UserGrantAddedAggregate(ctx, es.Eventstore.AggregateCreator(), repoGrant)
if err != nil {
return nil, nil, err
}
return repoGrant, addAggregates, nil
}
func (es *UserGrantEventStore) PrepareChangeUserGrant(ctx context.Context, grant *grant_model.UserGrant, cascade bool) (*model.UserGrant, *es_models.Aggregate, error) {
if grant == nil { if grant == nil {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-lo0s9", "invalid grant") return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-lo0s9", "invalid grant")
} }
existing, err := es.UserGrantByID(ctx, grant.AggregateID) existing, err := es.UserGrantByID(ctx, grant.AggregateID)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
repoExisting := model.UserGrantFromModel(existing) repoExisting := model.UserGrantFromModel(existing)
repoGrant := model.UserGrantFromModel(grant) repoGrant := model.UserGrantFromModel(grant)
projectAggregate := UserGrantChangedAggregate(es.Eventstore.AggregateCreator(), repoExisting, repoGrant) aggFunc := UserGrantChangedAggregate(es.Eventstore.AggregateCreator(), repoExisting, repoGrant, cascade)
err = es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, projectAggregate) projectAggregate, err := aggFunc(ctx)
if err != nil {
return nil, nil, err
}
return repoExisting, projectAggregate, err
}
func (es *UserGrantEventStore) ChangeUserGrant(ctx context.Context, grant *grant_model.UserGrant) (*grant_model.UserGrant, error) {
repoExisting, agg, err := es.PrepareChangeUserGrant(ctx, grant, false)
if err != nil {
return nil, err
}
err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoExisting.AppendEvents, agg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -96,23 +132,57 @@ func (es *UserGrantEventStore) ChangeUserGrant(ctx context.Context, grant *grant
return model.UserGrantToModel(repoExisting), nil return model.UserGrantToModel(repoExisting), nil
} }
func (es *UserGrantEventStore) ChangeUserGrants(ctx context.Context, grants ...*grant_model.UserGrant) error {
aggregates := make([]*es_models.Aggregate, len(grants))
for i, grant := range grants {
_, agg, err := es.PrepareChangeUserGrant(ctx, grant, false)
if err != nil {
return err
}
aggregates[i] = agg
}
return es_sdk.PushAggregates(ctx, es.PushAggregates, nil, aggregates...)
}
func (es *UserGrantEventStore) RemoveUserGrant(ctx context.Context, grantID string) error { func (es *UserGrantEventStore) RemoveUserGrant(ctx context.Context, grantID string) error {
existing, err := es.UserGrantByID(ctx, grantID) existing, projectAggregates, err := es.PrepareRemoveUserGrant(ctx, grantID, false)
if err != nil { if err != nil {
return err return err
} }
err = es_sdk.PushAggregates(ctx, es.PushAggregates, existing.AppendEvents, projectAggregates...)
if err != nil {
return err
}
es.userGrantCache.cacheUserGrant(existing)
return nil
}
func (es *UserGrantEventStore) RemoveUserGrants(ctx context.Context, grantIDs ...string) error {
aggregates := make([]*es_models.Aggregate, 0)
for _, grantID := range grantIDs {
_, aggs, err := es.PrepareRemoveUserGrant(ctx, grantID, false)
if err != nil {
return err
}
for _, agg := range aggs {
aggregates = append(aggregates, agg)
}
}
return es_sdk.PushAggregates(ctx, es.PushAggregates, nil, aggregates...)
}
func (es *UserGrantEventStore) PrepareRemoveUserGrant(ctx context.Context, grantID string, cascade bool) (*model.UserGrant, []*es_models.Aggregate, error) {
existing, err := es.UserGrantByID(ctx, grantID)
if err != nil {
return nil, nil, err
}
repoExisting := model.UserGrantFromModel(existing) repoExisting := model.UserGrantFromModel(existing)
repoGrant := &model.UserGrant{ObjectRoot: models.ObjectRoot{AggregateID: grantID}} repoGrant := &model.UserGrant{ObjectRoot: models.ObjectRoot{AggregateID: grantID}}
projectAggregates, err := UserGrantRemovedAggregate(ctx, es.Eventstore.AggregateCreator(), repoExisting, repoGrant) projectAggregates, err := UserGrantRemovedAggregate(ctx, es.Eventstore.AggregateCreator(), repoExisting, repoGrant, cascade)
if err != nil { if err != nil {
return err return nil, nil, err
} }
err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoExisting.AppendEvents, projectAggregates...) return repoExisting, projectAggregates, nil
if err != nil {
return err
}
es.userGrantCache.cacheUserGrant(repoExisting)
return nil
} }
func (es *UserGrantEventStore) DeactivateUserGrant(ctx context.Context, grantID string) (*grant_model.UserGrant, error) { func (es *UserGrantEventStore) DeactivateUserGrant(ctx context.Context, grantID string) (*grant_model.UserGrant, error) {

View File

@ -13,4 +13,7 @@ const (
UserGrantReactivated models.EventType = "user.grant.reactivated" UserGrantReactivated models.EventType = "user.grant.reactivated"
UserGrantReserved models.EventType = "user.grant.reserved" UserGrantReserved models.EventType = "user.grant.reserved"
UserGrantReleased models.EventType = "user.grant.released" UserGrantReleased models.EventType = "user.grant.released"
UserGrantCascadeRemoved models.EventType = "user.grant.cascade.removed"
UserGrantCascadeChanged models.EventType = "user.grant.cascade.changed"
) )

View File

@ -68,13 +68,15 @@ func (g *UserGrant) AppendEvent(event *es_models.Event) error {
g.ObjectRoot.AppendEvent(event) g.ObjectRoot.AppendEvent(event)
switch event.Type { switch event.Type {
case UserGrantAdded, case UserGrantAdded,
UserGrantChanged: UserGrantChanged,
UserGrantCascadeChanged:
return g.setData(event) return g.setData(event)
case UserGrantDeactivated: case UserGrantDeactivated:
g.appendGrantStateEvent(model.USERGRANTSTATE_INACTIVE) g.appendGrantStateEvent(model.USERGRANTSTATE_INACTIVE)
case UserGrantReactivated: case UserGrantReactivated:
g.appendGrantStateEvent(model.USERGRANTSTATE_ACTIVE) g.appendGrantStateEvent(model.USERGRANTSTATE_ACTIVE)
case UserGrantRemoved: case UserGrantRemoved,
UserGrantCascadeRemoved:
g.appendGrantStateEvent(model.USERGRANTSTATE_REMOVED) g.appendGrantStateEvent(model.USERGRANTSTATE_REMOVED)
} }
return nil return nil

View File

@ -95,7 +95,7 @@ func releasedUniqueUserGrantAggregate(ctx context.Context, aggCreator *es_models
return aggregate.SetPrecondition(UserGrantUniqueQuery(grant.ResourceOwner, grant.ProjectID, grant.UserID), isEventValidation(aggregate, model.UserGrantReleased)), nil return aggregate.SetPrecondition(UserGrantUniqueQuery(grant.ResourceOwner, grant.ProjectID, grant.UserID), isEventValidation(aggregate, model.UserGrantReleased)), nil
} }
func UserGrantChangedAggregate(aggCreator *es_models.AggregateCreator, existing *model.UserGrant, grant *model.UserGrant) func(ctx context.Context) (*es_models.Aggregate, error) { func UserGrantChangedAggregate(aggCreator *es_models.AggregateCreator, existing *model.UserGrant, grant *model.UserGrant, cascade bool) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) { return func(ctx context.Context) (*es_models.Aggregate, error) {
if grant == nil { if grant == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-osl8x", "grant should not be nil") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-osl8x", "grant should not be nil")
@ -105,7 +105,10 @@ func UserGrantChangedAggregate(aggCreator *es_models.AggregateCreator, existing
return nil, err return nil, err
} }
changes := existing.Changes(grant) changes := existing.Changes(grant)
return agg.AppendEvent(model.UserGrantChanged, changes) if !cascade {
return agg.AppendEvent(model.UserGrantChanged, changes)
}
return agg.AppendEvent(model.UserGrantCascadeChanged, changes)
} }
} }
@ -135,7 +138,7 @@ func UserGrantReactivatedAggregate(aggCreator *es_models.AggregateCreator, exist
} }
} }
func UserGrantRemovedAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *model.UserGrant, grant *model.UserGrant) ([]*es_models.Aggregate, error) { func UserGrantRemovedAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *model.UserGrant, grant *model.UserGrant, cascade bool) ([]*es_models.Aggregate, error) {
if grant == nil { if grant == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-lo21s", "grant should not be nil") return nil, errors.ThrowPreconditionFailed(nil, "EVENT-lo21s", "grant should not be nil")
} }
@ -143,7 +146,11 @@ func UserGrantRemovedAggregate(ctx context.Context, aggCreator *es_models.Aggreg
if err != nil { if err != nil {
return nil, err return nil, err
} }
agg, err = agg.AppendEvent(model.UserGrantRemoved, nil) eventType := model.UserGrantRemoved
if cascade {
eventType = model.UserGrantCascadeRemoved
}
agg, err = agg.AppendEvent(eventType, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -196,37 +203,43 @@ func addUserGrantValidation(resourceOwner string, grant *model.UserGrant) func(.
project.AppendEvent(event) project.AppendEvent(event)
} }
} }
if existsOrg && existsUser && checkProjectConditions(resourceOwner, grant, project) { if !existsOrg {
return nil return errors.ThrowPreconditionFailed(nil, "EVENT-3OfIm", "org doesn't exist")
} }
return errors.ThrowPreconditionFailed(nil, "EVENT-3OfIm", "conditions not met") if !existsUser {
return errors.ThrowPreconditionFailed(nil, "EVENT-Sl8uS", "user doesn't exist")
}
if err := checkProjectConditions(resourceOwner, grant, project); err != nil {
return err
}
return nil
} }
} }
func checkProjectConditions(resourceOwner string, grant *model.UserGrant, project *proj_es_model.Project) bool { func checkProjectConditions(resourceOwner string, grant *model.UserGrant, project *proj_es_model.Project) error {
if project.State == int32(proj_model.PROJECTSTATE_REMOVED) { if project.State == int32(proj_model.PROJECTSTATE_REMOVED) {
return false return errors.ThrowPreconditionFailed(nil, "EVENT-Lxp0s", "project doesn't exist")
} }
if resourceOwner == project.ResourceOwner { if resourceOwner == project.ResourceOwner {
return checkIfProjectHasRoles(grant.RoleKeys, project.Roles) return checkIfProjectHasRoles(grant.RoleKeys, project.Roles)
} }
if _, projectGrant := proj_es_model.GetProjectGrantByResourceOwner(project.Grants, resourceOwner); projectGrant != nil { if _, projectGrant := proj_es_model.GetProjectGrantByOrgID(project.Grants, resourceOwner); projectGrant != nil {
return checkIfProjectGrantHasRoles(grant.RoleKeys, projectGrant.RoleKeys) return checkIfProjectGrantHasRoles(grant.RoleKeys, projectGrant.RoleKeys)
} }
return false return nil
} }
func checkIfProjectHasRoles(roles []string, existing []*proj_es_model.ProjectRole) bool { func checkIfProjectHasRoles(roles []string, existing []*proj_es_model.ProjectRole) error {
for _, roleKey := range roles { for _, roleKey := range roles {
if _, role := proj_es_model.GetProjectRole(existing, roleKey); role == nil { if _, role := proj_es_model.GetProjectRole(existing, roleKey); role == nil {
return false return errors.ThrowPreconditionFailedf(nil, "EVENT-Lxp0s", "project doesn't have role %v", roleKey)
} }
} }
return true return nil
} }
func checkIfProjectGrantHasRoles(roles []string, existing []string) bool { func checkIfProjectGrantHasRoles(roles []string, existing []string) error {
roleExists := false roleExists := false
for _, roleKey := range roles { for _, roleKey := range roles {
for _, existingRoleKey := range existing { for _, existingRoleKey := range existing {
@ -236,8 +249,8 @@ func checkIfProjectGrantHasRoles(roles []string, existing []string) bool {
} }
} }
if !roleExists { if !roleExists {
return false return errors.ThrowPreconditionFailed(nil, "EVENT-LSpwi", "project grant doesn't have role")
} }
} }
return true return nil
} }

View File

@ -74,6 +74,7 @@ func TestUserGrantChangedAggregate(t *testing.T) {
ctx context.Context ctx context.Context
existing *model.UserGrant existing *model.UserGrant
new *model.UserGrant new *model.UserGrant
cascade bool
aggCreator *models.AggregateCreator aggCreator *models.AggregateCreator
} }
type res struct { type res struct {
@ -108,6 +109,29 @@ func TestUserGrantChangedAggregate(t *testing.T) {
eventTypes: []models.EventType{model.UserGrantChanged}, eventTypes: []models.EventType{model.UserGrantChanged},
}, },
}, },
{
name: "change project grant cascade",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.UserGrant{
ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
UserID: "UserID",
ProjectID: "ProjectID",
RoleKeys: []string{"Key"}},
new: &model.UserGrant{
ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
UserID: "UserID",
ProjectID: "ProjectID",
RoleKeys: []string{"KeyChanged"},
},
cascade: true,
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventTypes: []models.EventType{model.UserGrantCascadeChanged},
},
},
{ {
name: "existing grant nil", name: "existing grant nil",
args: args{ args: args{
@ -143,7 +167,7 @@ func TestUserGrantChangedAggregate(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
agg, err := UserGrantChangedAggregate(tt.args.aggCreator, tt.args.existing, tt.args.new)(tt.args.ctx) agg, err := UserGrantChangedAggregate(tt.args.aggCreator, tt.args.existing, tt.args.new, tt.args.cascade)(tt.args.ctx)
if tt.res.errFunc == nil && len(agg.Events) != tt.res.eventLen { if tt.res.errFunc == nil && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events)) t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
@ -169,6 +193,7 @@ func TestUserGrantRemovedAggregate(t *testing.T) {
ctx context.Context ctx context.Context
existing *model.UserGrant existing *model.UserGrant
new *model.UserGrant new *model.UserGrant
cascade bool
aggCreator *models.AggregateCreator aggCreator *models.AggregateCreator
} }
type res struct { type res struct {
@ -200,6 +225,26 @@ func TestUserGrantRemovedAggregate(t *testing.T) {
eventTypes: []models.EventType{model.UserGrantRemoved}, eventTypes: []models.EventType{model.UserGrantRemoved},
}, },
}, },
{
name: "remove app cascade",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.UserGrant{
ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
UserID: "UserID",
ProjectID: "ProjectID",
RoleKeys: []string{"Key"}},
new: &model.UserGrant{
ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
},
cascade: true,
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventTypes: []models.EventType{model.UserGrantCascadeRemoved},
},
},
{ {
name: "existing project nil", name: "existing project nil",
args: args{ args: args{
@ -230,7 +275,7 @@ func TestUserGrantRemovedAggregate(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
aggregates, err := UserGrantRemovedAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.existing, tt.args.new) aggregates, err := UserGrantRemovedAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.existing, tt.args.new, tt.args.cascade)
if tt.res.errFunc == nil && len(aggregates[0].Events) != tt.res.eventLen { if tt.res.errFunc == nil && len(aggregates[0].Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(aggregates[0].Events)) t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(aggregates[0].Events))

View File

@ -19,6 +19,7 @@ const (
UserGrantKeyResourceOwner = "resource_owner" UserGrantKeyResourceOwner = "resource_owner"
UserGrantKeyState = "state" UserGrantKeyState = "state"
UserGrantKeyOrgName = "org_name" UserGrantKeyOrgName = "org_name"
UserGrantKeyRole = "role_keys"
) )
type UserGrantView struct { type UserGrantView struct {
@ -98,7 +99,7 @@ func (g *UserGrantView) AppendEvent(event *models.Event) (err error) {
g.CreationDate = event.CreationDate g.CreationDate = event.CreationDate
g.setRootData(event) g.setRootData(event)
err = g.setData(event) err = g.setData(event)
case es_model.UserGrantChanged: case es_model.UserGrantChanged, es_model.UserGrantCascadeChanged:
err = g.setData(event) err = g.setData(event)
case es_model.UserGrantDeactivated: case es_model.UserGrantDeactivated:
g.State = int32(model.USERGRANTSTATE_INACTIVE) g.State = int32(model.USERGRANTSTATE_INACTIVE)

View File

@ -63,6 +63,8 @@ func (key UserGrantSearchKey) ToColumnName() string {
return UserGrantKeyID return UserGrantKeyID
case grant_model.USERGRANTSEARCHKEY_ORG_NAME: case grant_model.USERGRANTSEARCHKEY_ORG_NAME:
return UserGrantKeyOrgName return UserGrantKeyOrgName
case grant_model.USERGRANTSEARCHKEY_ROLE_KEY:
return UserGrantKeyRole
default: default:
return "" return ""
} }

View File

@ -62,6 +62,34 @@ func UserGrantsByProjectID(db *gorm.DB, table, projectID string) ([]*model.UserG
return users, nil return users, nil
} }
func UserGrantsByProjectIDAndRole(db *gorm.DB, table, projectID, roleKey string) ([]*model.UserGrantView, error) {
users := make([]*model.UserGrantView, 0)
queries := []*grant_model.UserGrantSearchQuery{
&grant_model.UserGrantSearchQuery{Key: grant_model.USERGRANTSEARCHKEY_PROJECT_ID, Value: projectID, Method: global_model.SEARCHMETHOD_EQUALS},
&grant_model.UserGrantSearchQuery{Key: grant_model.USERGRANTSEARCHKEY_ROLE_KEY, Value: roleKey, Method: global_model.SEARCHMETHOD_LIST_CONTAINS},
}
query := view.PrepareSearchQuery(table, model.UserGrantSearchRequest{Queries: queries})
_, err := query(db, &users)
if err != nil {
return nil, err
}
return users, nil
}
func UserGrantsByOrgIDAndProjectID(db *gorm.DB, table, orgID, projectID string) ([]*model.UserGrantView, error) {
users := make([]*model.UserGrantView, 0)
queries := []*grant_model.UserGrantSearchQuery{
&grant_model.UserGrantSearchQuery{Key: grant_model.USERGRANTSEARCHKEY_RESOURCEOWNER, Value: orgID, Method: global_model.SEARCHMETHOD_EQUALS},
&grant_model.UserGrantSearchQuery{Key: grant_model.USERGRANTSEARCHKEY_PROJECT_ID, Value: projectID, Method: global_model.SEARCHMETHOD_EQUALS},
}
query := view.PrepareSearchQuery(table, model.UserGrantSearchRequest{Queries: queries})
_, err := query(db, &users)
if err != nil {
return nil, err
}
return users, nil
}
func UserGrantsByOrgID(db *gorm.DB, table, orgID string) ([]*model.UserGrantView, error) { func UserGrantsByOrgID(db *gorm.DB, table, orgID string) ([]*model.UserGrantView, error) {
users := make([]*model.UserGrantView, 0) users := make([]*model.UserGrantView, 0)
queries := []*grant_model.UserGrantSearchQuery{ queries := []*grant_model.UserGrantSearchQuery{

View File

@ -5,6 +5,7 @@ import (
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/model" "github.com/caos/zitadel/internal/model"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/lib/pq"
) )
type SearchRequest interface { type SearchRequest interface {
@ -102,10 +103,14 @@ func SetQuery(query *gorm.DB, key ColumnKey, value interface{}, method model.Sea
query = query.Where(column+" > ?", value) query = query.Where(column+" > ?", value)
case model.SEARCHMETHOD_LESS_THAN: case model.SEARCHMETHOD_LESS_THAN:
query = query.Where(column+" < ?", value) query = query.Where(column+" < ?", value)
case model.SEARCHMETHOD_IN: case model.SEARCHMETHOD_IS_ONE_OF:
query = query.Where(column+" IN (?)", value) query = query.Where(column+" IN (?)", value)
case model.SEARCHMETHOD_EQUALS_IN_ARRAY: case model.SEARCHMETHOD_LIST_CONTAINS:
query = query.Where("? <@ "+column, value) valueText, ok := value.(string)
if !ok {
return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-Psois", "list contains only possible for strings")
}
query = query.Where("? <@ "+column, pq.Array([]string{valueText}))
default: default:
return nil, nil return nil, nil
} }

View File

@ -0,0 +1,5 @@
BEGIN;
ALTER TABLE management.project_grants ADD COLUMN resource_owner_name TEXT;
COMMIT;

View File

@ -1,4 +1,4 @@
package grpc package grpc
//go:generate protoc -I$GOPATH/src -I../proto -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis -I${GOPATH}/src/github.com/envoyproxy/protoc-gen-validate -I${GOPATH}/src/github.com/caos/zitadel/internal/protoc/protoc-gen-authoption --go_out=plugins=grpc:$GOPATH/src --grpc-gateway_out=logtostderr=true:$GOPATH/src --swagger_out=logtostderr=true:. --authoption_out=. ../proto/management.proto //go:generate protoc -I$GOPATH/src -I../proto -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis -I${GOPATH}/src/github.com/envoyproxy/protoc-gen-validate -I${GOPATH}/src/github.com/caos/zitadel/internal/protoc/protoc-gen-authoption --go_out=plugins=grpc:$GOPATH/src --grpc-gateway_out=logtostderr=true,allow_delete_body=true:$GOPATH/src --swagger_out=logtostderr=true,allow_delete_body=true:. --authoption_out=:. ../proto/management.proto
//go:generate mockgen -package api -destination ./mock/management.proto.mock.go github.com/caos/zitadel/pkg/management/api/grpc ManagementServiceClient //go:generate mockgen -package api -destination ./mock/management.proto.mock.go github.com/caos/zitadel/pkg/management/api/grpc ManagementServiceClient

View File

@ -355,6 +355,11 @@ var ManagementService_AuthMethods = utils_auth.MethodMapping{
CheckParam: "Id", CheckParam: "Id",
}, },
"/caos.zitadel.management.api.v1.ManagementService/BulkAddProjectRole": utils_auth.Option{
Permission: "project.role.write",
CheckParam: "Id",
},
"/caos.zitadel.management.api.v1.ManagementService/ChangeProjectRole": utils_auth.Option{ "/caos.zitadel.management.api.v1.ManagementService/ChangeProjectRole": utils_auth.Option{
Permission: "project.role.write", Permission: "project.role.write",
CheckParam: "Id", CheckParam: "Id",
@ -505,6 +510,21 @@ var ManagementService_AuthMethods = utils_auth.MethodMapping{
CheckParam: "", CheckParam: "",
}, },
"/caos.zitadel.management.api.v1.ManagementService/BulkCreateUserGrant": utils_auth.Option{
Permission: "user.grant.write",
CheckParam: "",
},
"/caos.zitadel.management.api.v1.ManagementService/BulkUpdateUserGrant": utils_auth.Option{
Permission: "user.grant.write",
CheckParam: "",
},
"/caos.zitadel.management.api.v1.ManagementService/BulkRemoveUserGrant": utils_auth.Option{
Permission: "user.grant.delete",
CheckParam: "",
},
"/caos.zitadel.management.api.v1.ManagementService/SearchProjectUserGrants": utils_auth.Option{ "/caos.zitadel.management.api.v1.ManagementService/SearchProjectUserGrants": utils_auth.Option{
Permission: "project.user.grant.read", Permission: "project.user.grant.read",
CheckParam: "ProjectId", CheckParam: "ProjectId",

File diff suppressed because it is too large Load Diff

View File

@ -1844,6 +1844,41 @@ func request_ManagementService_AddProjectRole_0(ctx context.Context, marshaler r
} }
func request_ManagementService_BulkAddProjectRole_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ProjectRoleAddBulk
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
}
msg, err := client.BulkAddProjectRole(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ManagementService_ChangeProjectRole_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func request_ManagementService_ChangeProjectRole_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ProjectRoleChange var protoReq ProjectRoleChange
var metadata runtime.ServerMetadata var metadata runtime.ServerMetadata
@ -3061,6 +3096,57 @@ func request_ManagementService_RemoveUserGrant_0(ctx context.Context, marshaler
} }
func request_ManagementService_BulkCreateUserGrant_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UserGrantCreateBulk
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.BulkCreateUserGrant(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ManagementService_BulkUpdateUserGrant_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UserGrantUpdateBulk
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.BulkUpdateUserGrant(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ManagementService_BulkRemoveUserGrant_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UserGrantRemoveBulk
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.BulkRemoveUserGrant(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ManagementService_SearchProjectUserGrants_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func request_ManagementService_SearchProjectUserGrants_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ProjectUserGrantSearchRequest var protoReq ProjectUserGrantSearchRequest
var metadata runtime.ServerMetadata var metadata runtime.ServerMetadata
@ -5138,6 +5224,26 @@ func RegisterManagementServiceHandlerClient(ctx context.Context, mux *runtime.Se
}) })
mux.Handle("POST", pattern_ManagementService_BulkAddProjectRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_ManagementService_BulkAddProjectRole_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_ManagementService_BulkAddProjectRole_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_ManagementService_ChangeProjectRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle("PUT", pattern_ManagementService_ChangeProjectRole_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
@ -5738,6 +5844,66 @@ func RegisterManagementServiceHandlerClient(ctx context.Context, mux *runtime.Se
}) })
mux.Handle("POST", pattern_ManagementService_BulkCreateUserGrant_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_ManagementService_BulkCreateUserGrant_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_ManagementService_BulkCreateUserGrant_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_ManagementService_BulkUpdateUserGrant_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_ManagementService_BulkUpdateUserGrant_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_ManagementService_BulkUpdateUserGrant_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_ManagementService_BulkRemoveUserGrant_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_ManagementService_BulkRemoveUserGrant_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_ManagementService_BulkRemoveUserGrant_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_ManagementService_SearchProjectUserGrants_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle("POST", pattern_ManagementService_SearchProjectUserGrants_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
@ -6144,6 +6310,8 @@ var (
pattern_ManagementService_AddProjectRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2}, []string{"projects", "id", "roles"}, "")) pattern_ManagementService_AddProjectRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2}, []string{"projects", "id", "roles"}, ""))
pattern_ManagementService_BulkAddProjectRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2, 2, 3}, []string{"projects", "id", "roles", "_bulk"}, ""))
pattern_ManagementService_ChangeProjectRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"projects", "id", "roles", "key"}, "")) pattern_ManagementService_ChangeProjectRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"projects", "id", "roles", "key"}, ""))
pattern_ManagementService_RemoveProjectRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"projects", "id", "roles", "key"}, "")) pattern_ManagementService_RemoveProjectRole_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"projects", "id", "roles", "key"}, ""))
@ -6204,6 +6372,12 @@ var (
pattern_ManagementService_RemoveUserGrant_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"users", "user_id", "grants", "id"}, "")) pattern_ManagementService_RemoveUserGrant_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"users", "user_id", "grants", "id"}, ""))
pattern_ManagementService_BulkCreateUserGrant_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"usergrants", "_bulk"}, ""))
pattern_ManagementService_BulkUpdateUserGrant_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"usergrants", "_bulk"}, ""))
pattern_ManagementService_BulkRemoveUserGrant_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"usersgrants", "_bulk"}, ""))
pattern_ManagementService_SearchProjectUserGrants_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2, 2, 3, 2, 4}, []string{"projects", "project_id", "users", "grants", "_search"}, "")) pattern_ManagementService_SearchProjectUserGrants_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2, 2, 3, 2, 4}, []string{"projects", "project_id", "users", "grants", "_search"}, ""))
pattern_ManagementService_ProjectUserGrantByID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"projects", "project_id", "users", "user_id", "grants", "id"}, "")) pattern_ManagementService_ProjectUserGrantByID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"projects", "project_id", "users", "user_id", "grants", "id"}, ""))
@ -6374,6 +6548,8 @@ var (
forward_ManagementService_AddProjectRole_0 = runtime.ForwardResponseMessage forward_ManagementService_AddProjectRole_0 = runtime.ForwardResponseMessage
forward_ManagementService_BulkAddProjectRole_0 = runtime.ForwardResponseMessage
forward_ManagementService_ChangeProjectRole_0 = runtime.ForwardResponseMessage forward_ManagementService_ChangeProjectRole_0 = runtime.ForwardResponseMessage
forward_ManagementService_RemoveProjectRole_0 = runtime.ForwardResponseMessage forward_ManagementService_RemoveProjectRole_0 = runtime.ForwardResponseMessage
@ -6434,6 +6610,12 @@ var (
forward_ManagementService_RemoveUserGrant_0 = runtime.ForwardResponseMessage forward_ManagementService_RemoveUserGrant_0 = runtime.ForwardResponseMessage
forward_ManagementService_BulkCreateUserGrant_0 = runtime.ForwardResponseMessage
forward_ManagementService_BulkUpdateUserGrant_0 = runtime.ForwardResponseMessage
forward_ManagementService_BulkRemoveUserGrant_0 = runtime.ForwardResponseMessage
forward_ManagementService_SearchProjectUserGrants_0 = runtime.ForwardResponseMessage forward_ManagementService_SearchProjectUserGrants_0 = runtime.ForwardResponseMessage
forward_ManagementService_ProjectUserGrantByID_0 = runtime.ForwardResponseMessage forward_ManagementService_ProjectUserGrantByID_0 = runtime.ForwardResponseMessage

View File

@ -1496,8 +1496,41 @@
] ]
} }
}, },
"/projects/{id}/roles/_bulk": {
"post": {
"operationId": "BulkAddProjectRole",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"properties": {}
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v1ProjectRoleAddBulk"
}
}
],
"tags": [
"ManagementService"
]
}
},
"/projects/{id}/roles/{key}": { "/projects/{id}/roles/{key}": {
"delete": { "delete": {
"summary": "RemoveProjectRole removes role from UserGrants, ProjectGrants and from Project",
"operationId": "RemoveProjectRole", "operationId": "RemoveProjectRole",
"responses": { "responses": {
"200": { "200": {
@ -2580,6 +2613,56 @@
] ]
} }
}, },
"/usergrants/_bulk": {
"post": {
"operationId": "BulkCreateUserGrant",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"properties": {}
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v1UserGrantCreateBulk"
}
}
],
"tags": [
"ManagementService"
]
},
"put": {
"operationId": "BulkUpdateUserGrant",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"properties": {}
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v1UserGrantUpdateBulk"
}
}
],
"tags": [
"ManagementService"
]
}
},
"/users": { "/users": {
"post": { "post": {
"operationId": "CreateUser", "operationId": "CreateUser",
@ -3485,6 +3568,32 @@
] ]
} }
}, },
"/usersgrants/_bulk": {
"delete": {
"operationId": "BulkRemoveUserGrant",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"properties": {}
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v1UserGrantRemoveBulk"
}
}
],
"tags": [
"ManagementService"
]
}
},
"/validate": { "/validate": {
"get": { "get": {
"operationId": "Validate", "operationId": "Validate",
@ -5109,6 +5218,29 @@
} }
} }
}, },
"v1ProjectGrantSearchKey": {
"type": "string",
"enum": [
"PROJECTGRANTSEARCHKEY_UNSPECIFIED",
"PROJECTGRANTSEARCHKEY_PROJECT_NAME",
"PROJECTGRANTSEARCHKEY_ROLE_KEY"
],
"default": "PROJECTGRANTSEARCHKEY_UNSPECIFIED"
},
"v1ProjectGrantSearchQuery": {
"type": "object",
"properties": {
"key": {
"$ref": "#/definitions/v1ProjectGrantSearchKey"
},
"method": {
"$ref": "#/definitions/v1SearchMethod"
},
"value": {
"type": "string"
}
}
},
"v1ProjectGrantSearchRequest": { "v1ProjectGrantSearchRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -5122,6 +5254,12 @@
"limit": { "limit": {
"type": "string", "type": "string",
"format": "uint64" "format": "uint64"
},
"queries": {
"type": "array",
"items": {
"$ref": "#/definitions/v1ProjectGrantSearchQuery"
}
} }
} }
}, },
@ -5268,9 +5406,6 @@
"granted_org_name": { "granted_org_name": {
"type": "string" "type": "string"
}, },
"granted_org_domain": {
"type": "string"
},
"role_keys": { "role_keys": {
"type": "array", "type": "array",
"items": { "items": {
@ -5294,6 +5429,12 @@
"sequence": { "sequence": {
"type": "string", "type": "string",
"format": "uint64" "format": "uint64"
},
"resource_owner": {
"type": "string"
},
"resource_owner_name": {
"type": "string"
} }
} }
}, },
@ -5531,6 +5672,20 @@
} }
} }
}, },
"v1ProjectRoleAddBulk": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"project_roles": {
"type": "array",
"items": {
"$ref": "#/definitions/v1ProjectRoleAdd"
}
}
}
},
"v1ProjectRoleChange": { "v1ProjectRoleChange": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -5818,7 +5973,12 @@
"SEARCHMETHOD_CONTAINS", "SEARCHMETHOD_CONTAINS",
"SEARCHMETHOD_EQUALS_IGNORE_CASE", "SEARCHMETHOD_EQUALS_IGNORE_CASE",
"SEARCHMETHOD_STARTS_WITH_IGNORE_CASE", "SEARCHMETHOD_STARTS_WITH_IGNORE_CASE",
"SEARCHMETHOD_CONTAINS_IGNORE_CASE" "SEARCHMETHOD_CONTAINS_IGNORE_CASE",
"SEARCHMETHOD_NOT_EQUALS",
"SEARCHMETHOD_GREATER_THAN",
"SEARCHMETHOD_LESS_THAN",
"SEARCHMETHOD_IS_ONE_OF",
"SEARCHMETHOD_LIST_CONTAINS"
], ],
"default": "SEARCHMETHOD_EQUALS" "default": "SEARCHMETHOD_EQUALS"
}, },
@ -6170,6 +6330,17 @@
} }
} }
}, },
"v1UserGrantCreateBulk": {
"type": "object",
"properties": {
"user_grants": {
"type": "array",
"items": {
"$ref": "#/definitions/v1UserGrantCreate"
}
}
}
},
"v1UserGrantID": { "v1UserGrantID": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -6181,13 +6352,25 @@
} }
} }
}, },
"v1UserGrantRemoveBulk": {
"type": "object",
"properties": {
"ids": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"v1UserGrantSearchKey": { "v1UserGrantSearchKey": {
"type": "string", "type": "string",
"enum": [ "enum": [
"USERGRANTSEARCHKEY_UNSPECIFIED", "USERGRANTSEARCHKEY_UNSPECIFIED",
"USERGRANTSEARCHKEY_PROJECT_ID", "USERGRANTSEARCHKEY_PROJECT_ID",
"USERGRANTSEARCHKEY_USER_ID", "USERGRANTSEARCHKEY_USER_ID",
"USERGRANTSEARCHKEY_ORG_ID" "USERGRANTSEARCHKEY_ORG_ID",
"USERGRANTSEARCHKEY_ROLE_KEY"
], ],
"default": "USERGRANTSEARCHKEY_UNSPECIFIED" "default": "USERGRANTSEARCHKEY_UNSPECIFIED"
}, },
@ -6273,6 +6456,17 @@
} }
} }
}, },
"v1UserGrantUpdateBulk": {
"type": "object",
"properties": {
"user_grants": {
"type": "array",
"items": {
"$ref": "#/definitions/v1UserGrantUpdate"
}
}
}
},
"v1UserGrantView": { "v1UserGrantView": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -177,6 +177,86 @@ func (mr *MockManagementServiceClientMockRecorder) ApplicationChanges(arg0, arg1
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplicationChanges", reflect.TypeOf((*MockManagementServiceClient)(nil).ApplicationChanges), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplicationChanges", reflect.TypeOf((*MockManagementServiceClient)(nil).ApplicationChanges), varargs...)
} }
// BulkAddProjectRole mocks base method
func (m *MockManagementServiceClient) BulkAddProjectRole(arg0 context.Context, arg1 *grpc.ProjectRoleAddBulk, arg2 ...grpc0.CallOption) (*emptypb.Empty, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "BulkAddProjectRole", varargs...)
ret0, _ := ret[0].(*emptypb.Empty)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BulkAddProjectRole indicates an expected call of BulkAddProjectRole
func (mr *MockManagementServiceClientMockRecorder) BulkAddProjectRole(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BulkAddProjectRole", reflect.TypeOf((*MockManagementServiceClient)(nil).BulkAddProjectRole), varargs...)
}
// BulkCreateUserGrant mocks base method
func (m *MockManagementServiceClient) BulkCreateUserGrant(arg0 context.Context, arg1 *grpc.UserGrantCreateBulk, arg2 ...grpc0.CallOption) (*emptypb.Empty, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "BulkCreateUserGrant", varargs...)
ret0, _ := ret[0].(*emptypb.Empty)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BulkCreateUserGrant indicates an expected call of BulkCreateUserGrant
func (mr *MockManagementServiceClientMockRecorder) BulkCreateUserGrant(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BulkCreateUserGrant", reflect.TypeOf((*MockManagementServiceClient)(nil).BulkCreateUserGrant), varargs...)
}
// BulkRemoveUserGrant mocks base method
func (m *MockManagementServiceClient) BulkRemoveUserGrant(arg0 context.Context, arg1 *grpc.UserGrantRemoveBulk, arg2 ...grpc0.CallOption) (*emptypb.Empty, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "BulkRemoveUserGrant", varargs...)
ret0, _ := ret[0].(*emptypb.Empty)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BulkRemoveUserGrant indicates an expected call of BulkRemoveUserGrant
func (mr *MockManagementServiceClientMockRecorder) BulkRemoveUserGrant(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BulkRemoveUserGrant", reflect.TypeOf((*MockManagementServiceClient)(nil).BulkRemoveUserGrant), varargs...)
}
// BulkUpdateUserGrant mocks base method
func (m *MockManagementServiceClient) BulkUpdateUserGrant(arg0 context.Context, arg1 *grpc.UserGrantUpdateBulk, arg2 ...grpc0.CallOption) (*emptypb.Empty, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "BulkUpdateUserGrant", varargs...)
ret0, _ := ret[0].(*emptypb.Empty)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BulkUpdateUserGrant indicates an expected call of BulkUpdateUserGrant
func (mr *MockManagementServiceClientMockRecorder) BulkUpdateUserGrant(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BulkUpdateUserGrant", reflect.TypeOf((*MockManagementServiceClient)(nil).BulkUpdateUserGrant), varargs...)
}
// ChangeMyOrgMember mocks base method // ChangeMyOrgMember mocks base method
func (m *MockManagementServiceClient) ChangeMyOrgMember(arg0 context.Context, arg1 *grpc.ChangeOrgMemberRequest, arg2 ...grpc0.CallOption) (*grpc.OrgMember, error) { func (m *MockManagementServiceClient) ChangeMyOrgMember(arg0 context.Context, arg1 *grpc.ChangeOrgMemberRequest, arg2 ...grpc0.CallOption) (*grpc.OrgMember, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -81,6 +81,12 @@ func (s *Server) AddProjectRole(ctx context.Context, in *ProjectRoleAdd) (*Proje
} }
return projectRoleFromModel(role), nil return projectRoleFromModel(role), nil
} }
func (s *Server) BulkAddProjectRole(ctx context.Context, in *ProjectRoleAddBulk) (*empty.Empty, error) {
err := s.project.BulkAddProjectRole(ctx, projectRoleAddBulkToModel(in))
return &empty.Empty{}, err
}
func (s *Server) ChangeProjectRole(ctx context.Context, in *ProjectRoleChange) (*ProjectRole, error) { func (s *Server) ChangeProjectRole(ctx context.Context, in *ProjectRoleChange) (*ProjectRole, error) {
role, err := s.project.ChangeProjectRole(ctx, projectRoleChangeToModel(in)) role, err := s.project.ChangeProjectRole(ctx, projectRoleChangeToModel(in))
if err != nil { if err != nil {

View File

@ -131,6 +131,21 @@ func projectRoleFromModel(role *proj_model.ProjectRole) *ProjectRole {
} }
} }
func projectRoleAddBulkToModel(bulk *ProjectRoleAddBulk) []*proj_model.ProjectRole {
roles := make([]*proj_model.ProjectRole, len(bulk.ProjectRoles))
for i, role := range bulk.ProjectRoles {
roles[i] = &proj_model.ProjectRole{
ObjectRoot: models.ObjectRoot{
AggregateID: bulk.Id,
},
Key: role.Key,
DisplayName: role.DisplayName,
Group: role.Group,
}
}
return roles
}
func projectRoleAddToModel(role *ProjectRoleAdd) *proj_model.ProjectRole { func projectRoleAddToModel(role *ProjectRoleAdd) *proj_model.ProjectRole {
return &proj_model.ProjectRole{ return &proj_model.ProjectRole{
ObjectRoot: models.ObjectRoot{ ObjectRoot: models.ObjectRoot{

View File

@ -51,17 +51,40 @@ func projectGrantSearchRequestsToModel(request *ProjectGrantSearchRequest) *proj
return &proj_model.ProjectGrantViewSearchRequest{ return &proj_model.ProjectGrantViewSearchRequest{
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
Queries: projectGrantSearchQueriesToModel(request.ProjectId), Queries: projectGrantSearchQueriesToModel(request.ProjectId, request.Queries),
} }
} }
func projectGrantSearchQueriesToModel(projectId string) []*proj_model.ProjectGrantViewSearchQuery { func projectGrantSearchQueriesToModel(projectId string, queries []*ProjectGrantSearchQuery) []*proj_model.ProjectGrantViewSearchQuery {
converted := make([]*proj_model.ProjectGrantViewSearchQuery, 0) converted := make([]*proj_model.ProjectGrantViewSearchQuery, 0)
return append(converted, &proj_model.ProjectGrantViewSearchQuery{ converted = append(converted, &proj_model.ProjectGrantViewSearchQuery{
Key: proj_model.GRANTEDPROJECTSEARCHKEY_PROJECTID, Key: proj_model.GRANTEDPROJECTSEARCHKEY_PROJECTID,
Method: model.SEARCHMETHOD_EQUALS, Method: model.SEARCHMETHOD_EQUALS,
Value: projectId, Value: projectId,
}) })
for i, query := range queries {
converted[i] = projectGrantSearchQueryToModel(query)
}
return converted
}
func projectGrantSearchQueryToModel(query *ProjectGrantSearchQuery) *proj_model.ProjectGrantViewSearchQuery {
return &proj_model.ProjectGrantViewSearchQuery{
Key: projectGrantViewSearchKeyToModel(query.Key),
Method: searchMethodToModel(query.Method),
Value: query.Value,
}
}
func projectGrantViewSearchKeyToModel(key ProjectGrantSearchKey) proj_model.ProjectGrantViewSearchKey {
switch key {
case ProjectGrantSearchKey_PROJECTGRANTSEARCHKEY_PROJECT_NAME:
return proj_model.GRANTEDPROJECTSEARCHKEY_PROJECTID
case ProjectGrantSearchKey_PROJECTGRANTSEARCHKEY_ROLE_KEY:
return proj_model.GRANTEDPROJECTSEARCHKEY_ROLE_KEYS
default:
return proj_model.GRANTEDPROJECTSEARCHKEY_UNSPECIFIED
}
} }
func projectGrantSearchResponseFromModel(response *proj_model.ProjectGrantViewSearchResponse) *ProjectGrantSearchResponse { func projectGrantSearchResponseFromModel(response *proj_model.ProjectGrantViewSearchResponse) *ProjectGrantSearchResponse {
@ -89,16 +112,18 @@ func projectGrantFromGrantedProjectModel(project *proj_model.ProjectGrantView) *
logging.Log("GRPC-sope3").OnError(err).Debug("unable to parse timestamp") logging.Log("GRPC-sope3").OnError(err).Debug("unable to parse timestamp")
return &ProjectGrantView{ return &ProjectGrantView{
ProjectId: project.ProjectID, ProjectId: project.ProjectID,
State: projectGrantStateFromProjectStateModel(project.State), State: projectGrantStateFromProjectStateModel(project.State),
CreationDate: creationDate, CreationDate: creationDate,
ChangeDate: changeDate, ChangeDate: changeDate,
ProjectName: project.Name, ProjectName: project.Name,
Sequence: project.Sequence, Sequence: project.Sequence,
GrantedOrgId: project.OrgID, GrantedOrgId: project.OrgID,
GrantedOrgName: project.OrgName, GrantedOrgName: project.OrgName,
GrantedOrgDomain: project.OrgDomain, Id: project.GrantID,
Id: project.GrantID, RoleKeys: project.GrantedRoleKeys,
ResourceOwner: project.ResourceOwner,
ResourceOwnerName: project.ResourceOwnerName,
} }
} }

View File

@ -16,6 +16,12 @@ func searchMethodToModel(method SearchMethod) model.SearchMethod {
return model.SEARCHMETHOD_CONTAINS_IGNORE_CASE return model.SEARCHMETHOD_CONTAINS_IGNORE_CASE
case SearchMethod_SEARCHMETHOD_STARTS_WITH_IGNORE_CASE: case SearchMethod_SEARCHMETHOD_STARTS_WITH_IGNORE_CASE:
return model.SEARCHMETHOD_STARTS_WITH_IGNORE_CASE return model.SEARCHMETHOD_STARTS_WITH_IGNORE_CASE
case SearchMethod_SEARCHMETHOD_NOT_EQUALS:
return model.SEARCHMETHOD_NOT_EQUALS
case SearchMethod_SEARCHMETHOD_IS_ONE_OF:
return model.SEARCHMETHOD_IS_ONE_OF
case SearchMethod_SEARCHMETHOD_LIST_CONTAINS:
return model.SEARCHMETHOD_LIST_CONTAINS
default: default:
return model.SEARCHMETHOD_EQUALS return model.SEARCHMETHOD_EQUALS
} }

View File

@ -2,12 +2,14 @@ package grpc
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/api/auth"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
"github.com/golang/protobuf/ptypes/empty" "github.com/golang/protobuf/ptypes/empty"
) )
func (s *Server) SearchUserGrants(ctx context.Context, in *UserGrantSearchRequest) (*UserGrantSearchResponse, error) { func (s *Server) SearchUserGrants(ctx context.Context, in *UserGrantSearchRequest) (*UserGrantSearchResponse, error) {
request := userGrantSearchRequestsToModel(in) request := userGrantSearchRequestsToModel(in)
request.AppendMyOrgQuery(auth.GetCtxData(ctx).OrgID)
response, err := s.usergrant.SearchUserGrants(ctx, request) response, err := s.usergrant.SearchUserGrants(ctx, request)
if err != nil { if err != nil {
return nil, err return nil, err
@ -30,6 +32,7 @@ func (s *Server) CreateUserGrant(ctx context.Context, in *UserGrantCreate) (*Use
} }
return usergrantFromModel(user), nil return usergrantFromModel(user), nil
} }
func (s *Server) UpdateUserGrant(ctx context.Context, in *UserGrantUpdate) (*UserGrant, error) { func (s *Server) UpdateUserGrant(ctx context.Context, in *UserGrantUpdate) (*UserGrant, error) {
user, err := s.usergrant.ChangeUserGrant(ctx, userGrantUpdateToModel(in)) user, err := s.usergrant.ChangeUserGrant(ctx, userGrantUpdateToModel(in))
if err != nil { if err != nil {
@ -57,6 +60,21 @@ func (s *Server) RemoveUserGrant(ctx context.Context, in *UserGrantID) (*empty.E
return &empty.Empty{}, err return &empty.Empty{}, err
} }
func (s *Server) BulkCreateUserGrant(ctx context.Context, in *UserGrantCreateBulk) (*empty.Empty, error) {
err := s.usergrant.BulkAddUserGrant(ctx, userGrantCreateBulkToModel(in)...)
return &empty.Empty{}, err
}
func (s *Server) BulkUpdateUserGrant(ctx context.Context, in *UserGrantUpdateBulk) (*empty.Empty, error) {
err := s.usergrant.BulkChangeUserGrant(ctx, userGrantUpdateBulkToModel(in)...)
return &empty.Empty{}, err
}
func (s *Server) BulkRemoveUserGrant(ctx context.Context, in *UserGrantRemoveBulk) (*empty.Empty, error) {
err := s.usergrant.BulkRemoveUserGrant(ctx, userGrantRemoveBulkToModel(in)...)
return &empty.Empty{}, err
}
func (s *Server) SearchProjectUserGrants(ctx context.Context, request *ProjectUserGrantSearchRequest) (*UserGrantSearchResponse, error) { func (s *Server) SearchProjectUserGrants(ctx context.Context, request *ProjectUserGrantSearchRequest) (*UserGrantSearchResponse, error) {
return nil, errors.ThrowUnimplemented(nil, "GRPC-8jdSw", "Not implemented") return nil, errors.ThrowUnimplemented(nil, "GRPC-8jdSw", "Not implemented")
} }

View File

@ -26,6 +26,14 @@ func usergrantFromModel(grant *grant_model.UserGrant) *UserGrant {
} }
} }
func userGrantCreateBulkToModel(u *UserGrantCreateBulk) []*grant_model.UserGrant {
grants := make([]*grant_model.UserGrant, len(u.UserGrants))
for i, grant := range u.UserGrants {
grants[i] = userGrantCreateToModel(grant)
}
return grants
}
func userGrantCreateToModel(u *UserGrantCreate) *grant_model.UserGrant { func userGrantCreateToModel(u *UserGrantCreate) *grant_model.UserGrant {
return &grant_model.UserGrant{ return &grant_model.UserGrant{
ObjectRoot: models.ObjectRoot{AggregateID: u.UserId}, ObjectRoot: models.ObjectRoot{AggregateID: u.UserId},
@ -35,6 +43,14 @@ func userGrantCreateToModel(u *UserGrantCreate) *grant_model.UserGrant {
} }
} }
func userGrantUpdateBulkToModel(u *UserGrantUpdateBulk) []*grant_model.UserGrant {
grants := make([]*grant_model.UserGrant, len(u.UserGrants))
for i, grant := range u.UserGrants {
grants[i] = userGrantUpdateToModel(grant)
}
return grants
}
func userGrantUpdateToModel(u *UserGrantUpdate) *grant_model.UserGrant { func userGrantUpdateToModel(u *UserGrantUpdate) *grant_model.UserGrant {
return &grant_model.UserGrant{ return &grant_model.UserGrant{
ObjectRoot: models.ObjectRoot{AggregateID: u.Id}, ObjectRoot: models.ObjectRoot{AggregateID: u.Id},
@ -42,6 +58,14 @@ func userGrantUpdateToModel(u *UserGrantUpdate) *grant_model.UserGrant {
} }
} }
func userGrantRemoveBulkToModel(u *UserGrantRemoveBulk) []string {
ids := make([]string, len(u.Ids))
for i, id := range u.Ids {
ids[i] = id
}
return ids
}
func projectUserGrantUpdateToModel(u *ProjectUserGrantUpdate) *grant_model.UserGrant { func projectUserGrantUpdateToModel(u *ProjectUserGrantUpdate) *grant_model.UserGrant {
return &grant_model.UserGrant{ return &grant_model.UserGrant{
ObjectRoot: models.ObjectRoot{AggregateID: u.Id}, ObjectRoot: models.ObjectRoot{AggregateID: u.Id},
@ -96,6 +120,8 @@ func userGrantSearchKeyToModel(key UserGrantSearchKey) grant_model.UserGrantSear
return grant_model.USERGRANTSEARCHKEY_PROJECT_ID return grant_model.USERGRANTSEARCHKEY_PROJECT_ID
case UserGrantSearchKey_USERGRANTSEARCHKEY_USER_ID: case UserGrantSearchKey_USERGRANTSEARCHKEY_USER_ID:
return grant_model.USERGRANTSEARCHKEY_USER_ID return grant_model.USERGRANTSEARCHKEY_USER_ID
case UserGrantSearchKey_USERGRANTSEARCHKEY_ROLE_KEY:
return grant_model.USERGRANTSEARCHKEY_ROLE_KEY
default: default:
return grant_model.USERGRANTSEARCHKEY_UNSPECIFIED return grant_model.USERGRANTSEARCHKEY_UNSPECIFIED
} }

View File

@ -801,6 +801,18 @@ service ManagementService {
}; };
} }
rpc BulkAddProjectRole(ProjectRoleAddBulk) returns (google.protobuf.Empty) {
option (google.api.http) = {
post: "/projects/{id}/roles/_bulk"
body: "*"
};
option (caos.zitadel.utils.v1.auth_option) = {
permission: "project.role.write"
check_field_name: "Id"
};
}
rpc ChangeProjectRole(ProjectRoleChange) returns (ProjectRole) { rpc ChangeProjectRole(ProjectRoleChange) returns (ProjectRole) {
option (google.api.http) = { option (google.api.http) = {
put: "/projects/{id}/roles/{key}" put: "/projects/{id}/roles/{key}"
@ -813,6 +825,7 @@ service ManagementService {
}; };
} }
/* RemoveProjectRole removes role from UserGrants, ProjectGrants and from Project */
rpc RemoveProjectRole(ProjectRoleRemove) returns (google.protobuf.Empty) { rpc RemoveProjectRole(ProjectRoleRemove) returns (google.protobuf.Empty) {
option (google.api.http) = { option (google.api.http) = {
delete: "/projects/{id}/roles/{key}" delete: "/projects/{id}/roles/{key}"
@ -1137,6 +1150,39 @@ service ManagementService {
}; };
} }
rpc BulkCreateUserGrant(UserGrantCreateBulk) returns (google.protobuf.Empty) {
option (google.api.http) = {
post: "/usergrants/_bulk"
body: "*"
};
option (caos.zitadel.utils.v1.auth_option) = {
permission: "user.grant.write"
};
}
rpc BulkUpdateUserGrant(UserGrantUpdateBulk) returns (google.protobuf.Empty) {
option (google.api.http) = {
put: "/usergrants/_bulk"
body: "*"
};
option (caos.zitadel.utils.v1.auth_option) = {
permission: "user.grant.write"
};
}
rpc BulkRemoveUserGrant(UserGrantRemoveBulk) returns (google.protobuf.Empty) {
option (google.api.http) = {
delete: "/usersgrants/_bulk"
body: "*"
};
option (caos.zitadel.utils.v1.auth_option) = {
permission: "user.grant.delete"
};
}
//PROJECT_USER_GRANT //PROJECT_USER_GRANT
rpc SearchProjectUserGrants(ProjectUserGrantSearchRequest) returns (UserGrantSearchResponse) { rpc SearchProjectUserGrants(ProjectUserGrantSearchRequest) returns (UserGrantSearchResponse) {
option (google.api.http) = { option (google.api.http) = {
@ -1470,6 +1516,11 @@ enum SearchMethod {
SEARCHMETHOD_EQUALS_IGNORE_CASE = 3; SEARCHMETHOD_EQUALS_IGNORE_CASE = 3;
SEARCHMETHOD_STARTS_WITH_IGNORE_CASE = 4; SEARCHMETHOD_STARTS_WITH_IGNORE_CASE = 4;
SEARCHMETHOD_CONTAINS_IGNORE_CASE = 5; SEARCHMETHOD_CONTAINS_IGNORE_CASE = 5;
SEARCHMETHOD_NOT_EQUALS = 6;
SEARCHMETHOD_GREATER_THAN = 7;
SEARCHMETHOD_LESS_THAN = 8;
SEARCHMETHOD_IS_ONE_OF = 9;
SEARCHMETHOD_LIST_CONTAINS = 10;
} }
message UserProfile { message UserProfile {
@ -1993,6 +2044,11 @@ message ProjectRoleAdd {
string group = 4; string group = 4;
} }
message ProjectRoleAddBulk {
string id = 1;
repeated ProjectRoleAdd project_roles = 2;
}
message ProjectRoleChange { message ProjectRoleChange {
string id = 1; string id = 1;
string key = 2; string key = 2;
@ -2252,13 +2308,14 @@ message ProjectGrantView {
string project_id = 2; string project_id = 2;
string granted_org_id = 3; string granted_org_id = 3;
string granted_org_name = 4; string granted_org_name = 4;
string granted_org_domain = 5; repeated string role_keys = 5;
repeated string role_keys = 6; ProjectGrantState state = 6;
ProjectGrantState state = 7; google.protobuf.Timestamp creation_date = 7;
google.protobuf.Timestamp creation_date = 8; google.protobuf.Timestamp change_date = 8;
google.protobuf.Timestamp change_date = 9; string project_name = 9;
string project_name = 10; uint64 sequence = 10;
uint64 sequence = 11; string resource_owner = 11;
string resource_owner_name = 12;
} }
message ProjectGrantSearchResponse { message ProjectGrantSearchResponse {
@ -2268,18 +2325,32 @@ message ProjectGrantSearchResponse {
repeated ProjectGrantView result = 4; repeated ProjectGrantView result = 4;
} }
message ProjectGrantSearchRequest {
string project_id = 1;
uint64 offset = 2;
uint64 limit = 3;
}
message GrantedProjectSearchRequest { message GrantedProjectSearchRequest {
uint64 offset = 1; uint64 offset = 1;
uint64 limit = 2; uint64 limit = 2;
repeated ProjectSearchQuery queries = 3; repeated ProjectSearchQuery queries = 3;
} }
message ProjectGrantSearchRequest {
string project_id = 1;
uint64 offset = 2;
uint64 limit = 3;
repeated ProjectGrantSearchQuery queries = 4;
}
message ProjectGrantSearchQuery {
ProjectGrantSearchKey key = 1 [(validate.rules).enum = {not_in: [0]}];
SearchMethod method = 2;
string value = 3;
}
enum ProjectGrantSearchKey {
PROJECTGRANTSEARCHKEY_UNSPECIFIED = 0;
PROJECTGRANTSEARCHKEY_PROJECT_NAME = 1;
PROJECTGRANTSEARCHKEY_ROLE_KEY = 2;
}
message ProjectGrantMemberRoles { message ProjectGrantMemberRoles {
repeated string roles = 1; repeated string roles = 1;
} }
@ -2366,18 +2437,30 @@ message UserGrant {
uint64 sequence = 9; uint64 sequence = 9;
} }
message UserGrantCreateBulk {
repeated UserGrantCreate user_grants = 1;
}
message UserGrantCreate { message UserGrantCreate {
string user_id = 1; string user_id = 1;
string project_id = 2; string project_id = 2;
repeated string role_keys = 3; repeated string role_keys = 3;
} }
message UserGrantUpdateBulk {
repeated UserGrantUpdate user_grants = 1;
}
message UserGrantUpdate { message UserGrantUpdate {
string user_id = 1; string user_id = 1;
string id = 2; string id = 2;
repeated string role_keys = 3; repeated string role_keys = 3;
} }
message UserGrantRemoveBulk {
repeated string ids = 1;
}
message UserGrantID { message UserGrantID {
string user_id = 1; string user_id = 1;
string id = 2; string id = 2;
@ -2467,6 +2550,7 @@ enum UserGrantSearchKey {
USERGRANTSEARCHKEY_PROJECT_ID = 1; USERGRANTSEARCHKEY_PROJECT_ID = 1;
USERGRANTSEARCHKEY_USER_ID = 2; USERGRANTSEARCHKEY_USER_ID = 2;
USERGRANTSEARCHKEY_ORG_ID = 3; USERGRANTSEARCHKEY_ORG_ID = 3;
USERGRANTSEARCHKEY_ROLE_KEY = 4;
} }
message ProjectUserGrantSearchRequest { message ProjectUserGrantSearchRequest {