zitadel/internal/command/org_features.go

219 lines
7.0 KiB
Go
Raw Normal View History

package command
import (
"context"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/org"
)
func (c *Commands) SetOrgFeatures(ctx context.Context, resourceOwner string, features *domain.Features) (*domain.ObjectDetails, error) {
if resourceOwner == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Features-G5tg", "Errors.ResourceOwnerMissing")
}
existingFeatures := NewOrgFeaturesWriteModel(resourceOwner)
err := c.eventstore.FilterToQueryReducer(ctx, existingFeatures)
if err != nil {
return nil, err
}
setEvent, hasChanged := existingFeatures.NewSetEvent(
ctx,
OrgAggregateFromWriteModel(&existingFeatures.FeaturesWriteModel.WriteModel),
features.TierName,
features.TierDescription,
features.State,
features.StateDescription,
features.AuditLogRetention,
features.LoginPolicyFactors,
features.LoginPolicyIDP,
features.LoginPolicyPasswordless,
features.LoginPolicyRegistration,
features.LoginPolicyUsernameLogin,
features.PasswordComplexityPolicy,
features.LabelPolicy,
)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Features-GE4h2", "Errors.Features.NotChanged")
}
events, err := c.ensureOrgSettingsToFeatures(ctx, resourceOwner, features)
if err != nil {
return nil, err
}
events = append(events, setEvent)
pushedEvents, err := c.eventstore.PushEvents(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(existingFeatures, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingFeatures.WriteModel), nil
}
func (c *Commands) RemoveOrgFeatures(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Features-G5tg", "Errors.ResourceOwnerMissing")
}
existingFeatures := NewOrgFeaturesWriteModel(orgID)
err := c.eventstore.FilterToQueryReducer(ctx, existingFeatures)
if err != nil {
return nil, err
}
if existingFeatures.State == domain.FeaturesStateUnspecified || existingFeatures.State == domain.FeaturesStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "Features-Bg32G", "Errors.Features.NotFound")
}
removedEvent := org.NewFeaturesRemovedEvent(ctx, OrgAggregateFromWriteModel(&existingFeatures.FeaturesWriteModel.WriteModel))
features, err := c.getDefaultFeatures(ctx)
if err != nil {
return nil, err
}
events, err := c.ensureOrgSettingsToFeatures(ctx, orgID, features)
if err != nil {
return nil, err
}
events = append(events, removedEvent)
pushedEvents, err := c.eventstore.PushEvents(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(existingFeatures, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingFeatures.WriteModel), nil
}
func (c *Commands) ensureOrgSettingsToFeatures(ctx context.Context, orgID string, features *domain.Features) ([]eventstore.EventPusher, error) {
events, err := c.setAllowedLoginPolicy(ctx, orgID, features)
if err != nil {
return nil, err
}
if !features.PasswordComplexityPolicy {
removePasswordComplexityEvent, err := c.removePasswordComplexityPolicyIfExists(ctx, orgID)
if err != nil {
return nil, err
}
if removePasswordComplexityEvent != nil {
events = append(events, removePasswordComplexityEvent)
}
}
if !features.LabelPolicy {
removeLabelPolicyEvent, err := c.removeLabelPolicyIfExists(ctx, orgID)
if err != nil {
return nil, err
}
if removeLabelPolicyEvent != nil {
events = append(events, removeLabelPolicyEvent)
}
}
return events, nil
}
func (c *Commands) setAllowedLoginPolicy(ctx context.Context, orgID string, features *domain.Features) ([]eventstore.EventPusher, error) {
events := make([]eventstore.EventPusher, 0)
existingPolicy, err := c.orgLoginPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, nil
}
defaultPolicy, err := c.getDefaultLoginPolicy(ctx)
if err != nil {
return nil, err
}
policy := *existingPolicy
if !features.LoginPolicyFactors {
if defaultPolicy.ForceMFA != existingPolicy.ForceMFA {
policy.ForceMFA = defaultPolicy.ForceMFA
}
authFactorsEvents, err := c.setDefaultAuthFactorsInCustomLoginPolicy(ctx, orgID)
if err != nil {
return nil, err
}
events = append(events, authFactorsEvents...)
}
if !features.LoginPolicyIDP {
if defaultPolicy.AllowExternalIDP != existingPolicy.AllowExternalIDP {
policy.AllowExternalIDP = defaultPolicy.AllowExternalIDP
}
//TODO: handle idps
}
if !features.LoginPolicyRegistration && defaultPolicy.AllowRegister != existingPolicy.AllowRegister {
policy.AllowRegister = defaultPolicy.AllowRegister
}
if !features.LoginPolicyPasswordless && defaultPolicy.PasswordlessType != existingPolicy.PasswordlessType {
policy.PasswordlessType = defaultPolicy.PasswordlessType
}
if !features.LoginPolicyUsernameLogin && defaultPolicy.AllowUsernamePassword != existingPolicy.AllowUserNamePassword {
policy.AllowUserNamePassword = defaultPolicy.AllowUsernamePassword
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, OrgAggregateFromWriteModel(&existingPolicy.WriteModel), policy.AllowUserNamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType)
if hasChanged {
events = append(events, changedEvent)
}
return events, nil
}
func (c *Commands) setDefaultAuthFactorsInCustomLoginPolicy(ctx context.Context, orgID string) ([]eventstore.EventPusher, error) {
orgAuthFactors, err := c.orgLoginPolicyAuthFactorsWriteModel(ctx, orgID)
if err != nil {
return nil, err
}
events := make([]eventstore.EventPusher, 0)
for factor, state := range orgAuthFactors.SecondFactors {
if state.IAM == state.Org {
continue
}
secondFactorWriteModel := orgAuthFactors.ToSecondFactorWriteModel(factor)
if state.IAM == domain.FactorStateActive {
event, err := c.addSecondFactorToLoginPolicy(ctx, secondFactorWriteModel, factor)
if err != nil {
return nil, err
}
if event != nil {
events = append(events, event)
}
continue
}
event, err := c.removeSecondFactorFromLoginPolicy(ctx, secondFactorWriteModel, factor)
if err != nil {
return nil, err
}
if event != nil {
events = append(events, event)
}
}
for factor, state := range orgAuthFactors.MultiFactors {
if state.IAM == state.Org {
continue
}
multiFactorWriteModel := orgAuthFactors.ToMultiFactorWriteModel(factor)
if state.IAM == domain.FactorStateActive {
event, err := c.addMultiFactorToLoginPolicy(ctx, multiFactorWriteModel, factor)
if err != nil {
return nil, err
}
if event != nil {
events = append(events, event)
}
continue
}
event, err := c.removeMultiFactorFromLoginPolicy(ctx, multiFactorWriteModel, factor)
if err != nil {
return nil, err
}
if event != nil {
events = append(events, event)
}
}
return events, nil
}