feat(login): additionally use email/phone for authentication (#4563)

* feat: add ability to disable login by email and phone

* feat: check login by email and phone

* fix: set verified email / phone correctly on notify users

* update projection version

* fix merge

* fix email/phone verified reduce tests

* fix user tests

* loginname check

* cleanup

* fix: update user projection version to handle fixed statement
This commit is contained in:
Livio Spring
2022-10-17 21:19:15 +02:00
committed by GitHub
parent 9ae58b62fd
commit b0b1e94090
54 changed files with 1245 additions and 768 deletions

View File

@@ -1,11 +1,13 @@
package command
import (
"context"
"net/http"
"time"
"github.com/zitadel/zitadel/internal/api/authz"
api_http "github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/command/preparation"
sd "github.com/zitadel/zitadel/internal/config/systemdefaults"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
@@ -132,3 +134,28 @@ func AppendAndReduce(object interface {
object.AppendEvents(events...)
return object.Reduce()
}
func queryAndReduce(ctx context.Context, filter preparation.FilterToQueryReducer, wm eventstore.QueryReducer) error {
events, err := filter(ctx, wm.Query())
if err != nil {
return err
}
if len(events) == 0 {
return nil
}
wm.AppendEvents(events...)
return wm.Reduce()
}
type existsWriteModel interface {
Exists() bool
eventstore.QueryReducer
}
func exists(ctx context.Context, filter preparation.FilterToQueryReducer, wm existsWriteModel) (bool, error) {
err := queryAndReduce(ctx, filter, wm)
if err != nil {
return false, err
}
return wm.Exists(), nil
}

View File

@@ -62,3 +62,7 @@ func (rm *IDPConfigWriteModel) reduceConfigChangedEvent(e *idpconfig.IDPConfigCh
func (rm *IDPConfigWriteModel) reduceConfigStateChanged(configID string, state domain.IDPConfigState) {
rm.State = state
}
func (rm *IDPConfigWriteModel) Exists() bool {
return rm.State.Exists()
}

View File

@@ -72,6 +72,8 @@ type InstanceSetup struct {
HidePasswordReset bool
IgnoreUnknownUsername bool
AllowDomainDiscovery bool
DisableLoginWithEmail bool
DisableLoginWithPhone bool
PasswordlessType domain.PasswordlessType
DefaultRedirectURI string
PasswordCheckLifetime time.Duration
@@ -219,6 +221,8 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str
setup.LoginPolicy.HidePasswordReset,
setup.LoginPolicy.IgnoreUnknownUsername,
setup.LoginPolicy.AllowDomainDiscovery,
setup.LoginPolicy.DisableLoginWithEmail,
setup.LoginPolicy.DisableLoginWithPhone,
setup.LoginPolicy.PasswordlessType,
setup.LoginPolicy.DefaultRedirectURI,
setup.LoginPolicy.PasswordCheckLifetime,

View File

@@ -15,56 +15,17 @@ import (
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
func (c *Commands) ChangeDefaultLoginPolicy(ctx context.Context, policy *domain.LoginPolicy) (*domain.LoginPolicy, error) {
existingPolicy := NewInstanceLoginPolicyWriteModel(ctx)
instanceAgg := InstanceAggregateFromWriteModel(&existingPolicy.LoginPolicyWriteModel.WriteModel)
event, err := c.changeDefaultLoginPolicy(ctx, instanceAgg, existingPolicy, policy)
func (c *Commands) ChangeDefaultLoginPolicy(ctx context.Context, policy *ChangeLoginPolicy) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareChangeDefaultLoginPolicy(instanceAgg, policy))
if err != nil {
return nil, err
}
pushedEvents, err := c.eventstore.Push(ctx, event)
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToLoginPolicy(&existingPolicy.LoginPolicyWriteModel), nil
}
func (c *Commands) changeDefaultLoginPolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, existingPolicy *InstanceLoginPolicyWriteModel, policy *domain.LoginPolicy) (eventstore.Command, error) {
if ok := domain.ValidateDefaultRedirectURI(policy.DefaultRedirectURI); !ok {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-SFdqd", "Errors.IAM.LoginPolicy.RedirectURIInvalid")
}
err := c.defaultLoginPolicyWriteModelByID(ctx, existingPolicy)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-M0sif", "Errors.IAM.LoginPolicy.NotFound")
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx,
instanceAgg,
policy.AllowUsernamePassword,
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.IgnoreUnknownUsernames,
policy.AllowDomainDiscovery,
policy.PasswordlessType,
policy.DefaultRedirectURI,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime,
)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "INSTANCE-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
}
return changedEvent, nil
return pushedEventsToObjectDetails(pushedEvents), nil
}
func (c *Commands) AddIDPProviderToDefaultLoginPolicy(ctx context.Context, idpProvider *domain.IDPProvider) (*domain.IDPProvider, error) {
@@ -255,6 +216,44 @@ func (c *Commands) getDefaultLoginPolicy(ctx context.Context) (*domain.LoginPoli
return policy, nil
}
func prepareChangeDefaultLoginPolicy(a *instance.Aggregate, policy *ChangeLoginPolicy) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if ok := domain.ValidateDefaultRedirectURI(policy.DefaultRedirectURI); !ok {
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-SFdqd", "Errors.IAM.LoginPolicy.RedirectURIInvalid")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
wm := NewInstanceLoginPolicyWriteModel(ctx)
if err := queryAndReduce(ctx, filter, wm); err != nil {
return nil, err
}
if !wm.State.Exists() {
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-M0sif", "Errors.IAM.LoginPolicy.NotFound")
}
changedEvent, hasChanged := wm.NewChangedEvent(ctx, &a.Aggregate,
policy.AllowUsernamePassword,
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.IgnoreUnknownUsernames,
policy.AllowDomainDiscovery,
policy.DisableLoginWithEmail,
policy.DisableLoginWithPhone,
policy.PasswordlessType,
policy.DefaultRedirectURI,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "INSTANCE-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
}
return []eventstore.Command{changedEvent}, nil
}, nil
}
}
func prepareAddDefaultLoginPolicy(
a *instance.Aggregate,
allowUsernamePassword bool,
@@ -264,6 +263,8 @@ func prepareAddDefaultLoginPolicy(
hidePasswordReset bool,
ignoreUnknownUsernames bool,
allowDomainDiscovery bool,
disableLoginWithEmail bool,
disableLoginWithPhone bool,
passwordlessType domain.PasswordlessType,
defaultRedirectURI string,
passwordCheckLifetime time.Duration,
@@ -295,6 +296,8 @@ func prepareAddDefaultLoginPolicy(
hidePasswordReset,
ignoreUnknownUsernames,
allowDomainDiscovery,
disableLoginWithEmail,
disableLoginWithPhone,
passwordlessType,
defaultRedirectURI,
passwordCheckLifetime,

View File

@@ -67,7 +67,9 @@ func (wm *InstanceLoginPolicyWriteModel) NewChangedEvent(
forceMFA,
hidePasswordReset,
ignoreUnknownUsernames,
allowDomainDiscovery bool,
allowDomainDiscovery,
disableLoginWithEmail,
disableLoginWithPhone bool,
passwordlessType domain.PasswordlessType,
defaultRedirectURI string,
passwordCheckLifetime,
@@ -120,6 +122,12 @@ func (wm *InstanceLoginPolicyWriteModel) NewChangedEvent(
if wm.MultiFactorCheckLifetime != multiFactorCheckLifetime {
changes = append(changes, policy.ChangeMultiFactorCheckLifetime(multiFactorCheckLifetime))
}
if wm.DisableLoginWithEmail != disableLoginWithEmail {
changes = append(changes, policy.ChangeDisableLoginWithEmail(disableLoginWithEmail))
}
if wm.DisableLoginWithPhone != disableLoginWithPhone {
changes = append(changes, policy.ChangeDisableLoginWithPhone(disableLoginWithPhone))
}
if len(changes) == 0 {
return nil, false
}

View File

@@ -24,10 +24,10 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
}
type args struct {
ctx context.Context
policy *domain.LoginPolicy
policy *ChangeLoginPolicy
}
type res struct {
want *domain.LoginPolicy
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
@@ -46,7 +46,7 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
policy: &domain.LoginPolicy{
policy: &ChangeLoginPolicy{
AllowRegister: true,
AllowExternalIDP: true,
},
@@ -71,6 +71,8 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"https://example.com/redirect",
time.Hour*1,
@@ -85,7 +87,7 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
},
args: args{
ctx: context.Background(),
policy: &domain.LoginPolicy{
policy: &ChangeLoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
@@ -93,6 +95,8 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
DisableLoginWithEmail: true,
DisableLoginWithPhone: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
@@ -123,6 +127,8 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"https://example.com/redirect",
time.Hour*1,
@@ -145,6 +151,8 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*10,
@@ -159,7 +167,7 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
policy: &domain.LoginPolicy{
policy: &ChangeLoginPolicy{
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
@@ -167,6 +175,8 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
HidePasswordReset: false,
IgnoreUnknownUsernames: false,
AllowDomainDiscovery: false,
DisableLoginWithEmail: false,
DisableLoginWithPhone: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
DefaultRedirectURI: "",
PasswordCheckLifetime: time.Hour * 10,
@@ -177,26 +187,8 @@ func TestCommandSide_ChangeDefaultLoginPolicy(t *testing.T) {
},
},
res: res{
want: &domain.LoginPolicy{
ObjectRoot: models.ObjectRoot{
InstanceID: "INSTANCE",
AggregateID: "INSTANCE",
ResourceOwner: "INSTANCE",
},
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
ForceMFA: false,
HidePasswordReset: false,
IgnoreUnknownUsernames: false,
AllowDomainDiscovery: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
DefaultRedirectURI: "",
PasswordCheckLifetime: time.Hour * 10,
ExternalLoginCheckLifetime: time.Hour * 20,
MFAInitSkipLifetime: time.Hour * 30,
SecondFactorCheckLifetime: time.Hour * 40,
MultiFactorCheckLifetime: time.Hour * 50,
want: &domain.ObjectDetails{
ResourceOwner: "INSTANCE",
},
},
},
@@ -287,6 +279,8 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -326,6 +320,8 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -385,6 +381,8 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -526,6 +524,8 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -565,6 +565,8 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -617,6 +619,8 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -674,6 +678,8 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -739,6 +745,8 @@ func TestCommandSide_RemoveIDPProviderDefaultLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -1293,7 +1301,8 @@ func TestCommandSide_RemoveMultiFactorDefaultLoginPolicy(t *testing.T) {
}
}
func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allowUsernamePassword, allowExternalIDP, forceMFA, hidePasswordReset, ignoreUnknownUsernames, allowDomainDiscovery bool,
func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allowUsernamePassword, allowExternalIDP, forceMFA,
hidePasswordReset, ignoreUnknownUsernames, allowDomainDiscovery, disableLoginWithEmail, disableLoginWithPhone bool,
passwordlessType domain.PasswordlessType,
redirectURI string,
passwordLifetime, externalLoginLifetime, mfaInitSkipLifetime, secondFactorLifetime, multiFactorLifetime time.Duration) *instance.LoginPolicyChangedEvent {
@@ -1307,6 +1316,8 @@ func newDefaultLoginPolicyChangedEvent(ctx context.Context, allowRegister, allow
policy.ChangeHidePasswordReset(hidePasswordReset),
policy.ChangeIgnoreUnknownUsernames(ignoreUnknownUsernames),
policy.ChangeAllowDomainDiscovery(allowDomainDiscovery),
policy.ChangeDisableLoginWithEmail(disableLoginWithEmail),
policy.ChangeDisableLoginWithPhone(disableLoginWithPhone),
policy.ChangePasswordlessType(passwordlessType),
policy.ChangeDefaultRedirectURI(redirectURI),
policy.ChangePasswordCheckLifetime(passwordLifetime),

View File

@@ -2,9 +2,12 @@ package command
import (
"context"
"time"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/command/preparation"
"github.com/zitadel/zitadel/internal/domain"
caos_errs "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
@@ -12,74 +15,63 @@ import (
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, policy *domain.LoginPolicy) (*domain.LoginPolicy, error) {
if resourceOwner == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Fn8ds", "Errors.ResourceOwnerMissing")
}
if ok := domain.ValidateDefaultRedirectURI(policy.DefaultRedirectURI); !ok {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-WSfdq", "Errors.Org.LoginPolicy.RedirectURIInvalid")
}
addedPolicy := NewOrgLoginPolicyWriteModel(resourceOwner)
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
type AddLoginPolicy struct {
AllowUsernamePassword bool
AllowRegister bool
AllowExternalIDP bool
IDPProviders []*AddLoginPolicyIDP
ForceMFA bool
SecondFactors []domain.SecondFactorType
MultiFactors []domain.MultiFactorType
PasswordlessType domain.PasswordlessType
HidePasswordReset bool
IgnoreUnknownUsernames bool
AllowDomainDiscovery bool
DefaultRedirectURI string
PasswordCheckLifetime time.Duration
ExternalLoginCheckLifetime time.Duration
MFAInitSkipLifetime time.Duration
SecondFactorCheckLifetime time.Duration
MultiFactorCheckLifetime time.Duration
DisableLoginWithEmail bool
DisableLoginWithPhone bool
}
type AddLoginPolicyIDP struct {
ConfigID string
Type domain.IdentityProviderType
}
type ChangeLoginPolicy struct {
AllowUsernamePassword bool
AllowRegister bool
AllowExternalIDP bool
ForceMFA bool
PasswordlessType domain.PasswordlessType
HidePasswordReset bool
IgnoreUnknownUsernames bool
AllowDomainDiscovery bool
DefaultRedirectURI string
PasswordCheckLifetime time.Duration
ExternalLoginCheckLifetime time.Duration
MFAInitSkipLifetime time.Duration
SecondFactorCheckLifetime time.Duration
MultiFactorCheckLifetime time.Duration
DisableLoginWithEmail bool
DisableLoginWithPhone bool
}
func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, policy *AddLoginPolicy) (*domain.ObjectDetails, error) {
orgAgg := org.NewAggregate(resourceOwner)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddLoginPolicy(orgAgg, policy))
if err != nil {
return nil, err
}
if addedPolicy.State == domain.PolicyStateActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "Org-Dgfb2", "Errors.Org.LoginPolicy.AlreadyExists")
}
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
cmds := []eventstore.Command{
org.NewLoginPolicyAddedEvent(
ctx,
orgAgg,
policy.AllowUsernamePassword,
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.IgnoreUnknownUsernames,
policy.AllowDomainDiscovery,
policy.PasswordlessType,
policy.DefaultRedirectURI,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime),
}
for _, factor := range policy.SecondFactors {
if !factor.Valid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-SFeea", "Errors.Org.LoginPolicy.MFA.Unspecified")
}
cmds = append(cmds, org.NewLoginPolicySecondFactorAddedEvent(ctx, orgAgg, factor))
}
for _, factor := range policy.MultiFactors {
if !factor.Valid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-WSfrg", "Errors.Org.LoginPolicy.MFA.Unspecified")
}
cmds = append(cmds, org.NewLoginPolicyMultiFactorAddedEvent(ctx, orgAgg, factor))
}
for _, provider := range policy.IDPProviders {
if provider.Type == domain.IdentityProviderTypeOrg {
_, err = c.getOrgIDPConfigByID(ctx, provider.IDPConfigID, resourceOwner)
} else {
_, err = c.getInstanceIDPConfigByID(ctx, provider.IDPConfigID)
}
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "Org-FEd32", "Errors.IDPConfig.NotExisting")
}
cmds = append(cmds, org.NewIdentityProviderAddedEvent(ctx, orgAgg, provider.IDPConfigID, provider.Type))
}
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
if err != nil {
return nil, err
}
err = AppendAndReduce(addedPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToLoginPolicy(&addedPolicy.LoginPolicyWriteModel), nil
return pushedEventsToObjectDetails(pushedEvents), nil
}
func (c *Commands) orgLoginPolicyWriteModelByID(ctx context.Context, orgID string) (*OrgLoginPolicyWriteModel, error) {
@@ -102,54 +94,17 @@ func (c *Commands) getOrgLoginPolicy(ctx context.Context, orgID string) (*domain
return c.getDefaultLoginPolicy(ctx)
}
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")
}
if ok := domain.ValidateDefaultRedirectURI(policy.DefaultRedirectURI); !ok {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Sfd21", "Errors.Org.LoginPolicy.RedirectURIInvalid")
}
existingPolicy := NewOrgLoginPolicyWriteModel(resourceOwner)
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string, policy *ChangeLoginPolicy) (*domain.ObjectDetails, error) {
orgAgg := org.NewAggregate(resourceOwner)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareChangeLoginPolicy(orgAgg, policy))
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "Org-M0sif", "Errors.Org.LoginPolicy.NotFound")
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LoginPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(
ctx,
orgAgg,
policy.AllowUsernamePassword,
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.IgnoreUnknownUsernames,
policy.AllowDomainDiscovery,
policy.PasswordlessType,
policy.DefaultRedirectURI,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged")
}
pushedEvents, err := c.eventstore.Push(ctx, changedEvent)
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
if err != nil {
return nil, err
}
err = AppendAndReduce(existingPolicy, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToLoginPolicy(&existingPolicy.LoginPolicyWriteModel), nil
return pushedEventsToObjectDetails(pushedEvents), nil
}
func (c *Commands) RemoveLoginPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
@@ -437,3 +392,106 @@ func (c *Commands) orgLoginPolicyAuthFactorsWriteModel(ctx context.Context, orgI
}
return writeModel, nil
}
func prepareAddLoginPolicy(a *org.Aggregate, policy *AddLoginPolicy) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if ok := domain.ValidateDefaultRedirectURI(policy.DefaultRedirectURI); !ok {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-WSfdq", "Errors.Org.LoginPolicy.RedirectURIInvalid")
}
for _, factor := range policy.SecondFactors {
if !factor.Valid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-SFeea", "Errors.Org.LoginPolicy.MFA.Unspecified")
}
}
for _, factor := range policy.MultiFactors {
if !factor.Valid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-WSfrg", "Errors.Org.LoginPolicy.MFA.Unspecified")
}
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
if exists, err := exists(ctx, filter, NewOrgLoginPolicyWriteModel(a.ID)); exists || err != nil {
return nil, caos_errs.ThrowAlreadyExists(nil, "Org-Dgfb2", "Errors.Org.LoginPolicy.AlreadyExists")
}
for _, idp := range policy.IDPProviders {
exists, err := idpExists(ctx, filter, idp)
if !exists || err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "Org-FEd32", "Errors.IDPConfig.NotExisting")
}
}
cmds := make([]eventstore.Command, 0, len(policy.SecondFactors)+len(policy.MultiFactors)+len(policy.IDPProviders)+1)
cmds = append(cmds, org.NewLoginPolicyAddedEvent(ctx, &a.Aggregate,
policy.AllowUsernamePassword,
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.IgnoreUnknownUsernames,
policy.AllowDomainDiscovery,
policy.DisableLoginWithEmail,
policy.DisableLoginWithPhone,
policy.PasswordlessType,
policy.DefaultRedirectURI,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime,
))
for _, factor := range policy.SecondFactors {
cmds = append(cmds, org.NewLoginPolicySecondFactorAddedEvent(ctx, &a.Aggregate, factor))
}
for _, factor := range policy.MultiFactors {
cmds = append(cmds, org.NewLoginPolicyMultiFactorAddedEvent(ctx, &a.Aggregate, factor))
}
for _, idp := range policy.IDPProviders {
cmds = append(cmds, org.NewIdentityProviderAddedEvent(ctx, &a.Aggregate, idp.ConfigID, idp.Type))
}
return cmds, nil
}, nil
}
}
func prepareChangeLoginPolicy(a *org.Aggregate, policy *ChangeLoginPolicy) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if ok := domain.ValidateDefaultRedirectURI(policy.DefaultRedirectURI); !ok {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Sfd21", "Errors.Org.LoginPolicy.RedirectURIInvalid")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
wm := NewOrgLoginPolicyWriteModel(a.ID)
if err := queryAndReduce(ctx, filter, wm); err != nil {
return nil, err
}
if !wm.State.Exists() {
return nil, caos_errs.ThrowNotFound(nil, "Org-M0sif", "Errors.Org.LoginPolicy.NotFound")
}
changedEvent, hasChanged := wm.NewChangedEvent(ctx, &a.Aggregate,
policy.AllowUsernamePassword,
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.IgnoreUnknownUsernames,
policy.AllowDomainDiscovery,
policy.DisableLoginWithEmail,
policy.DisableLoginWithPhone,
policy.PasswordlessType,
policy.DefaultRedirectURI,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-5M9vdd", "Errors.Org.LoginPolicy.NotChanged")
}
return []eventstore.Command{changedEvent}, nil
}, nil
}
}
func idpExists(ctx context.Context, filter preparation.FilterToQueryReducer, idp *AddLoginPolicyIDP) (bool, error) {
if idp.Type == domain.IdentityProviderTypeSystem {
return exists(ctx, filter, NewInstanceIDPConfigWriteModel(ctx, idp.ConfigID))
}
return exists(ctx, filter, NewOrgIDPConfigWriteModel(idp.ConfigID, authz.GetCtxData(ctx).ResourceOwner))
}

