mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:37:31 +00:00
feat: features (#1427)
* features * features * features * fix json tags * add features handler to auth * mocks for tests * add setup step * fixes * add featurelist to auth api * grandfather state and typos * typo * merge new-eventstore * fix login policy tests * label policy in features * audit log retention
This commit is contained in:
@@ -3,6 +3,7 @@ package command
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
authz_repo "github.com/caos/zitadel/internal/authz/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/config/types"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
@@ -49,13 +50,14 @@ type Commands struct {
|
||||
keyAlgorithm crypto.EncryptionAlgorithm
|
||||
privateKeyLifetime time.Duration
|
||||
publicKeyLifetime time.Duration
|
||||
tokenVerifier *authz.TokenVerifier
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Eventstore types.SQLUser
|
||||
}
|
||||
|
||||
func StartCommands(eventstore *eventstore.Eventstore, defaults sd.SystemDefaults, authZConfig authz.Config) (repo *Commands, err error) {
|
||||
func StartCommands(eventstore *eventstore.Eventstore, defaults sd.SystemDefaults, authZConfig authz.Config, authZRepo *authz_repo.EsRepository) (repo *Commands, err error) {
|
||||
repo = &Commands{
|
||||
eventstore: eventstore,
|
||||
idGenerator: id.SonyFlakeGenerator,
|
||||
@@ -119,6 +121,8 @@ func StartCommands(eventstore *eventstore.Eventstore, defaults sd.SystemDefaults
|
||||
return nil, err
|
||||
}
|
||||
repo.keyAlgorithm = keyAlgorithm
|
||||
|
||||
repo.tokenVerifier = authz.Start(authZRepo)
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
|
74
internal/command/features_model.go
Normal file
74
internal/command/features_model.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/features"
|
||||
)
|
||||
|
||||
type FeaturesWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
TierName string
|
||||
TierDescription string
|
||||
State domain.FeaturesState
|
||||
StateDescription string
|
||||
AuditLogRetention time.Duration
|
||||
LoginPolicyFactors bool
|
||||
LoginPolicyIDP bool
|
||||
LoginPolicyPasswordless bool
|
||||
LoginPolicyRegistration bool
|
||||
LoginPolicyUsernameLogin bool
|
||||
PasswordComplexityPolicy bool
|
||||
LabelPolicy bool
|
||||
}
|
||||
|
||||
func (wm *FeaturesWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *features.FeaturesSetEvent:
|
||||
if e.TierName != nil {
|
||||
wm.TierName = *e.TierName
|
||||
}
|
||||
if e.TierDescription != nil {
|
||||
wm.TierDescription = *e.TierDescription
|
||||
}
|
||||
wm.State = domain.FeaturesStateActive
|
||||
if e.State != nil {
|
||||
wm.State = *e.State
|
||||
}
|
||||
if e.StateDescription != nil {
|
||||
wm.StateDescription = *e.StateDescription
|
||||
}
|
||||
if e.AuditLogRetention != nil {
|
||||
wm.AuditLogRetention = *e.AuditLogRetention
|
||||
}
|
||||
if e.LoginPolicyFactors != nil {
|
||||
wm.LoginPolicyFactors = *e.LoginPolicyFactors
|
||||
}
|
||||
if e.LoginPolicyIDP != nil {
|
||||
wm.LoginPolicyIDP = *e.LoginPolicyIDP
|
||||
}
|
||||
if e.LoginPolicyPasswordless != nil {
|
||||
wm.LoginPolicyPasswordless = *e.LoginPolicyPasswordless
|
||||
}
|
||||
if e.LoginPolicyRegistration != nil {
|
||||
wm.LoginPolicyRegistration = *e.LoginPolicyRegistration
|
||||
}
|
||||
if e.LoginPolicyUsernameLogin != nil {
|
||||
wm.LoginPolicyUsernameLogin = *e.LoginPolicyUsernameLogin
|
||||
}
|
||||
if e.PasswordComplexityPolicy != nil {
|
||||
wm.PasswordComplexityPolicy = *e.PasswordComplexityPolicy
|
||||
}
|
||||
if e.LabelPolicy != nil {
|
||||
wm.LabelPolicy = *e.LabelPolicy
|
||||
}
|
||||
case *features.FeaturesRemovedEvent:
|
||||
wm.State = domain.FeaturesStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
@@ -160,3 +160,21 @@ func writeModelToIDPProvider(wm *IdentityProviderWriteModel) *domain.IDPProvider
|
||||
Type: wm.IDPProviderType,
|
||||
}
|
||||
}
|
||||
|
||||
func writeModelToFeatures(wm *FeaturesWriteModel) *domain.Features {
|
||||
return &domain.Features{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
TierName: wm.TierName,
|
||||
TierDescription: wm.TierDescription,
|
||||
State: wm.State,
|
||||
StateDescription: wm.StateDescription,
|
||||
AuditLogRetention: wm.AuditLogRetention,
|
||||
LoginPolicyFactors: wm.LoginPolicyFactors,
|
||||
LoginPolicyIDP: wm.LoginPolicyIDP,
|
||||
LoginPolicyPasswordless: wm.LoginPolicyPasswordless,
|
||||
LoginPolicyRegistration: wm.LoginPolicyRegistration,
|
||||
LoginPolicyUsernameLogin: wm.LoginPolicyUsernameLogin,
|
||||
PasswordComplexityPolicy: wm.PasswordComplexityPolicy,
|
||||
LabelPolicy: wm.LabelPolicy,
|
||||
}
|
||||
}
|
||||
|
64
internal/command/iam_features.go
Normal file
64
internal/command/iam_features.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/repository/iam"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
func (c *Commands) SetDefaultFeatures(ctx context.Context, features *domain.Features) (*domain.ObjectDetails, error) {
|
||||
existingFeatures := NewIAMFeaturesWriteModel()
|
||||
setEvent, err := c.setDefaultFeatures(ctx, existingFeatures, features)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, setEvent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingFeatures, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingFeatures.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) setDefaultFeatures(ctx context.Context, existingFeatures *IAMFeaturesWriteModel, features *domain.Features) (*iam.FeaturesSetEvent, error) {
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, existingFeatures)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setEvent, hasChanged := existingFeatures.NewSetEvent(
|
||||
ctx,
|
||||
IAMAggregateFromWriteModel(&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")
|
||||
}
|
||||
return setEvent, nil
|
||||
}
|
||||
|
||||
func (c *Commands) getDefaultFeatures(ctx context.Context) (*domain.Features, error) {
|
||||
existingFeatures := NewIAMFeaturesWriteModel()
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, existingFeatures)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToFeatures(&existingFeatures.FeaturesWriteModel), nil
|
||||
}
|
115
internal/command/iam_features_model.go
Normal file
115
internal/command/iam_features_model.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/features"
|
||||
"github.com/caos/zitadel/internal/repository/iam"
|
||||
)
|
||||
|
||||
type IAMFeaturesWriteModel struct {
|
||||
FeaturesWriteModel
|
||||
}
|
||||
|
||||
func NewIAMFeaturesWriteModel() *IAMFeaturesWriteModel {
|
||||
return &IAMFeaturesWriteModel{
|
||||
FeaturesWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: domain.IAMID,
|
||||
ResourceOwner: domain.IAMID,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *IAMFeaturesWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *iam.FeaturesSetEvent:
|
||||
wm.FeaturesWriteModel.AppendEvents(&e.FeaturesSetEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *IAMFeaturesWriteModel) IsValid() bool {
|
||||
return wm.AggregateID != ""
|
||||
}
|
||||
|
||||
func (wm *IAMFeaturesWriteModel) Reduce() error {
|
||||
return wm.FeaturesWriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *IAMFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
|
||||
AggregateIDs(wm.FeaturesWriteModel.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
EventTypes(iam.FeaturesSetEventType)
|
||||
}
|
||||
|
||||
func (wm *IAMFeaturesWriteModel) NewSetEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
tierName, tierDescription string,
|
||||
state domain.FeaturesState,
|
||||
stateDescription string,
|
||||
auditLogRetention time.Duration,
|
||||
loginPolicyFactors,
|
||||
loginPolicyIDP,
|
||||
loginPolicyPasswordless,
|
||||
loginPolicyRegistration,
|
||||
loginPolicyUsernameLogin,
|
||||
passwordComplexityPolicy,
|
||||
labelPolicy bool,
|
||||
) (*iam.FeaturesSetEvent, bool) {
|
||||
|
||||
changes := make([]features.FeaturesChanges, 0)
|
||||
|
||||
if tierName != "" && wm.TierName != tierName {
|
||||
changes = append(changes, features.ChangeTierName(tierName))
|
||||
}
|
||||
if tierDescription != "" && wm.TierDescription != tierDescription {
|
||||
changes = append(changes, features.ChangeTierDescription(tierDescription))
|
||||
}
|
||||
if wm.State != state {
|
||||
changes = append(changes, features.ChangeState(state))
|
||||
}
|
||||
if stateDescription != "" && wm.StateDescription != stateDescription {
|
||||
changes = append(changes, features.ChangeStateDescription(stateDescription))
|
||||
}
|
||||
if auditLogRetention != 0 && wm.AuditLogRetention != auditLogRetention {
|
||||
changes = append(changes, features.ChangeAuditLogRetention(auditLogRetention))
|
||||
}
|
||||
if wm.LoginPolicyFactors != loginPolicyFactors {
|
||||
changes = append(changes, features.ChangeLoginPolicyFactors(loginPolicyFactors))
|
||||
}
|
||||
if wm.LoginPolicyIDP != loginPolicyIDP {
|
||||
changes = append(changes, features.ChangeLoginPolicyIDP(loginPolicyIDP))
|
||||
}
|
||||
if wm.LoginPolicyPasswordless != loginPolicyPasswordless {
|
||||
changes = append(changes, features.ChangeLoginPolicyPasswordless(loginPolicyPasswordless))
|
||||
}
|
||||
if wm.LoginPolicyRegistration != loginPolicyRegistration {
|
||||
changes = append(changes, features.ChangeLoginPolicyRegistration(loginPolicyRegistration))
|
||||
}
|
||||
if wm.LoginPolicyUsernameLogin != loginPolicyUsernameLogin {
|
||||
changes = append(changes, features.ChangeLoginPolicyUsernameLogin(loginPolicyUsernameLogin))
|
||||
}
|
||||
if wm.PasswordComplexityPolicy != passwordComplexityPolicy {
|
||||
changes = append(changes, features.ChangePasswordComplexityPolicy(passwordComplexityPolicy))
|
||||
}
|
||||
if wm.LabelPolicy != labelPolicy {
|
||||
changes = append(changes, features.ChangeLabelPolicy(labelPolicy))
|
||||
}
|
||||
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
changedEvent, err := iam.NewFeaturesSetEvent(ctx, aggregate, changes)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return changedEvent, true
|
||||
}
|
@@ -221,7 +221,7 @@ func (c *Commands) AddMultiFactorToDefaultLoginPolicy(ctx context.Context, multi
|
||||
return domain.MultiFactorTypeUnspecified, nil, caos_errs.ThrowInvalidArgument(nil, "IAM-5m9fs", "Errors.IAM.LoginPolicy.MFA.Unspecified")
|
||||
}
|
||||
multiFactorModel := NewIAMMultiFactorWriteModel(multiFactor)
|
||||
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
|
||||
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactorWriteModel.WriteModel)
|
||||
event, err := c.addMultiFactorToDefaultLoginPolicy(ctx, iamAgg, multiFactorModel, multiFactor)
|
||||
if err != nil {
|
||||
return domain.MultiFactorTypeUnspecified, nil, err
|
||||
@@ -235,7 +235,7 @@ func (c *Commands) AddMultiFactorToDefaultLoginPolicy(ctx context.Context, multi
|
||||
if err != nil {
|
||||
return domain.MultiFactorTypeUnspecified, nil, err
|
||||
}
|
||||
return multiFactorModel.MultiFactoryWriteModel.MFAType, writeModelToObjectDetails(&multiFactorModel.WriteModel), nil
|
||||
return multiFactorModel.MultiFactorWriteModel.MFAType, writeModelToObjectDetails(&multiFactorModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) addMultiFactorToDefaultLoginPolicy(ctx context.Context, iamAgg *eventstore.Aggregate, multiFactorModel *IAMMultiFactorWriteModel, multiFactor domain.MultiFactorType) (eventstore.EventPusher, error) {
|
||||
@@ -262,7 +262,7 @@ func (c *Commands) RemoveMultiFactorFromDefaultLoginPolicy(ctx context.Context,
|
||||
if multiFactorModel.State == domain.FactorStateUnspecified || multiFactorModel.State == domain.FactorStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-3M9df", "Errors.IAM.LoginPolicy.MFA.NotExisting")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
|
||||
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactorWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLoginPolicyMultiFactorRemovedEvent(ctx, iamAgg, multiFactor))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -51,12 +51,12 @@ func (wm *IAMSecondFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
}
|
||||
|
||||
type IAMMultiFactorWriteModel struct {
|
||||
MultiFactoryWriteModel
|
||||
MultiFactorWriteModel
|
||||
}
|
||||
|
||||
func NewIAMMultiFactorWriteModel(factorType domain.MultiFactorType) *IAMMultiFactorWriteModel {
|
||||
return &IAMMultiFactorWriteModel{
|
||||
MultiFactoryWriteModel{
|
||||
MultiFactorWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: domain.IAMID,
|
||||
ResourceOwner: domain.IAMID,
|
||||
@@ -82,7 +82,7 @@ func (wm *IAMMultiFactorWriteModel) AppendEvents(events ...eventstore.EventReade
|
||||
}
|
||||
|
||||
func (wm *IAMMultiFactorWriteModel) Reduce() error {
|
||||
return wm.MultiFactoryWriteModel.Reduce()
|
||||
return wm.MultiFactorWriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *IAMMultiFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
|
@@ -2,7 +2,14 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/eventstore/repository"
|
||||
"github.com/caos/zitadel/internal/eventstore/repository/mock"
|
||||
@@ -12,9 +19,6 @@ import (
|
||||
proj_repo "github.com/caos/zitadel/internal/repository/project"
|
||||
usr_repo "github.com/caos/zitadel/internal/repository/user"
|
||||
"github.com/caos/zitadel/internal/repository/usergrant"
|
||||
"github.com/golang/mock/gomock"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type expect func(mockRepository *mock.MockRepository)
|
||||
@@ -172,3 +176,48 @@ func GetMockSecretGenerator(t *testing.T) crypto.Generator {
|
||||
|
||||
return generator
|
||||
}
|
||||
|
||||
func GetMockVerifier(t *testing.T, features ...string) *authz.TokenVerifier {
|
||||
return authz.Start(&testVerifier{
|
||||
features: features,
|
||||
})
|
||||
}
|
||||
|
||||
type testVerifier struct {
|
||||
features []string
|
||||
}
|
||||
|
||||
func (v *testVerifier) VerifyAccessToken(ctx context.Context, token, clientID string) (string, string, string, string, error) {
|
||||
return "userID", "agentID", "de", "orgID", nil
|
||||
}
|
||||
func (v *testVerifier) SearchMyMemberships(ctx context.Context) ([]*authz.Membership, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (v *testVerifier) ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (string, []string, error) {
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
func (v *testVerifier) ExistsOrg(ctx context.Context, orgID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *testVerifier) VerifierClientID(ctx context.Context, appName string) (string, error) {
|
||||
return "clientID", nil
|
||||
}
|
||||
|
||||
func (v *testVerifier) CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error {
|
||||
for _, feature := range requiredFeatures {
|
||||
hasFeature := false
|
||||
for _, f := range v.features {
|
||||
if f == feature {
|
||||
hasFeature = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasFeature {
|
||||
return errors.ThrowPermissionDenied(nil, "id", "missing feature")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
67
internal/command/org_features.go
Normal file
67
internal/command/org_features.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func (c *Commands) SetOrgFeatures(ctx context.Context, resourceOwner string, features *domain.Features) (*domain.ObjectDetails, error) {
|
||||
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")
|
||||
}
|
||||
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, setEvent)
|
||||
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) {
|
||||
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")
|
||||
}
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingFeatures.FeaturesWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewFeaturesRemovedEvent(ctx, orgAgg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingFeatures, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingFeatures.WriteModel), nil
|
||||
}
|
121
internal/command/org_features_model.go
Normal file
121
internal/command/org_features_model.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/features"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
type OrgFeaturesWriteModel struct {
|
||||
FeaturesWriteModel
|
||||
}
|
||||
|
||||
func NewOrgFeaturesWriteModel(orgID string) *OrgFeaturesWriteModel {
|
||||
return &OrgFeaturesWriteModel{
|
||||
FeaturesWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: orgID,
|
||||
ResourceOwner: orgID,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgFeaturesWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *org.FeaturesSetEvent:
|
||||
wm.FeaturesWriteModel.AppendEvents(&e.FeaturesSetEvent)
|
||||
case *org.FeaturesRemovedEvent:
|
||||
wm.FeaturesWriteModel.AppendEvents(&e.FeaturesRemovedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgFeaturesWriteModel) IsValid() bool {
|
||||
return wm.AggregateID != ""
|
||||
}
|
||||
|
||||
func (wm *OrgFeaturesWriteModel) Reduce() error {
|
||||
return wm.FeaturesWriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *OrgFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, org.AggregateType).
|
||||
AggregateIDs(wm.FeaturesWriteModel.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
EventTypes(
|
||||
org.FeaturesSetEventType,
|
||||
org.FeaturesRemovedEventType,
|
||||
)
|
||||
}
|
||||
|
||||
func (wm *OrgFeaturesWriteModel) NewSetEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
tierName,
|
||||
tierDescription string,
|
||||
state domain.FeaturesState,
|
||||
stateDescription string,
|
||||
auditLogRetention time.Duration,
|
||||
loginPolicyFactors,
|
||||
loginPolicyIDP,
|
||||
loginPolicyPasswordless,
|
||||
loginPolicyRegistration,
|
||||
loginPolicyUsernameLogin,
|
||||
passwordComplexityPolicy,
|
||||
labelPolicy bool,
|
||||
) (*org.FeaturesSetEvent, bool) {
|
||||
|
||||
changes := make([]features.FeaturesChanges, 0)
|
||||
|
||||
if tierName != "" && wm.TierName != tierName {
|
||||
changes = append(changes, features.ChangeTierName(tierName))
|
||||
}
|
||||
if tierDescription != "" && wm.TierDescription != tierDescription {
|
||||
changes = append(changes, features.ChangeTierDescription(tierDescription))
|
||||
}
|
||||
if wm.State != state {
|
||||
changes = append(changes, features.ChangeState(state))
|
||||
}
|
||||
if stateDescription != "" && wm.StateDescription != stateDescription {
|
||||
changes = append(changes, features.ChangeStateDescription(stateDescription))
|
||||
}
|
||||
if auditLogRetention != 0 && wm.AuditLogRetention != auditLogRetention {
|
||||
changes = append(changes, features.ChangeAuditLogRetention(auditLogRetention))
|
||||
}
|
||||
if wm.LoginPolicyFactors != loginPolicyFactors {
|
||||
changes = append(changes, features.ChangeLoginPolicyFactors(loginPolicyFactors))
|
||||
}
|
||||
if wm.LoginPolicyIDP != loginPolicyIDP {
|
||||
changes = append(changes, features.ChangeLoginPolicyIDP(loginPolicyIDP))
|
||||
}
|
||||
if wm.LoginPolicyPasswordless != loginPolicyPasswordless {
|
||||
changes = append(changes, features.ChangeLoginPolicyPasswordless(loginPolicyPasswordless))
|
||||
}
|
||||
if wm.LoginPolicyRegistration != loginPolicyRegistration {
|
||||
changes = append(changes, features.ChangeLoginPolicyRegistration(loginPolicyRegistration))
|
||||
}
|
||||
if wm.LoginPolicyUsernameLogin != loginPolicyUsernameLogin {
|
||||
changes = append(changes, features.ChangeLoginPolicyUsernameLogin(loginPolicyUsernameLogin))
|
||||
}
|
||||
if wm.PasswordComplexityPolicy != passwordComplexityPolicy {
|
||||
changes = append(changes, features.ChangePasswordComplexityPolicy(passwordComplexityPolicy))
|
||||
}
|
||||
if wm.LabelPolicy != labelPolicy {
|
||||
changes = append(changes, features.ChangeLabelPolicy(labelPolicy))
|
||||
}
|
||||
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
changedEvent, err := org.NewFeaturesSetEvent(ctx, aggregate, changes)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return changedEvent, true
|
||||
}
|
@@ -2,7 +2,11 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
@@ -22,6 +26,11 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "Org-Dgfb2", "Errors.Org.LoginPolicy.AlreadyExists")
|
||||
}
|
||||
|
||||
err = c.checkLoginPolicyAllowed(ctx, resourceOwner, policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(
|
||||
ctx,
|
||||
@@ -43,6 +52,15 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
|
||||
return writeModelToLoginPolicy(&addedPolicy.LoginPolicyWriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) orgLoginPolicyWriteModelByID(ctx context.Context, orgID string) (*OrgLoginPolicyWriteModel, error) {
|
||||
policyWriteModel := NewOrgLoginPolicyWriteModel(orgID)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, policyWriteModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policyWriteModel, nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string, policy *domain.LoginPolicy) (*domain.LoginPolicy, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Mf9sf", "Errors.ResourceOwnerMissing")
|
||||
@@ -55,6 +73,12 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "Org-M0sif", "Errors.Org.LoginPolicy.NotFound")
|
||||
}
|
||||
|
||||
err = c.checkLoginPolicyAllowed(ctx, resourceOwner, policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LoginPolicyWriteModel.WriteModel)
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIDP, policy.ForceMFA, policy.PasswordlessType)
|
||||
if !hasChanged {
|
||||
@@ -72,6 +96,30 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
|
||||
return writeModelToLoginPolicy(&existingPolicy.LoginPolicyWriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) checkLoginPolicyAllowed(ctx context.Context, resourceOwner string, policy *domain.LoginPolicy) error {
|
||||
defaultPolicy, err := c.getDefaultLoginPolicy(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requiredFeatures := make([]string, 0)
|
||||
if defaultPolicy.ForceMFA != policy.ForceMFA || !reflect.DeepEqual(defaultPolicy.MultiFactors, policy.MultiFactors) || !reflect.DeepEqual(defaultPolicy.SecondFactors, policy.SecondFactors) {
|
||||
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyFactors)
|
||||
}
|
||||
if defaultPolicy.AllowExternalIDP != policy.AllowExternalIDP || !reflect.DeepEqual(defaultPolicy.IDPProviders, policy.IDPProviders) {
|
||||
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyIDP)
|
||||
}
|
||||
if defaultPolicy.AllowRegister != policy.AllowRegister {
|
||||
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyRegistration)
|
||||
}
|
||||
if defaultPolicy.PasswordlessType != policy.PasswordlessType {
|
||||
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyPasswordless)
|
||||
}
|
||||
if defaultPolicy.AllowUsernamePassword != policy.AllowUsernamePassword {
|
||||
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyUsernameLogin)
|
||||
}
|
||||
return authz.CheckOrgFeatures(ctx, c.tokenVerifier, resourceOwner, requiredFeatures...)
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveLoginPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
|
||||
if orgID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-55Mg9", "Errors.ResourceOwnerMissing")
|
||||
@@ -267,7 +315,7 @@ func (c *Commands) AddMultiFactorToLoginPolicy(ctx context.Context, multiFactor
|
||||
if err != nil {
|
||||
return domain.MultiFactorTypeUnspecified, nil, err
|
||||
}
|
||||
return multiFactorModel.MultiFactoryWriteModel.MFAType, writeModelToObjectDetails(&multiFactorModel.WriteModel), nil
|
||||
return multiFactorModel.MultiFactorWriteModel.MFAType, writeModelToObjectDetails(&multiFactorModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveMultiFactorFromLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType, orgID string) (*domain.ObjectDetails, error) {
|
||||
@@ -285,7 +333,7 @@ func (c *Commands) RemoveMultiFactorFromLoginPolicy(ctx context.Context, multiFa
|
||||
if multiFactorModel.State == domain.FactorStateUnspecified || multiFactorModel.State == domain.FactorStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "Org-3M9df", "Errors.Org.LoginPolicy.MFA.NotExisting")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
|
||||
orgAgg := OrgAggregateFromWriteModel(&multiFactorModel.MultiFactorWriteModel.WriteModel)
|
||||
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLoginPolicyMultiFactorRemovedEvent(ctx, orgAgg, multiFactor))
|
||||
if err != nil {
|
||||
|
@@ -51,12 +51,12 @@ func (wm *OrgSecondFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
}
|
||||
|
||||
type OrgMultiFactorWriteModel struct {
|
||||
MultiFactoryWriteModel
|
||||
MultiFactorWriteModel
|
||||
}
|
||||
|
||||
func NewOrgMultiFactorWriteModel(orgID string, factorType domain.MultiFactorType) *OrgMultiFactorWriteModel {
|
||||
return &OrgMultiFactorWriteModel{
|
||||
MultiFactoryWriteModel{
|
||||
MultiFactorWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: orgID,
|
||||
ResourceOwner: orgID,
|
||||
@@ -82,7 +82,7 @@ func (wm *OrgMultiFactorWriteModel) AppendEvents(events ...eventstore.EventReade
|
||||
}
|
||||
|
||||
func (wm *OrgMultiFactorWriteModel) Reduce() error {
|
||||
return wm.MultiFactoryWriteModel.Reduce()
|
||||
return wm.MultiFactorWriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *OrgMultiFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
|
@@ -6,11 +6,13 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"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/eventstore/repository"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/caos/zitadel/internal/repository/iam"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
@@ -18,7 +20,8 @@ import (
|
||||
|
||||
func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore *eventstore.Eventstore
|
||||
tokenVerifier *authz.TokenVerifier
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -88,12 +91,60 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
err: caos_errs.IsErrorAlreadyExists,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "loginpolicy not allowed, permission denied error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
tokenVerifier: GetMockVerifier(t),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.LoginPolicy{
|
||||
AllowRegister: true,
|
||||
AllowUsernamePassword: true,
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPermissionDenied,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add policy,ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
@@ -109,6 +160,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
},
|
||||
),
|
||||
),
|
||||
tokenVerifier: GetMockVerifier(t, domain.FeatureLoginPolicyUsernameLogin),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -139,7 +191,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore,
|
||||
tokenVerifier: tt.fields.tokenVerifier,
|
||||
}
|
||||
got, err := r.AddLoginPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||
if tt.res.err == nil {
|
||||
@@ -157,7 +210,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
|
||||
|
||||
func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore *eventstore.Eventstore
|
||||
tokenVerifier *authz.TokenVerifier
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -218,6 +272,53 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not allowed, permission denied error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewLoginPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
tokenVerifier: GetMockVerifier(t),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.LoginPolicy{
|
||||
AllowRegister: true,
|
||||
AllowUsernamePassword: true,
|
||||
AllowExternalIDP: true,
|
||||
ForceMFA: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsPermissionDenied,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
@@ -235,7 +336,20 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
domain.PasswordlessTypeAllowed,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
tokenVerifier: GetMockVerifier(t, domain.FeatureLoginPolicyUsernameLogin),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -269,6 +383,18 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewLoginPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PasswordlessTypeNotAllowed,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
@@ -277,6 +403,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
},
|
||||
),
|
||||
),
|
||||
tokenVerifier: GetMockVerifier(t, domain.FeatureLoginPolicyUsernameLogin),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -307,7 +434,8 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore,
|
||||
tokenVerifier: tt.fields.tokenVerifier,
|
||||
}
|
||||
got, err := r.ChangeLoginPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||
if tt.res.err == nil {
|
||||
|
@@ -26,13 +26,13 @@ func (wm *SecondFactorWriteModel) Reduce() error {
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
type MultiFactoryWriteModel struct {
|
||||
type MultiFactorWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
MFAType domain.MultiFactorType
|
||||
State domain.FactorState
|
||||
}
|
||||
|
||||
func (wm *MultiFactoryWriteModel) Reduce() error {
|
||||
func (wm *MultiFactorWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *policy.MultiFactorAddedEvent:
|
||||
|
52
internal/command/setup_step12.go
Normal file
52
internal/command/setup_step12.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/config/types"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step12 struct {
|
||||
TierName string
|
||||
TierDescription string
|
||||
AuditLogRetention types.Duration
|
||||
LoginPolicyFactors bool
|
||||
LoginPolicyIDP bool
|
||||
LoginPolicyPasswordless bool
|
||||
LoginPolicyRegistration bool
|
||||
LoginPolicyUsernameLogin bool
|
||||
PasswordComplexityPolicy bool
|
||||
}
|
||||
|
||||
func (s *Step12) Step() domain.Step {
|
||||
return domain.Step12
|
||||
}
|
||||
|
||||
func (s *Step12) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep12(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep12(ctx context.Context, step *Step12) error {
|
||||
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
|
||||
featuresWriteModel := NewIAMFeaturesWriteModel()
|
||||
featuresEvent, err := c.setDefaultFeatures(ctx, featuresWriteModel, &domain.Features{
|
||||
TierName: step.TierName,
|
||||
TierDescription: step.TierDescription,
|
||||
State: domain.FeaturesStateActive,
|
||||
AuditLogRetention: step.AuditLogRetention.Duration,
|
||||
LoginPolicyFactors: step.LoginPolicyFactors,
|
||||
LoginPolicyIDP: step.LoginPolicyIDP,
|
||||
LoginPolicyPasswordless: step.LoginPolicyPasswordless,
|
||||
LoginPolicyRegistration: step.LoginPolicyRegistration,
|
||||
LoginPolicyUsernameLogin: step.LoginPolicyUsernameLogin,
|
||||
PasswordComplexityPolicy: step.PasswordComplexityPolicy,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []eventstore.EventPusher{featuresEvent}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@@ -23,7 +23,7 @@ func (s *Step9) execute(ctx context.Context, commandSide *Commands) error {
|
||||
func (c *Commands) SetupStep9(ctx context.Context, step *Step9) error {
|
||||
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
|
||||
multiFactorModel := NewIAMMultiFactorWriteModel(domain.MultiFactorTypeU2FWithPIN)
|
||||
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
|
||||
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactorWriteModel.WriteModel)
|
||||
if !step.Passwordless {
|
||||
return []eventstore.EventPusher{}, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user