mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:27:32 +00:00
chore: move the go code into a subfolder
This commit is contained in:
818
apps/api/internal/command/org.go
Normal file
818
apps/api/internal/command/org.go
Normal file
@@ -0,0 +1,818 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
http_util "github.com/zitadel/zitadel/internal/api/http"
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/org"
|
||||
"github.com/zitadel/zitadel/internal/repository/project"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
// InstanceOrgSetup is used for the first organisation in the instance setup.
|
||||
// It used to be called OrgSetup, which now allows multiple Users, but it's used in the config.yaml and therefore
|
||||
// a breaking change was not possible.
|
||||
type InstanceOrgSetup struct {
|
||||
Name string
|
||||
CustomDomain string
|
||||
Human *AddHuman
|
||||
Machine *AddMachine
|
||||
LoginClient *AddLoginClient
|
||||
Roles []string
|
||||
}
|
||||
|
||||
type AddLoginClient struct {
|
||||
Machine *Machine
|
||||
Pat *AddPat
|
||||
}
|
||||
|
||||
type OrgSetup struct {
|
||||
Name string
|
||||
CustomDomain string
|
||||
Admins []*OrgSetupAdmin
|
||||
OrgID string
|
||||
}
|
||||
|
||||
// OrgSetupAdmin describes a user to be created (Human / Machine) or an existing (ID) to be used for an org setup.
|
||||
type OrgSetupAdmin struct {
|
||||
ID string
|
||||
Human *AddHuman
|
||||
Machine *AddMachine
|
||||
Roles []string
|
||||
}
|
||||
|
||||
type orgSetupCommands struct {
|
||||
validations []preparation.Validation
|
||||
aggregate *org.Aggregate
|
||||
commands *Commands
|
||||
|
||||
admins []*OrgSetupAdmin
|
||||
pats []*PersonalAccessToken
|
||||
machineKeys []*MachineKey
|
||||
}
|
||||
|
||||
type CreatedOrg struct {
|
||||
ObjectDetails *domain.ObjectDetails
|
||||
OrgAdmins []OrgAdmin
|
||||
}
|
||||
|
||||
type OrgAdmin interface {
|
||||
GetID() string
|
||||
}
|
||||
|
||||
type CreatedOrgAdmin struct {
|
||||
ID string
|
||||
EmailCode *string
|
||||
PhoneCode *string
|
||||
PAT *PersonalAccessToken
|
||||
MachineKey *MachineKey
|
||||
}
|
||||
|
||||
func (a *CreatedOrgAdmin) GetID() string {
|
||||
return a.ID
|
||||
}
|
||||
|
||||
type AssignedOrgAdmin struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (a *AssignedOrgAdmin) GetID() string {
|
||||
return a.ID
|
||||
}
|
||||
|
||||
func (o *OrgSetup) Validate() (err error) {
|
||||
if o.OrgID != "" && strings.TrimSpace(o.OrgID) == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "ORG-4ABd3", "Errors.Invalid.Argument")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) setUpOrgWithIDs(ctx context.Context, o *OrgSetup, orgID string, allowInitialMail bool, userIDs ...string) (_ *CreatedOrg, err error) {
|
||||
cmds := c.newOrgSetupCommands(ctx, orgID, o)
|
||||
for _, admin := range o.Admins {
|
||||
if err = cmds.setupOrgAdmin(admin, allowInitialMail); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err = cmds.addCustomDomain(o.CustomDomain, userIDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cmds.push(ctx)
|
||||
}
|
||||
|
||||
func (c *Commands) newOrgSetupCommands(ctx context.Context, orgID string, orgSetup *OrgSetup) *orgSetupCommands {
|
||||
orgAgg := org.NewAggregate(orgID)
|
||||
validations := []preparation.Validation{
|
||||
AddOrgCommand(ctx, orgAgg, orgSetup.Name),
|
||||
}
|
||||
return &orgSetupCommands{
|
||||
validations: validations,
|
||||
aggregate: orgAgg,
|
||||
commands: c,
|
||||
admins: orgSetup.Admins,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *orgSetupCommands) setupOrgAdmin(admin *OrgSetupAdmin, allowInitialMail bool) error {
|
||||
if admin.ID != "" {
|
||||
c.validations = append(c.validations, c.commands.AddOrgMemberCommand(&AddOrgMember{OrgID: c.aggregate.ID, UserID: admin.ID, Roles: orgAdminRoles(admin.Roles)}))
|
||||
return nil
|
||||
}
|
||||
|
||||
var userID string
|
||||
if admin.Human != nil && admin.Human.ID != "" {
|
||||
userID = admin.Human.ID
|
||||
} else {
|
||||
var err error
|
||||
userID, err = c.commands.idGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if admin.Human != nil {
|
||||
admin.Human.ID = userID
|
||||
c.validations = append(c.validations, c.commands.AddHumanCommand(admin.Human, c.aggregate.ID, c.commands.userPasswordHasher, c.commands.userEncryption, allowInitialMail))
|
||||
} else if admin.Machine != nil {
|
||||
admin.Machine.Machine.AggregateID = userID
|
||||
if err := c.setupOrgAdminMachine(c.aggregate, admin.Machine); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.validations = append(c.validations, c.commands.AddOrgMemberCommand(&AddOrgMember{OrgID: c.aggregate.ID, UserID: userID, Roles: orgAdminRoles(admin.Roles)}))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *orgSetupCommands) setupOrgAdminMachine(orgAgg *org.Aggregate, machine *AddMachine) error {
|
||||
userAgg := user.NewAggregate(machine.Machine.AggregateID, orgAgg.ID)
|
||||
c.validations = append(c.validations, AddMachineCommand(userAgg, machine.Machine))
|
||||
var pat *PersonalAccessToken
|
||||
var machineKey *MachineKey
|
||||
if machine.Pat != nil {
|
||||
pat = NewPersonalAccessToken(orgAgg.ID, machine.Machine.AggregateID, machine.Pat.ExpirationDate, machine.Pat.Scopes, domain.UserTypeMachine)
|
||||
tokenID, err := c.commands.idGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pat.TokenID = tokenID
|
||||
c.pats = append(c.pats, pat)
|
||||
c.validations = append(c.validations, prepareAddPersonalAccessToken(pat, c.commands.keyAlgorithm))
|
||||
}
|
||||
if machine.MachineKey != nil {
|
||||
machineKey = NewMachineKey(orgAgg.ID, machine.Machine.AggregateID, machine.MachineKey.ExpirationDate, machine.MachineKey.Type)
|
||||
keyID, err := c.commands.idGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
machineKey.KeyID = keyID
|
||||
c.machineKeys = append(c.machineKeys, machineKey)
|
||||
c.validations = append(c.validations, prepareAddUserMachineKey(machineKey, c.commands.keySize))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *orgSetupCommands) addCustomDomain(domain string, userIDs []string) error {
|
||||
if domain != "" {
|
||||
c.validations = append(c.validations, c.commands.prepareAddOrgDomain(c.aggregate, domain, userIDs))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func orgAdminRoles(roles []string) []string {
|
||||
if len(roles) > 0 {
|
||||
return roles
|
||||
}
|
||||
return []string{domain.RoleOrgOwner}
|
||||
}
|
||||
|
||||
func (c *orgSetupCommands) push(ctx context.Context) (_ *CreatedOrg, err error) {
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.commands.eventstore.Filter, c.validations...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events, err := c.commands.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CreatedOrg{
|
||||
ObjectDetails: &domain.ObjectDetails{
|
||||
Sequence: events[len(events)-1].Sequence(),
|
||||
EventDate: events[len(events)-1].CreatedAt(),
|
||||
ResourceOwner: c.aggregate.ID,
|
||||
},
|
||||
OrgAdmins: c.createdAdmins(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *orgSetupCommands) createdAdmins() []OrgAdmin {
|
||||
users := make([]OrgAdmin, 0, len(c.admins))
|
||||
for _, admin := range c.admins {
|
||||
if admin.ID != "" && admin.Human == nil {
|
||||
users = append(users, &AssignedOrgAdmin{ID: admin.ID})
|
||||
continue
|
||||
}
|
||||
if admin.Human != nil {
|
||||
users = append(users, c.createdHumanAdmin(admin))
|
||||
continue
|
||||
}
|
||||
if admin.Machine != nil {
|
||||
users = append(users, c.createdMachineAdmin(admin))
|
||||
}
|
||||
}
|
||||
return users
|
||||
}
|
||||
|
||||
func (c *orgSetupCommands) createdHumanAdmin(admin *OrgSetupAdmin) *CreatedOrgAdmin {
|
||||
createdAdmin := &CreatedOrgAdmin{
|
||||
ID: admin.Human.ID,
|
||||
}
|
||||
if admin.Human.EmailCode != nil {
|
||||
createdAdmin.EmailCode = admin.Human.EmailCode
|
||||
}
|
||||
return createdAdmin
|
||||
}
|
||||
|
||||
func (c *orgSetupCommands) createdMachineAdmin(admin *OrgSetupAdmin) *CreatedOrgAdmin {
|
||||
createdAdmin := &CreatedOrgAdmin{
|
||||
ID: admin.Machine.Machine.AggregateID,
|
||||
}
|
||||
if admin.Machine.Pat != nil {
|
||||
for _, pat := range c.pats {
|
||||
if pat.AggregateID == createdAdmin.ID {
|
||||
createdAdmin.PAT = pat
|
||||
}
|
||||
}
|
||||
}
|
||||
if admin.Machine.MachineKey != nil {
|
||||
for _, key := range c.machineKeys {
|
||||
if key.AggregateID == createdAdmin.ID {
|
||||
createdAdmin.MachineKey = key
|
||||
}
|
||||
}
|
||||
}
|
||||
return createdAdmin
|
||||
}
|
||||
|
||||
func (c *Commands) SetUpOrg(ctx context.Context, o *OrgSetup, allowInitialMail bool, userIDs ...string) (*CreatedOrg, error) {
|
||||
if err := o.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if o.OrgID == "" {
|
||||
var err error
|
||||
o.OrgID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// because users can choose their own ID, we must check that an org with the same ID does not already exist
|
||||
existingOrg, err := c.getOrgWriteModelByID(ctx, o.OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingOrg.State.Exists() {
|
||||
return nil, zerrors.ThrowAlreadyExists(nil, "ORG-laho2n", "Errors.Org.AlreadyExisting")
|
||||
}
|
||||
|
||||
return c.setUpOrgWithIDs(ctx, o, o.OrgID, allowInitialMail, userIDs...)
|
||||
}
|
||||
|
||||
// AddOrgCommand defines the commands to create a new org,
|
||||
// this includes the verified default domain
|
||||
func AddOrgCommand(ctx context.Context, a *org.Aggregate, name string) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if name = strings.TrimSpace(name); name == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "ORG-mruNY", "Errors.Invalid.Argument")
|
||||
}
|
||||
defaultDomain, err := domain.NewIAMDomainName(name, http_util.DomainContext(ctx).RequestedDomain())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
return []eventstore.Command{
|
||||
org.NewOrgAddedEvent(ctx, &a.Aggregate, name),
|
||||
org.NewDomainAddedEvent(ctx, &a.Aggregate, defaultDomain),
|
||||
org.NewDomainVerifiedEvent(ctx, &a.Aggregate, defaultDomain),
|
||||
org.NewDomainPrimarySetEvent(ctx, &a.Aggregate, defaultDomain),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) getOrg(ctx context.Context, orgID string) (*domain.Org, error) {
|
||||
writeModel, err := c.getOrgWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isOrgStateExists(writeModel.State) {
|
||||
return nil, zerrors.ThrowInternal(err, "COMMAND-4M9sf", "Errors.Org.NotFound")
|
||||
}
|
||||
return orgWriteModelToOrg(writeModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) checkOrgExists(ctx context.Context, orgID string) error {
|
||||
orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isOrgStateExists(orgWriteModel.State) {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-QXPGs", "Errors.Org.NotFound")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddOrgWithID(ctx context.Context, name, userID, resourceOwner, orgID string, setOrgInactive bool, claimedUserIDs []string) (_ *domain.Org, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
// because users can choose their own ID, we must check that an org with the same ID does not already exist
|
||||
existingOrg, err := c.getOrgWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingOrg.State.Exists() {
|
||||
return nil, zerrors.ThrowAlreadyExists(nil, "ORG-lapo2n", "Errors.Org.AlreadyExisting")
|
||||
}
|
||||
|
||||
return c.addOrgWithIDAndMember(ctx, name, userID, resourceOwner, orgID, setOrgInactive, claimedUserIDs)
|
||||
}
|
||||
|
||||
func (c *Commands) AddOrg(ctx context.Context, name, userID, resourceOwner string, claimedUserIDs []string) (*domain.Org, error) {
|
||||
if name = strings.TrimSpace(name); name == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "EVENT-Mf9sd", "Errors.Org.Invalid")
|
||||
}
|
||||
|
||||
orgID, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "COMMA-OwciI", "Errors.Internal")
|
||||
}
|
||||
|
||||
return c.addOrgWithIDAndMember(ctx, name, userID, resourceOwner, orgID, false, claimedUserIDs)
|
||||
}
|
||||
|
||||
func (c *Commands) addOrgWithIDAndMember(ctx context.Context, name, userID, resourceOwner, orgID string, setOrgInactive bool, claimedUserIDs []string) (_ *domain.Org, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
orgAgg, addedOrg, events, err := c.addOrgWithID(ctx, &domain.Org{Name: name}, orgID, claimedUserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = c.checkUserExists(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addMember := &AddOrgMember{OrgID: orgAgg.ID, UserID: userID, Roles: []string{domain.RoleOrgOwner}}
|
||||
if err := addMember.IsValid(c.zitadelRoles); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events = append(events, org.NewMemberAddedEvent(ctx, orgAgg, addMember.UserID, addMember.Roles...))
|
||||
if setOrgInactive {
|
||||
deactivateOrgEvent := org.NewOrgDeactivatedEvent(ctx, orgAgg)
|
||||
events = append(events, deactivateOrgEvent)
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = AppendAndReduce(addedOrg, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orgWriteModelToOrg(addedOrg), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeOrg(ctx context.Context, orgID, name string) (*domain.ObjectDetails, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
if orgID == "" || name == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "EVENT-Mf9sd", "Errors.Org.Invalid")
|
||||
}
|
||||
|
||||
orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isOrgStateExists(orgWriteModel.State) {
|
||||
return nil, zerrors.ThrowNotFound(nil, "ORG-1MRds", "Errors.Org.NotFound")
|
||||
}
|
||||
if orgWriteModel.Name == name {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "ORG-4VSdf", "Errors.Org.NotChanged")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&orgWriteModel.WriteModel)
|
||||
events := make([]eventstore.Command, 0)
|
||||
events = append(events, org.NewOrgChangedEvent(ctx, orgAgg, orgWriteModel.Name, name))
|
||||
changeDomainEvents, err := c.changeDefaultDomain(ctx, orgID, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(changeDomainEvents) > 0 {
|
||||
events = append(events, changeDomainEvents...)
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(orgWriteModel, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&orgWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) DeactivateOrg(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
|
||||
orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isOrgStateExists(orgWriteModel.State) {
|
||||
return nil, zerrors.ThrowNotFound(nil, "ORG-oL9nT", "Errors.Org.NotFound")
|
||||
}
|
||||
if orgWriteModel.State == domain.OrgStateInactive {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "EVENT-Dbs2g", "Errors.Org.AlreadyDeactivated")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&orgWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewOrgDeactivatedEvent(ctx, orgAgg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(orgWriteModel, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&orgWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ReactivateOrg(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
|
||||
orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isOrgStateExists(orgWriteModel.State) {
|
||||
return nil, zerrors.ThrowNotFound(nil, "ORG-Dgf3g", "Errors.Org.NotFound")
|
||||
}
|
||||
if orgWriteModel.State == domain.OrgStateActive {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "EVENT-bfnrh", "Errors.Org.AlreadyActive")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&orgWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewOrgReactivatedEvent(ctx, orgAgg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(orgWriteModel, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&orgWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveOrg(ctx context.Context, id string) (*domain.ObjectDetails, error) {
|
||||
orgAgg := org.NewAggregate(id)
|
||||
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareRemoveOrg(orgAgg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &domain.ObjectDetails{
|
||||
Sequence: events[len(events)-1].Sequence(),
|
||||
EventDate: events[len(events)-1].CreatedAt(),
|
||||
ResourceOwner: events[len(events)-1].Aggregate().InstanceID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Commands) prepareRemoveOrg(a *org.Aggregate) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
instance := authz.GetInstance(ctx)
|
||||
if a.ID == instance.DefaultOrganisationID() {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMA-wG9p1", "Errors.Org.DefaultOrgNotDeletable")
|
||||
}
|
||||
|
||||
_, err := c.checkProjectExists(ctx, instance.ProjectID(), a.ID)
|
||||
// if there is no error, the ZITADEL project was found on the org to be deleted
|
||||
if err == nil {
|
||||
return nil, zerrors.ThrowPreconditionFailed(err, "COMMA-AF3JW", "Errors.Org.ZitadelOrgNotDeletable")
|
||||
}
|
||||
// "precondition failed" error means the project does not exist, return other errors
|
||||
if !zerrors.IsPreconditionFailed(err) {
|
||||
return nil, err
|
||||
}
|
||||
writeModel, err := c.getOrgWriteModelByID(ctx, a.ID)
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowPreconditionFailed(err, "COMMA-wG9p1", "Errors.Org.NotFound")
|
||||
}
|
||||
if !isOrgStateExists(writeModel.State) {
|
||||
return nil, zerrors.ThrowNotFound(nil, "COMMA-aps2n", "Errors.Org.NotFound")
|
||||
}
|
||||
|
||||
domainPolicy, err := domainPolicyWriteModel(ctx, filter, a.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
organizationScopedUsername, err := checkOrganizationScopedUsernames(ctx, filter, a.ID, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
usernames, err := OrgUsers(ctx, filter, a.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
domains, err := OrgDomains(ctx, filter, a.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
links, err := OrgUserIDPLinks(ctx, filter, a.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entityIds, err := OrgSamlEntityIDs(ctx, filter, a.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []eventstore.Command{org.NewOrgRemovedEvent(ctx, &a.Aggregate, writeModel.Name, usernames, domainPolicy.UserLoginMustBeDomain || organizationScopedUsername, domains, links, entityIds)}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func OrgUserIDPLinks(ctx context.Context, filter preparation.FilterToQueryReducer, orgID string) ([]*domain.UserIDPLink, error) {
|
||||
events, err := filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(orgID).
|
||||
OrderAsc().
|
||||
AddQuery().
|
||||
AggregateTypes(user.AggregateType).
|
||||
EventTypes(
|
||||
user.UserIDPLinkAddedType, user.UserIDPLinkRemovedType, user.UserIDPLinkCascadeRemovedType,
|
||||
).Builder())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
links := make([]*domain.UserIDPLink, 0)
|
||||
for _, event := range events {
|
||||
switch eventTyped := event.(type) {
|
||||
case *user.UserIDPLinkAddedEvent:
|
||||
links = append(links, &domain.UserIDPLink{
|
||||
IDPConfigID: eventTyped.IDPConfigID,
|
||||
ExternalUserID: eventTyped.ExternalUserID,
|
||||
DisplayName: eventTyped.DisplayName,
|
||||
})
|
||||
case *user.UserIDPLinkRemovedEvent:
|
||||
for i := range links {
|
||||
if links[i].ExternalUserID == eventTyped.ExternalUserID &&
|
||||
links[i].IDPConfigID == eventTyped.IDPConfigID {
|
||||
links[i] = links[len(links)-1]
|
||||
links[len(links)-1] = nil
|
||||
links = links[:len(links)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
case *user.UserIDPLinkCascadeRemovedEvent:
|
||||
for i := range links {
|
||||
if links[i].ExternalUserID == eventTyped.ExternalUserID &&
|
||||
links[i].IDPConfigID == eventTyped.IDPConfigID {
|
||||
links[i] = links[len(links)-1]
|
||||
links[len(links)-1] = nil
|
||||
links = links[:len(links)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return links, nil
|
||||
}
|
||||
|
||||
type samlEntityID struct {
|
||||
appID string
|
||||
entityID string
|
||||
}
|
||||
|
||||
func OrgSamlEntityIDs(ctx context.Context, filter preparation.FilterToQueryReducer, orgID string) ([]string, error) {
|
||||
events, err := filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(orgID).
|
||||
OrderAsc().
|
||||
AddQuery().
|
||||
AggregateTypes(project.AggregateType).
|
||||
EventTypes(
|
||||
project.SAMLConfigAddedType, project.SAMLConfigChangedType, project.ApplicationRemovedType,
|
||||
).Builder())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entityIDs := make([]samlEntityID, 0)
|
||||
for _, event := range events {
|
||||
switch eventTyped := event.(type) {
|
||||
case *project.SAMLConfigAddedEvent:
|
||||
entityIDs = append(entityIDs, samlEntityID{appID: eventTyped.AppID, entityID: eventTyped.EntityID})
|
||||
case *project.SAMLConfigChangedEvent:
|
||||
for i := range entityIDs {
|
||||
if entityIDs[i].appID == eventTyped.AppID {
|
||||
entityIDs[i].entityID = eventTyped.EntityID
|
||||
break
|
||||
}
|
||||
}
|
||||
case *project.ApplicationRemovedEvent:
|
||||
for i := range entityIDs {
|
||||
if entityIDs[i].appID == eventTyped.AppID {
|
||||
entityIDs[i] = entityIDs[len(entityIDs)-1]
|
||||
entityIDs = entityIDs[:len(entityIDs)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ids := make([]string, len(entityIDs))
|
||||
for i := range entityIDs {
|
||||
ids[i] = entityIDs[i].entityID
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func OrgDomains(ctx context.Context, filter preparation.FilterToQueryReducer, orgID string) ([]string, error) {
|
||||
events, err := filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(orgID).
|
||||
OrderAsc().
|
||||
AddQuery().
|
||||
AggregateTypes(org.AggregateType).
|
||||
EventTypes(
|
||||
org.OrgDomainVerifiedEventType,
|
||||
org.OrgDomainRemovedEventType,
|
||||
).Builder())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := make([]string, 0)
|
||||
for _, event := range events {
|
||||
switch eventTyped := event.(type) {
|
||||
case *org.DomainVerifiedEvent:
|
||||
names = append(names, eventTyped.Domain)
|
||||
case *org.DomainRemovedEvent:
|
||||
for i := range names {
|
||||
if names[i] == eventTyped.Domain {
|
||||
names[i] = names[len(names)-1]
|
||||
names = names[:len(names)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
type userIDName struct {
|
||||
name string
|
||||
id string
|
||||
}
|
||||
|
||||
func OrgUsers(ctx context.Context, filter preparation.FilterToQueryReducer, orgID string) ([]string, error) {
|
||||
events, err := filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
InstanceID(authz.GetInstance(ctx).InstanceID()).
|
||||
ResourceOwner(orgID).
|
||||
OrderAsc().
|
||||
AddQuery().
|
||||
AggregateTypes(user.AggregateType).
|
||||
EventTypes(
|
||||
user.HumanAddedType,
|
||||
user.MachineAddedEventType,
|
||||
user.HumanRegisteredType,
|
||||
user.UserDomainClaimedType,
|
||||
user.UserUserNameChangedType,
|
||||
user.UserRemovedType,
|
||||
).Builder())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users := make([]userIDName, 0)
|
||||
for _, event := range events {
|
||||
switch eventTyped := event.(type) {
|
||||
case *user.HumanAddedEvent:
|
||||
users = append(users, userIDName{eventTyped.UserName, eventTyped.Aggregate().ID})
|
||||
case *user.MachineAddedEvent:
|
||||
users = append(users, userIDName{eventTyped.UserName, eventTyped.Aggregate().ID})
|
||||
case *user.HumanRegisteredEvent:
|
||||
users = append(users, userIDName{eventTyped.UserName, eventTyped.Aggregate().ID})
|
||||
case *user.DomainClaimedEvent:
|
||||
for i := range users {
|
||||
if users[i].id == eventTyped.Aggregate().ID {
|
||||
users[i].name = eventTyped.UserName
|
||||
}
|
||||
}
|
||||
case *user.UsernameChangedEvent:
|
||||
for i := range users {
|
||||
if users[i].id == eventTyped.Aggregate().ID {
|
||||
users[i].name = eventTyped.UserName
|
||||
}
|
||||
}
|
||||
case *user.UserRemovedEvent:
|
||||
for i := range users {
|
||||
if users[i].id == eventTyped.Aggregate().ID {
|
||||
users[i] = users[len(users)-1]
|
||||
users = users[:len(users)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
names := make([]string, len(users))
|
||||
for i := range users {
|
||||
names[i] = users[i].name
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func ExistsOrg(ctx context.Context, filter preparation.FilterToQueryReducer, id string) (exists bool, err error) {
|
||||
events, err := filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
InstanceID(authz.GetInstance(ctx).InstanceID()).
|
||||
ResourceOwner(id).
|
||||
OrderAsc().
|
||||
AddQuery().
|
||||
AggregateTypes(org.AggregateType).
|
||||
AggregateIDs(id).
|
||||
EventTypes(
|
||||
org.OrgAddedEventType,
|
||||
org.OrgDeactivatedEventType,
|
||||
org.OrgReactivatedEventType,
|
||||
org.OrgRemovedEventType,
|
||||
).Builder())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
switch event.(type) {
|
||||
case *org.OrgAddedEvent, *org.OrgReactivatedEvent:
|
||||
exists = true
|
||||
case *org.OrgDeactivatedEvent, *org.OrgRemovedEvent:
|
||||
exists = false
|
||||
}
|
||||
}
|
||||
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func (c *Commands) addOrgWithID(ctx context.Context, organisation *domain.Org, orgID string, claimedUserIDs []string) (_ *eventstore.Aggregate, _ *OrgWriteModel, _ []eventstore.Command, err error) {
|
||||
if !organisation.IsValid() {
|
||||
return nil, nil, nil, zerrors.ThrowInvalidArgument(nil, "COMM-deLSk", "Errors.Org.Invalid")
|
||||
}
|
||||
|
||||
organisation.AggregateID = orgID
|
||||
organisation.AddIAMDomain(http_util.DomainContext(ctx).RequestedDomain())
|
||||
addedOrg := NewOrgWriteModel(organisation.AggregateID)
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&addedOrg.WriteModel)
|
||||
events := []eventstore.Command{
|
||||
org.NewOrgAddedEvent(ctx, orgAgg, organisation.Name),
|
||||
}
|
||||
for _, orgDomain := range organisation.Domains {
|
||||
orgDomainEvents, err := c.addOrgDomain(ctx, orgAgg, NewOrgDomainWriteModel(orgAgg.ID, orgDomain.Domain), orgDomain, claimedUserIDs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
events = append(events, orgDomainEvents...)
|
||||
}
|
||||
return orgAgg, addedOrg, events, nil
|
||||
}
|
||||
|
||||
func (c *Commands) getOrgWriteModelByID(ctx context.Context, orgID string) (_ *OrgWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
orgWriteModel := NewOrgWriteModel(orgID)
|
||||
err = c.eventstore.FilterToQueryReducer(ctx, orgWriteModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orgWriteModel, nil
|
||||
}
|
||||
|
||||
func isOrgStateExists(state domain.OrgState) bool {
|
||||
return !hasOrgState(state, domain.OrgStateRemoved, domain.OrgStateUnspecified)
|
||||
}
|
||||
|
||||
func hasOrgState(check domain.OrgState, states ...domain.OrgState) bool {
|
||||
for _, state := range states {
|
||||
if check == state {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
Reference in New Issue
Block a user