View File

@@ -69,7 +69,9 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
forceMFA,
hidePasswordReset,
ignoreUnknownUsernames,
allowDomainDiscovery bool,
allowDomainDiscovery,
disableLoginWithEmail,
disableLoginWithPhone bool,
passwordlessType domain.PasswordlessType,
defaultRedirectURI string,
passwordCheckLifetime,
@@ -122,6 +124,12 @@ func (wm *OrgLoginPolicyWriteModel) NewChangedEvent(
if wm.DefaultRedirectURI != defaultRedirectURI {
changes = append(changes, policy.ChangeDefaultRedirectURI(defaultRedirectURI))
}
if wm.DisableLoginWithEmail != disableLoginWithEmail {
changes = append(changes, policy.ChangeDisableLoginWithEmail(disableLoginWithEmail))
}
if wm.DisableLoginWithPhone != disableLoginWithPhone {
changes = append(changes, policy.ChangeDisableLoginWithPhone(disableLoginWithPhone))
}
if len(changes) == 0 {
return nil, false
}

View File

@@ -33,10 +33,10 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
type args struct {
ctx context.Context
orgID string
policy *domain.LoginPolicy
policy *AddLoginPolicy
}
type res struct {
want *domain.LoginPolicy
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
@@ -45,25 +45,6 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
args args
res res
}{
{
name: "org id missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
},
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "loginpolicy already existing, already exists error",
fields: fields{
@@ -80,6 +61,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true,
true,
true,
false,
false,
domain.PasswordlessTypeAllowed,
"https://example.com/redirect",
time.Hour*1,
@@ -95,7 +78,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
policy: &AddLoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
@@ -133,6 +116,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"https://example.com/redirect",
time.Hour*1,
@@ -149,7 +134,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
policy: &AddLoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
@@ -157,6 +142,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
DisableLoginWithEmail: true,
DisableLoginWithPhone: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
@@ -167,25 +154,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
},
},
res: res{
want: &domain.LoginPolicy{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
ResourceOwner: "org1",
},
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
@@ -194,13 +164,12 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
policy: &AddLoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
@@ -208,6 +177,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
DisableLoginWithEmail: true,
DisableLoginWithPhone: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
@@ -240,6 +211,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"https://example.com/redirect",
time.Hour*1,
@@ -268,7 +241,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
policy: &AddLoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
@@ -276,6 +249,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
DisableLoginWithEmail: true,
DisableLoginWithPhone: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
@@ -288,25 +263,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
},
},
res: res{
want: &domain.LoginPolicy{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
ResourceOwner: "org1",
},
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
@@ -322,7 +280,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
policy: &AddLoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
@@ -330,6 +288,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
DisableLoginWithEmail: true,
DisableLoginWithPhone: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
@@ -337,10 +297,10 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
IDPProviders: []*domain.IDPProvider{
IDPProviders: []*AddLoginPolicyIDP{
{
Type: domain.IdentityProviderTypeSystem,
IDPConfigID: "invalid",
Type: domain.IdentityProviderTypeSystem,
ConfigID: "invalid",
},
},
},
@@ -379,6 +339,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"https://example.com/redirect",
time.Hour*1,
@@ -402,7 +364,7 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
policy: &AddLoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
@@ -410,6 +372,8 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
DisableLoginWithEmail: true,
DisableLoginWithPhone: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
@@ -417,34 +381,17 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
IDPProviders: []*domain.IDPProvider{
IDPProviders: []*AddLoginPolicyIDP{
{
Type: domain.IdentityProviderTypeSystem,
IDPConfigID: "config1",
Type: domain.IdentityProviderTypeSystem,
ConfigID: "config1",
},
},
},
},
res: res{
want: &domain.LoginPolicy{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
ResourceOwner: "org1",
},
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
@@ -475,10 +422,10 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
type args struct {
ctx context.Context
orgID string
policy *domain.LoginPolicy
policy *ChangeLoginPolicy
}
type res struct {
want *domain.LoginPolicy
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
@@ -487,30 +434,6 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
args args
res res
}{
{
name: "org id missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
},
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "loginpolicy not existing, not found error",
fields: fields{
@@ -522,13 +445,15 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
policy: &ChangeLoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
DisableLoginWithEmail: true,
DisableLoginWithPhone: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
},
@@ -553,6 +478,8 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"https://example.com/redirect",
time.Hour*1,
@@ -568,7 +495,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
policy: &ChangeLoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
@@ -576,6 +503,8 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
HidePasswordReset: true,
IgnoreUnknownUsernames: true,
AllowDomainDiscovery: true,
DisableLoginWithEmail: true,
DisableLoginWithPhone: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
DefaultRedirectURI: "https://example.com/redirect",
PasswordCheckLifetime: time.Hour * 1,
@@ -605,6 +534,8 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"https://example.com/redirect",
time.Hour*1,
@@ -627,6 +558,8 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
&duration10,
@@ -643,13 +576,15 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
policy: &ChangeLoginPolicy{
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
ForceMFA: false,
IgnoreUnknownUsernames: false,
AllowDomainDiscovery: false,
DisableLoginWithEmail: false,
DisableLoginWithPhone: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
DefaultRedirectURI: "",
PasswordCheckLifetime: time.Hour * 10,
@@ -660,25 +595,8 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
},
},
res: res{
want: &domain.LoginPolicy{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
ResourceOwner: "org1",
},
AllowRegister: false,
AllowUsernamePassword: false,
AllowExternalIDP: false,
ForceMFA: false,
HidePasswordReset: false,
IgnoreUnknownUsernames: false,
AllowDomainDiscovery: false,
PasswordlessType: domain.PasswordlessTypeNotAllowed,
DefaultRedirectURI: "",
PasswordCheckLifetime: time.Hour * 10,
ExternalLoginCheckLifetime: time.Hour * 20,
MFAInitSkipLifetime: time.Hour * 30,
SecondFactorCheckLifetime: time.Hour * 40,
MultiFactorCheckLifetime: time.Hour * 50,
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
@@ -766,6 +684,8 @@ func TestCommandSide_RemoveLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -907,6 +827,8 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -949,6 +871,8 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -1011,6 +935,8 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -1176,6 +1102,8 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -1218,6 +1146,8 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -1272,6 +1202,8 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -1333,6 +1265,8 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -1402,6 +1336,8 @@ func TestCommandSide_RemoveIDPProviderLoginPolicy(t *testing.T) {
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
"",
time.Hour*1,
@@ -2020,7 +1956,8 @@ func TestCommandSide_RemoveMultiFactorLoginPolicy(t *testing.T) {
}
}
func newLoginPolicyChangedEvent(ctx context.Context, orgID string, usernamePassword, register, externalIDP, mfa, passwordReset, ignoreUnknownUsernames, allowDomainDiscovery bool,
func newLoginPolicyChangedEvent(ctx context.Context, orgID string,
usernamePassword, register, externalIDP, mfa, passwordReset, ignoreUnknownUsernames, allowDomainDiscovery, disableLoginWithEmail, disableLoginWithPhone bool,
passwordlessType domain.PasswordlessType,
redirectURI string,
passwordLifetime, externalLoginLifetime, mfaInitSkipLifetime, secondFactorLifetime, multiFactorLifetime *time.Duration) *org.LoginPolicyChangedEvent {
@@ -2034,6 +1971,8 @@ func newLoginPolicyChangedEvent(ctx context.Context, orgID string, usernamePassw
policy.ChangeAllowDomainDiscovery(allowDomainDiscovery),
policy.ChangePasswordlessType(passwordlessType),
policy.ChangeDefaultRedirectURI(redirectURI),
policy.ChangeDisableLoginWithEmail(disableLoginWithEmail),
policy.ChangeDisableLoginWithPhone(disableLoginWithPhone),
}
if passwordLifetime != nil {
changes = append(changes, policy.ChangePasswordCheckLifetime(*passwordLifetime))

View File

@@ -18,6 +18,8 @@ type LoginPolicyWriteModel struct {
HidePasswordReset bool
IgnoreUnknownUsernames bool
AllowDomainDiscovery bool
DisableLoginWithEmail bool
DisableLoginWithPhone bool
PasswordlessType domain.PasswordlessType
DefaultRedirectURI string
PasswordCheckLifetime time.Duration
@@ -40,6 +42,8 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
wm.HidePasswordReset = e.HidePasswordReset
wm.IgnoreUnknownUsernames = e.IgnoreUnknownUsernames
wm.AllowDomainDiscovery = e.AllowDomainDiscovery
wm.DisableLoginWithEmail = e.DisableLoginWithEmail
wm.DisableLoginWithPhone = e.DisableLoginWithPhone
wm.DefaultRedirectURI = e.DefaultRedirectURI
wm.PasswordCheckLifetime = e.PasswordCheckLifetime
wm.ExternalLoginCheckLifetime = e.ExternalLoginCheckLifetime
@@ -90,9 +94,19 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
if e.MultiFactorCheckLifetime != nil {
wm.MultiFactorCheckLifetime = *e.MultiFactorCheckLifetime
}
if e.DisableLoginWithEmail != nil {
wm.DisableLoginWithEmail = *e.DisableLoginWithEmail
}
if e.DisableLoginWithPhone != nil {
wm.DisableLoginWithPhone = *e.DisableLoginWithPhone
}
case *policy.LoginPolicyRemovedEvent:
wm.State = domain.PolicyStateRemoved
}
}
return wm.WriteModel.Reduce()
}
func (wm *LoginPolicyWriteModel) Exists() bool {
return wm.State.Exists()
}

View File

@@ -1158,6 +1158,8 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -1196,6 +1198,8 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -1235,6 +1239,8 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -1290,6 +1296,8 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -1379,6 +1387,8 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -1475,6 +1485,8 @@ func TestCommandSide_CheckPassword(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,

View File

@@ -1680,6 +1680,8 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -1747,6 +1749,8 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -1814,6 +1818,8 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -1898,6 +1904,8 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -2040,6 +2048,8 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -2150,6 +2160,8 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -2254,6 +2266,8 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,
@@ -2380,6 +2394,8 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
"",
time.Hour*1,