zitadel/internal/v2/command/project_grant.go
Fabi ee86eb4399
feat: migrate unique constraints v1 to v2 (#1274)
* fix: unique migration

* fix: unique migration

* fix: remove migrations

* fix: member events
2021-02-15 13:31:24 +01:00

218 lines
8.0 KiB
Go

package command
import (
"context"
"github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/project"
"reflect"
)
func (r *CommandSide) AddProjectGrant(ctx context.Context, grant *domain.ProjectGrant, resourceOwner string) (_ *domain.ProjectGrant, err error) {
if !grant.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-Bff2g", "Errors.Project.Grant.Invalid")
}
grant.GrantID, err = r.idGenerator.Next()
if err != nil {
return nil, err
}
err = r.checkProjectExists(ctx, grant.AggregateID, resourceOwner)
if err != nil {
return nil, err
}
err = r.checkOrgExists(ctx, grant.GrantedOrgID)
if err != nil {
return nil, err
}
addedGrant := NewProjectGrantWriteModel(grant.GrantID, grant.AggregateID, resourceOwner)
projectAgg := ProjectAggregateFromWriteModel(&addedGrant.WriteModel)
projectAgg.PushEvents(project.NewGrantAddedEvent(ctx, grant.GrantID, grant.GrantedOrgID, grant.AggregateID, grant.RoleKeys))
if err != nil {
return nil, err
}
err = r.eventstore.PushAggregate(ctx, addedGrant, projectAgg)
if err != nil {
return nil, err
}
return projectGrantWriteModelToProjectGrant(addedGrant), nil
}
func (r *CommandSide) ChangeProjectGrant(ctx context.Context, grant *domain.ProjectGrant, resourceOwner string, cascadeUserGrantIDs ...string) (_ *domain.ProjectGrant, err error) {
if grant.GrantID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-1j83s", "Errors.IDMissing")
}
err = r.checkProjectExists(ctx, grant.AggregateID, resourceOwner)
if err != nil {
return nil, err
}
existingGrant, err := r.projectGrantWriteModelByID(ctx, grant.GrantID, grant.AggregateID, resourceOwner)
if err != nil {
return nil, err
}
projectAgg := ProjectAggregateFromWriteModel(&existingGrant.WriteModel)
if reflect.DeepEqual(existingGrant.RoleKeys, grant.RoleKeys) {
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-0o0pL", "Errors.NoChangesFoundc")
}
projectAgg.PushEvents(project.NewGrantChangedEvent(ctx, grant.GrantID, grant.RoleKeys))
removedRoles := domain.GetRemovedRoles(existingGrant.RoleKeys, grant.RoleKeys)
if len(removedRoles) == 0 {
err = r.eventstore.PushAggregate(ctx, existingGrant, projectAgg)
if err != nil {
return nil, err
}
return projectGrantWriteModelToProjectGrant(existingGrant), nil
}
aggregates := make([]eventstore.Aggregater, 0)
aggregates = append(aggregates, projectAgg)
for _, userGrantID := range cascadeUserGrantIDs {
grantAgg, _, err := r.removeRoleFromUserGrant(ctx, userGrantID, removedRoles, true)
if err != nil {
continue
}
aggregates = append(aggregates, grantAgg)
}
resultEvents, err := r.eventstore.PushAggregates(ctx, aggregates...)
if err != nil {
return nil, err
}
existingGrant.AppendEvents(resultEvents...)
err = existingGrant.Reduce()
if err != nil {
return nil, err
}
return projectGrantWriteModelToProjectGrant(existingGrant), nil
}
func (r *CommandSide) removeRoleFromProjectGrant(ctx context.Context, projectAgg *project.Aggregate, projectID, projectGrantID, roleKey string, cascade bool) (_ *ProjectGrantWriteModel, err error) {
existingProjectGrant, err := r.projectGrantWriteModelByID(ctx, projectID, projectGrantID, "")
if err != nil {
return nil, err
}
if existingProjectGrant.State == domain.ProjectGrantStateUnspecified || existingProjectGrant.State == domain.ProjectGrantStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.Project.Grant.NotFound")
}
keyExists := false
for i, key := range existingProjectGrant.RoleKeys {
if key == roleKey {
keyExists = true
copy(existingProjectGrant.RoleKeys[i:], existingProjectGrant.RoleKeys[i+1:])
existingProjectGrant.RoleKeys[len(existingProjectGrant.RoleKeys)-1] = ""
existingProjectGrant.RoleKeys = existingProjectGrant.RoleKeys[:len(existingProjectGrant.RoleKeys)-1]
continue
}
}
if !keyExists {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5m8g9", "Errors.Project.Grant.RoleKeyNotFound")
}
changedProjectGrant := NewProjectGrantWriteModel(projectGrantID, projectID, existingProjectGrant.ResourceOwner)
if !cascade {
projectAgg.PushEvents(
project.NewGrantChangedEvent(ctx, projectGrantID, existingProjectGrant.RoleKeys),
)
} else {
projectAgg.PushEvents(
project.NewGrantCascadeChangedEvent(ctx, projectGrantID, existingProjectGrant.RoleKeys),
)
}
return changedProjectGrant, nil
}
func (r *CommandSide) DeactivateProjectGrant(ctx context.Context, projectID, grantID, resourceOwner string) (err error) {
if grantID == "" || projectID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-p0s4V", "Errors.IDMissing")
}
err = r.checkProjectExists(ctx, projectID, resourceOwner)
if err != nil {
return err
}
existingGrant, err := r.projectGrantWriteModelByID(ctx, grantID, projectID, resourceOwner)
if err != nil {
return err
}
if existingGrant.State != domain.ProjectGrantStateActive {
return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-47fu8", "Errors.Project.Grant.NotActive")
}
projectAgg := ProjectAggregateFromWriteModel(&existingGrant.WriteModel)
projectAgg.PushEvents(project.NewGrantDeactivateEvent(ctx, grantID))
return r.eventstore.PushAggregate(ctx, existingGrant, projectAgg)
}
func (r *CommandSide) ReactivateProjectGrant(ctx context.Context, projectID, grantID, resourceOwner string) (err error) {
if grantID == "" || projectID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-p0s4V", "Errors.IDMissing")
}
err = r.checkProjectExists(ctx, projectID, resourceOwner)
if err != nil {
return err
}
existingGrant, err := r.projectGrantWriteModelByID(ctx, grantID, projectID, resourceOwner)
if err != nil {
return err
}
if existingGrant.State != domain.ProjectGrantStateInactive {
return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-47fu8", "Errors.Project.Grant.NotInactive")
}
projectAgg := ProjectAggregateFromWriteModel(&existingGrant.WriteModel)
projectAgg.PushEvents(project.NewGrantReactivatedEvent(ctx, grantID))
return r.eventstore.PushAggregate(ctx, existingGrant, projectAgg)
}
func (r *CommandSide) RemoveProjectGrant(ctx context.Context, projectID, grantID, resourceOwner string, cascadeUserGrantIDs ...string) (err error) {
if grantID == "" || projectID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "PROJECT-1m9fJ", "Errors.IDMissing")
}
err = r.checkProjectExists(ctx, projectID, resourceOwner)
if err != nil {
return err
}
existingGrant, err := r.projectGrantWriteModelByID(ctx, grantID, projectID, resourceOwner)
if err != nil {
return err
}
aggregates := make([]eventstore.Aggregater, 0)
projectAgg := ProjectAggregateFromWriteModel(&existingGrant.WriteModel)
projectAgg.PushEvents(project.NewGrantRemovedEvent(ctx, grantID, existingGrant.GrantedOrgID, projectID))
aggregates = append(aggregates, projectAgg)
for _, userGrantID := range cascadeUserGrantIDs {
grantAgg, _, err := r.removeUserGrant(ctx, userGrantID, "", true)
if err != nil {
logging.LogWithFields("COMMAND-3m8sG", "usergrantid", grantID).WithError(err).Warn("could not cascade remove user grant")
continue
}
aggregates = append(aggregates, grantAgg)
}
_, err = r.eventstore.PushAggregates(ctx, aggregates...)
return err
}
func (r *CommandSide) projectGrantWriteModelByID(ctx context.Context, grantID, projectID, resourceOwner string) (member *ProjectGrantWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel := NewProjectGrantWriteModel(grantID, projectID, resourceOwner)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
if writeModel.State == domain.ProjectGrantStateUnspecified || writeModel.State == domain.ProjectGrantStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "PROJECT-D8JxR", "Errors.Project.Grant.NotFound")
}
return writeModel, nil
}