feat: Policy check (#149)

* check password complexity policy

* check password complexity policy

* fix tests

* Update internal/admin/repository/eventsourcing/setup/setup.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* changes for mr

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Fabi 2020-05-29 08:44:01 +02:00 committed by GitHub
parent 5a7d44327e
commit a4c7b39552
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 477 additions and 61 deletions

View File

@ -10,13 +10,19 @@ import (
org_model "github.com/caos/zitadel/internal/org/model" org_model "github.com/caos/zitadel/internal/org/model"
org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing"
org_view "github.com/caos/zitadel/internal/org/repository/view" org_view "github.com/caos/zitadel/internal/org/repository/view"
policy_es "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
usr_es "github.com/caos/zitadel/internal/user/repository/eventsourcing" usr_es "github.com/caos/zitadel/internal/user/repository/eventsourcing"
) )
const (
DEFAULT_POLICY = "0"
)
type OrgRepo struct { type OrgRepo struct {
Eventstore eventstore.Eventstore Eventstore eventstore.Eventstore
OrgEventstore *org_es.OrgEventstore OrgEventstore *org_es.OrgEventstore
UserEventstore *usr_es.UserEventstore UserEventstore *usr_es.UserEventstore
PolicyEventstore *policy_es.PolicyEventstore
View *admin_view.View View *admin_view.View
@ -24,12 +30,16 @@ type OrgRepo struct {
} }
func (repo *OrgRepo) SetUpOrg(ctx context.Context, setUp *admin_model.SetupOrg) (*admin_model.SetupOrg, error) { func (repo *OrgRepo) SetUpOrg(ctx context.Context, setUp *admin_model.SetupOrg) (*admin_model.SetupOrg, error) {
policy, err := repo.PolicyEventstore.GetPasswordComplexityPolicy(ctx, DEFAULT_POLICY)
if err != nil {
return nil, err
}
org, aggregates, err := repo.OrgEventstore.PrepareCreateOrg(ctx, setUp.Org) org, aggregates, err := repo.OrgEventstore.PrepareCreateOrg(ctx, setUp.Org)
if err != nil { if err != nil {
return nil, err return nil, err
} }
user, userAggregates, err := repo.UserEventstore.PrepareCreateUser(ctx, setUp.User, org.AggregateID) user, userAggregates, err := repo.UserEventstore.PrepareCreateUser(ctx, setUp.User, policy, org.AggregateID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2,12 +2,15 @@ package eventstore
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/api/auth"
policy_event "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
) )
type UserRepo struct { type UserRepo struct {
UserEvents *usr_event.UserEventstore UserEvents *usr_event.UserEventstore
PolicyEvents *policy_event.PolicyEventstore
} }
func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_model.User, err error) { func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_model.User, err error) {
@ -15,9 +18,21 @@ func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_mod
} }
func (repo *UserRepo) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) { func (repo *UserRepo) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) {
return repo.UserEvents.CreateUser(ctx, user) policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, auth.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return repo.UserEvents.CreateUser(ctx, user, policy)
} }
func (repo *UserRepo) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) { func (repo *UserRepo) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) {
return repo.UserEvents.RegisterUser(ctx, user, resourceOwner) policyResourceOwner := auth.GetCtxData(ctx).OrgID
if resourceOwner != "" {
policyResourceOwner = resourceOwner
}
policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, policyResourceOwner)
if err != nil {
return nil, err
}
return repo.UserEvents.RegisterUser(ctx, user, policy, resourceOwner)
} }

View File

@ -2,6 +2,7 @@ package eventsourcing
import ( import (
"context" "context"
es_policy "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/eventstore" "github.com/caos/zitadel/internal/admin/repository/eventsourcing/eventstore"
@ -61,7 +62,13 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults) (
if err != nil { if err != nil {
return nil, err return nil, err
} }
policy, err := es_policy.StartPolicy(es_policy.PolicyConfig{
Eventstore: es,
Cache: conf.Eventstore.Cache,
}, systemDefaults)
if err != nil {
return nil, err
}
sqlClient, err := conf.View.Start() sqlClient, err := conf.View.Start()
if err != nil { if err != nil {
return nil, err return nil, err
@ -71,7 +78,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults) (
return nil, err return nil, err
} }
eventstoreRepos := setup.EventstoreRepos{OrgEvents: org, UserEvents: user, ProjectEvents: project, IamEvents: iam} eventstoreRepos := setup.EventstoreRepos{OrgEvents: org, UserEvents: user, ProjectEvents: project, IamEvents: iam, PolicyEvents: policy}
err = setup.StartSetup(systemDefaults, eventstoreRepos).Execute(ctx) err = setup.StartSetup(systemDefaults, eventstoreRepos).Execute(ctx)
logging.Log("SERVE-k280HZ").OnError(err).Panic("failed to execute setup") logging.Log("SERVE-k280HZ").OnError(err).Panic("failed to execute setup")

View File

@ -2,6 +2,8 @@ package setup
import ( import (
"context" "context"
policy_model "github.com/caos/zitadel/internal/policy/model"
policy_event "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
"time" "time"
"github.com/caos/logging" "github.com/caos/logging"
@ -31,13 +33,15 @@ type EventstoreRepos struct {
OrgEvents *org_event.OrgEventstore OrgEvents *org_event.OrgEventstore
UserEvents *usr_event.UserEventstore UserEvents *usr_event.UserEventstore
ProjectEvents *proj_event.ProjectEventstore ProjectEvents *proj_event.ProjectEventstore
PolicyEvents *policy_event.PolicyEventstore
} }
type initializer struct { type initializer struct {
*Setup *Setup
createdUsers map[string]*usr_model.User createdUsers map[string]*usr_model.User
createdOrgs map[string]*org_model.Org createdOrgs map[string]*org_model.Org
createdProjects map[string]*proj_model.Project createdProjects map[string]*proj_model.Project
pwComplexityPolicy *policy_model.PasswordComplexityPolicy
} }
const ( const (
@ -55,6 +59,7 @@ const (
OIDCAuthMethodType_NONE = "NONE" OIDCAuthMethodType_NONE = "NONE"
OIDCAuthMethodType_BASIC = "BASIC" OIDCAuthMethodType_BASIC = "BASIC"
OIDCAuthMethodType_POST = "POST" OIDCAuthMethodType_POST = "POST"
DEFAULT_POLICY = "0"
) )
func StartSetup(sd systemdefaults.SystemDefaults, repos EventstoreRepos) *Setup { func StartSetup(sd systemdefaults.SystemDefaults, repos EventstoreRepos) *Setup {
@ -92,6 +97,13 @@ func (s *Setup) Execute(ctx context.Context) error {
createdProjects: make(map[string]*proj_model.Project), createdProjects: make(map[string]*proj_model.Project),
} }
pwComplexityPolicy, err := s.repos.PolicyEvents.GetPasswordComplexityPolicy(ctx, DEFAULT_POLICY)
if err != nil {
logging.Log("SETUP-9osWF").WithError(err).Error("unable to read complexity policy")
return err
}
setUp.pwComplexityPolicy = pwComplexityPolicy
err = setUp.orgs(ctx, s.setUpConfig.Orgs) err = setUp.orgs(ctx, s.setUpConfig.Orgs)
if err != nil { if err != nil {
logging.Log("SETUP-p4oWq").WithError(err).Error("unable to set up orgs") logging.Log("SETUP-p4oWq").WithError(err).Error("unable to set up orgs")
@ -264,7 +276,7 @@ func (setUp *initializer) user(ctx context.Context, user types.User) (*usr_model
SecretString: user.Password, SecretString: user.Password,
}, },
} }
return setUp.repos.UserEvents.CreateUser(ctx, createUser) return setUp.repos.UserEvents.CreateUser(ctx, createUser, setUp.pwComplexityPolicy)
} }
func (setUp *initializer) orgOwners(ctx context.Context, org *org_model.Org, owners []string) error { func (setUp *initializer) orgOwners(ctx context.Context, org *org_model.Org, owners []string) error {

View File

@ -7,13 +7,15 @@ import (
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view" "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models"
policy_event "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
"github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/model"
user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
) )
type UserRepo struct { type UserRepo struct {
UserEvents *user_event.UserEventstore UserEvents *user_event.UserEventstore
View *view.View PolicyEvents *policy_event.PolicyEventstore
View *view.View
} }
func (repo *UserRepo) Health(ctx context.Context) error { func (repo *UserRepo) Health(ctx context.Context) error {
@ -21,7 +23,15 @@ func (repo *UserRepo) Health(ctx context.Context) error {
} }
func (repo *UserRepo) Register(ctx context.Context, user *model.User, resourceOwner string) (*model.User, error) { func (repo *UserRepo) Register(ctx context.Context, user *model.User, resourceOwner string) (*model.User, error) {
return repo.UserEvents.RegisterUser(ctx, user, resourceOwner) policyResourceOwner := auth.GetCtxData(ctx).OrgID
if resourceOwner != "" {
policyResourceOwner = resourceOwner
}
policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, policyResourceOwner)
if err != nil {
return nil, err
}
return repo.UserEvents.RegisterUser(ctx, user, policy, resourceOwner)
} }
func (repo *UserRepo) MyProfile(ctx context.Context) (*model.Profile, error) { func (repo *UserRepo) MyProfile(ctx context.Context) (*model.Profile, error) {
@ -85,7 +95,11 @@ func (repo *UserRepo) ChangeMyAddress(ctx context.Context, address *model.Addres
} }
func (repo *UserRepo) ChangeMyPassword(ctx context.Context, old, new string) error { func (repo *UserRepo) ChangeMyPassword(ctx context.Context, old, new string) error {
_, err := repo.UserEvents.ChangePassword(ctx, auth.GetCtxData(ctx).UserID, old, new) policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, auth.GetCtxData(ctx).OrgID)
if err != nil {
return err
}
_, err = repo.UserEvents.ChangePassword(ctx, policy, auth.GetCtxData(ctx).UserID, old, new)
return err return err
} }
@ -114,7 +128,11 @@ func (repo *UserRepo) RequestPasswordReset(ctx context.Context, username string)
} }
func (repo *UserRepo) SetPassword(ctx context.Context, userID, code, password string) error { func (repo *UserRepo) SetPassword(ctx context.Context, userID, code, password string) error {
return repo.UserEvents.SetPassword(ctx, userID, code, password) policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, auth.GetCtxData(ctx).OrgID)
if err != nil {
return err
}
return repo.UserEvents.SetPassword(ctx, policy, userID, code, password)
} }
func (repo *UserRepo) SignOut(ctx context.Context, agentID, userID string) error { func (repo *UserRepo) SignOut(ctx context.Context, agentID, userID string) error {

View File

@ -13,6 +13,7 @@ import (
es_int "github.com/caos/zitadel/internal/eventstore" es_int "github.com/caos/zitadel/internal/eventstore"
es_spol "github.com/caos/zitadel/internal/eventstore/spooler" es_spol "github.com/caos/zitadel/internal/eventstore/spooler"
"github.com/caos/zitadel/internal/id" "github.com/caos/zitadel/internal/id"
es_policy "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
es_user "github.com/caos/zitadel/internal/user/repository/eventsourcing" es_user "github.com/caos/zitadel/internal/user/repository/eventsourcing"
) )
@ -44,7 +45,16 @@ func Start(conf Config, systemDefaults sd.SystemDefaults) (*EsRepository, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
policy, err := es_policy.StartPolicy(
es_policy.PolicyConfig{
Eventstore: es,
Cache: conf.Eventstore.Cache,
},
systemDefaults,
)
if err != nil {
return nil, err
}
user, err := es_user.StartUser( user, err := es_user.StartUser(
es_user.UserConfig{ es_user.UserConfig{
Eventstore: es, Eventstore: es,
@ -66,8 +76,9 @@ func Start(conf Config, systemDefaults sd.SystemDefaults) (*EsRepository, error)
return &EsRepository{ return &EsRepository{
spool, spool,
eventstore.UserRepo{ eventstore.UserRepo{
UserEvents: user, UserEvents: user,
View: view, PolicyEvents: policy,
View: view,
}, },
eventstore.AuthRequestRepo{ eventstore.AuthRequestRepo{
UserEvents: user, UserEvents: user,

View File

@ -2,16 +2,19 @@ package eventstore
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/api/auth"
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
policy_event "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
usr_model "github.com/caos/zitadel/internal/user/model" usr_model "github.com/caos/zitadel/internal/user/model"
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
"github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/user/repository/view/model"
) )
type UserRepo struct { type UserRepo struct {
SearchLimit uint64 SearchLimit uint64
UserEvents *usr_event.UserEventstore UserEvents *usr_event.UserEventstore
View *view.View PolicyEvents *policy_event.PolicyEventstore
View *view.View
} }
func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_model.User, err error) { func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_model.User, err error) {
@ -19,11 +22,23 @@ func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_mod
} }
func (repo *UserRepo) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) { func (repo *UserRepo) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) {
return repo.UserEvents.CreateUser(ctx, user) policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, auth.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return repo.UserEvents.CreateUser(ctx, user, policy)
} }
func (repo *UserRepo) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) { func (repo *UserRepo) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) {
return repo.UserEvents.RegisterUser(ctx, user, resourceOwner) policyResourceOwner := auth.GetCtxData(ctx).OrgID
if resourceOwner != "" {
policyResourceOwner = resourceOwner
}
policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, policyResourceOwner)
if err != nil {
return nil, err
}
return repo.UserEvents.RegisterUser(ctx, user, policy, resourceOwner)
} }
func (repo *UserRepo) DeactivateUser(ctx context.Context, id string) (*usr_model.User, error) { func (repo *UserRepo) DeactivateUser(ctx context.Context, id string) (*usr_model.User, error) {
@ -73,7 +88,11 @@ func (repo *UserRepo) UserMfas(ctx context.Context, userID string) ([]*usr_model
} }
func (repo *UserRepo) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) { func (repo *UserRepo) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) {
return repo.UserEvents.SetOneTimePassword(ctx, password) policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, auth.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return repo.UserEvents.SetOneTimePassword(ctx, policy, password)
} }
func (repo *UserRepo) RequestSetPassword(ctx context.Context, id string, notifyType usr_model.NotificationType) error { func (repo *UserRepo) RequestSetPassword(ctx context.Context, id string, notifyType usr_model.NotificationType) error {

View File

@ -86,7 +86,7 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, roles []string) (*EsRe
spooler: spool, spooler: spool,
OrgRepository: eventstore.OrgRepository{conf.SearchLimit, org, view, roles}, OrgRepository: eventstore.OrgRepository{conf.SearchLimit, org, view, roles},
ProjectRepo: eventstore.ProjectRepo{conf.SearchLimit, project, view, roles}, ProjectRepo: eventstore.ProjectRepo{conf.SearchLimit, project, view, roles},
UserRepo: eventstore.UserRepo{conf.SearchLimit, user, view}, UserRepo: eventstore.UserRepo{conf.SearchLimit, user, policy, view},
UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, usergrant, view}, UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, usergrant, view},
PolicyRepo: eventstore.PolicyRepo{policy}, PolicyRepo: eventstore.PolicyRepo{policy},
}, nil }, nil

View File

@ -1,6 +1,8 @@
package model package model
import "github.com/caos/zitadel/internal/eventstore/models" import (
"github.com/caos/zitadel/internal/eventstore/models"
)
type PasswordAgePolicy struct { type PasswordAgePolicy struct {
models.ObjectRoot models.ObjectRoot

View File

@ -0,0 +1,197 @@
package model
import (
"testing"
)
func TestCheckPasswordComplexityPolicy(t *testing.T) {
type args struct {
policy *PasswordComplexityPolicy
password string
}
tests := []struct {
name string
args args
hasError bool
}{
{
name: "has minlength ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: false,
HasUppercase: false,
HasSymbol: false,
HasNumber: false,
MinLength: 10,
},
password: "password12",
},
hasError: false,
},
{
name: "has minlength not ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: false,
HasUppercase: false,
HasSymbol: false,
HasNumber: false,
MinLength: 10,
},
password: "password",
},
hasError: true,
},
{
name: "has lowercase ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: true,
HasUppercase: false,
HasSymbol: false,
HasNumber: false,
MinLength: 0,
},
password: "password",
},
hasError: false,
},
{
name: "has lowercase not ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: true,
HasUppercase: false,
HasSymbol: false,
HasNumber: false,
MinLength: 0,
},
password: "PASSWORD",
},
hasError: true,
},
{
name: "has uppercase ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: false,
HasUppercase: true,
HasSymbol: false,
HasNumber: false,
MinLength: 0,
},
password: "PASSWORD",
},
hasError: false,
},
{
name: "has uppercase not ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: false,
HasUppercase: true,
HasSymbol: false,
HasNumber: false,
MinLength: 0,
},
password: "password",
},
hasError: true,
},
{
name: "has symbol ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: false,
HasUppercase: false,
HasSymbol: true,
HasNumber: false,
MinLength: 0,
},
password: "!G$",
},
hasError: false,
},
{
name: "has symbol not ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: false,
HasUppercase: false,
HasSymbol: true,
HasNumber: false,
MinLength: 0,
},
password: "PASSWORD",
},
hasError: true,
},
{
name: "has number ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: false,
HasUppercase: false,
HasSymbol: false,
HasNumber: true,
MinLength: 0,
},
password: "123456",
},
hasError: false,
},
{
name: "has number not ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: false,
HasUppercase: false,
HasSymbol: false,
HasNumber: true,
MinLength: 0,
},
password: "PASSWORD",
},
hasError: true,
},
{
name: "has everything ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: true,
HasUppercase: true,
HasSymbol: true,
HasNumber: true,
MinLength: 10,
},
password: "Password1!",
},
hasError: false,
},
{
name: "has everything not ok",
args: args{
policy: &PasswordComplexityPolicy{
HasLowercase: true,
HasUppercase: true,
HasSymbol: true,
HasNumber: true,
MinLength: 10,
},
password: "password",
},
hasError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.args.policy.Check(tt.args.password)
if !tt.hasError && err != nil {
t.Errorf("should not get err: %v", err)
}
if tt.hasError && err == nil {
t.Errorf("should have error: %v", err)
}
})
}
}

View File

@ -1,6 +1,17 @@
package model package model
import "github.com/caos/zitadel/internal/eventstore/models" import (
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
"regexp"
)
var (
hasStringLowerCase = regexp.MustCompile(`[a-z]`).MatchString
hasStringUpperCase = regexp.MustCompile(`[A-Z]`).MatchString
hasNumber = regexp.MustCompile(`[0-9]`).MatchString
hasSymbol = regexp.MustCompile(`[^A-Za-z0-9]`).MatchString
)
type PasswordComplexityPolicy struct { type PasswordComplexityPolicy struct {
models.ObjectRoot models.ObjectRoot
@ -17,3 +28,26 @@ type PasswordComplexityPolicy struct {
func (p *PasswordComplexityPolicy) IsValid() bool { func (p *PasswordComplexityPolicy) IsValid() bool {
return p.Description != "" return p.Description != ""
} }
func (p *PasswordComplexityPolicy) Check(password string) error {
if p.MinLength != 0 && uint64(len(password)) < p.MinLength {
return caos_errs.ThrowInvalidArgumentf(nil, "MODEL-HuJf6", "Passwordpolicy doesn't match: Minlength %v", p.MinLength)
}
if p.HasLowercase && !hasStringLowerCase(password) {
return caos_errs.ThrowInvalidArgument(nil, "MODEL-co3Xw", "Passwordpolicy doesn't match: HasLowerCase")
}
if p.HasUppercase && !hasStringUpperCase(password) {
return caos_errs.ThrowInvalidArgument(nil, "MODEL-VoaRj", "Passwordpolicy doesn't match: HasUpperCase")
}
if p.HasNumber && !hasNumber(password) {
return caos_errs.ThrowInvalidArgument(nil, "MODEL-ZBv4H", "Passwordpolicy doesn't match: HasNumber")
}
if p.HasSymbol && !hasSymbol(password) {
return caos_errs.ThrowInvalidArgument(nil, "MODEL-ZDLwA", "Passwordpolicy doesn't match: HasSymbol")
}
return nil
}

View File

@ -2,7 +2,9 @@ package model
import ( import (
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
caos_errs "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models"
policy_model "github.com/caos/zitadel/internal/policy/model"
"time" "time"
) )
@ -33,10 +35,16 @@ func (p *Password) IsValid() bool {
return p.AggregateID != "" && p.SecretString != "" return p.AggregateID != "" && p.SecretString != ""
} }
func (p *Password) HashPasswordIfExisting(passwordAlg crypto.HashAlgorithm, onetime bool) error { func (p *Password) HashPasswordIfExisting(policy *policy_model.PasswordComplexityPolicy, passwordAlg crypto.HashAlgorithm, onetime bool) error {
if p.SecretString == "" { if p.SecretString == "" {
return nil return nil
} }
if policy == nil {
return caos_errs.ThrowPreconditionFailed(nil, "MODEL-s8ifS", "Policy should not be nil")
}
if err := policy.Check(p.SecretString); err != nil {
return err
}
secret, err := crypto.Hash([]byte(p.SecretString), passwordAlg) secret, err := crypto.Hash([]byte(p.SecretString), passwordAlg)
if err != nil { if err != nil {
return err return err

View File

@ -1,6 +1,7 @@
package model package model
import ( import (
policy_model "github.com/caos/zitadel/internal/policy/model"
"time" "time"
"github.com/caos/zitadel/internal/crypto" "github.com/caos/zitadel/internal/crypto"
@ -85,9 +86,9 @@ func (u *User) IsOTPReady() bool {
return u.OTP != nil && u.OTP.State == MFASTATE_READY return u.OTP != nil && u.OTP.State == MFASTATE_READY
} }
func (u *User) HashPasswordIfExisting(passwordAlg crypto.HashAlgorithm, onetime bool) error { func (u *User) HashPasswordIfExisting(policy *policy_model.PasswordComplexityPolicy, passwordAlg crypto.HashAlgorithm, onetime bool) error {
if u.Password != nil { if u.Password != nil {
return u.Password.HashPasswordIfExisting(passwordAlg, onetime) return u.Password.HashPasswordIfExisting(policy, passwordAlg, onetime)
} }
return nil return nil
} }

View File

@ -3,6 +3,8 @@ package eventsourcing
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/id" "github.com/caos/zitadel/internal/id"
policy_model "github.com/caos/zitadel/internal/policy/model"
"github.com/pquerna/otp/totp" "github.com/pquerna/otp/totp"
req_model "github.com/caos/zitadel/internal/auth_request/model" req_model "github.com/caos/zitadel/internal/auth_request/model"
@ -87,20 +89,19 @@ func (es *UserEventstore) UserByID(ctx context.Context, id string) (*usr_model.U
return model.UserToModel(user), nil return model.UserToModel(user), nil
} }
func (es *UserEventstore) PrepareCreateUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*model.User, []*es_models.Aggregate, error) { func (es *UserEventstore) PrepareCreateUser(ctx context.Context, user *usr_model.User, policy *policy_model.PasswordComplexityPolicy, resourceOwner string) (*model.User, []*es_models.Aggregate, error) {
user.SetEmailAsUsername() user.SetEmailAsUsername()
if !user.IsValid() { if !user.IsValid() {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "User is invalid") return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "User is invalid")
} }
//TODO: Check Uniqueness
id, err := es.idGenerator.Next() id, err := es.idGenerator.Next()
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
user.AggregateID = id user.AggregateID = id
err = user.HashPasswordIfExisting(es.PasswordAlg, true) err = user.HashPasswordIfExisting(policy, es.PasswordAlg, true)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -122,8 +123,8 @@ func (es *UserEventstore) PrepareCreateUser(ctx context.Context, user *usr_model
return repoUser, createAggregates, err return repoUser, createAggregates, err
} }
func (es *UserEventstore) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) { func (es *UserEventstore) CreateUser(ctx context.Context, user *usr_model.User, policy *policy_model.PasswordComplexityPolicy) (*usr_model.User, error) {
repoUser, aggregates, err := es.PrepareCreateUser(ctx, user, "") repoUser, aggregates, err := es.PrepareCreateUser(ctx, user, policy, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -137,19 +138,18 @@ func (es *UserEventstore) CreateUser(ctx context.Context, user *usr_model.User)
return model.UserToModel(repoUser), nil return model.UserToModel(repoUser), nil
} }
func (es *UserEventstore) PrepareRegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*model.User, []*es_models.Aggregate, error) { func (es *UserEventstore) PrepareRegisterUser(ctx context.Context, user *usr_model.User, policy *policy_model.PasswordComplexityPolicy, resourceOwner string) (*model.User, []*es_models.Aggregate, error) {
user.SetEmailAsUsername() user.SetEmailAsUsername()
if !user.IsValid() || user.Password == nil || user.SecretString == "" { if !user.IsValid() || user.Password == nil || user.SecretString == "" {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "user is invalid") return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "user is invalid")
} }
//TODO: Check Uniqueness
id, err := es.idGenerator.Next() id, err := es.idGenerator.Next()
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
user.AggregateID = id user.AggregateID = id
err = user.HashPasswordIfExisting(es.PasswordAlg, false) err = user.HashPasswordIfExisting(policy, es.PasswordAlg, false)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -165,8 +165,8 @@ func (es *UserEventstore) PrepareRegisterUser(ctx context.Context, user *usr_mod
return repoUser, aggregates, err return repoUser, aggregates, err
} }
func (es *UserEventstore) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) { func (es *UserEventstore) RegisterUser(ctx context.Context, user *usr_model.User, policy *policy_model.PasswordComplexityPolicy, resourceOwner string) (*usr_model.User, error) {
repoUser, createAggregates, err := es.PrepareRegisterUser(ctx, user, resourceOwner) repoUser, createAggregates, err := es.PrepareRegisterUser(ctx, user, policy, resourceOwner)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -380,15 +380,15 @@ func (es *UserEventstore) setPasswordCheckResult(ctx context.Context, user *usr_
return nil return nil
} }
func (es *UserEventstore) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) { func (es *UserEventstore) SetOneTimePassword(ctx context.Context, policy *policy_model.PasswordComplexityPolicy, password *usr_model.Password) (*usr_model.Password, error) {
user, err := es.UserByID(ctx, password.AggregateID) user, err := es.UserByID(ctx, password.AggregateID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return es.changedPassword(ctx, user, password.SecretString, true) return es.changedPassword(ctx, user, policy, password.SecretString, true)
} }
func (es *UserEventstore) SetPassword(ctx context.Context, userID, code, password string) error { func (es *UserEventstore) SetPassword(ctx context.Context, policy *policy_model.PasswordComplexityPolicy, userID, code, password string) error {
user, err := es.UserByID(ctx, userID) user, err := es.UserByID(ctx, userID)
if err != nil { if err != nil {
return err return err
@ -399,11 +399,11 @@ func (es *UserEventstore) SetPassword(ctx context.Context, userID, code, passwor
if err := crypto.VerifyCode(user.PasswordCode.CreationDate, user.PasswordCode.Expiry, user.PasswordCode.Code, code, es.PasswordVerificationCode); err != nil { if err := crypto.VerifyCode(user.PasswordCode.CreationDate, user.PasswordCode.Expiry, user.PasswordCode.Code, code, es.PasswordVerificationCode); err != nil {
return caos_errs.ThrowPreconditionFailed(err, "EVENT-sd6DF", "code invalid") return caos_errs.ThrowPreconditionFailed(err, "EVENT-sd6DF", "code invalid")
} }
_, err = es.changedPassword(ctx, user, password, false) _, err = es.changedPassword(ctx, user, policy, password, false)
return err return err
} }
func (es *UserEventstore) ChangePassword(ctx context.Context, userID, old, new string) (*usr_model.Password, error) { func (es *UserEventstore) ChangePassword(ctx context.Context, policy *policy_model.PasswordComplexityPolicy, userID, old, new string) (*usr_model.Password, error) {
user, err := es.UserByID(ctx, userID) user, err := es.UserByID(ctx, userID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -414,16 +414,16 @@ func (es *UserEventstore) ChangePassword(ctx context.Context, userID, old, new s
if err := crypto.CompareHash(user.Password.SecretCrypto, []byte(old), es.PasswordAlg); err != nil { if err := crypto.CompareHash(user.Password.SecretCrypto, []byte(old), es.PasswordAlg); err != nil {
return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-s56a3", "invalid password") return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-s56a3", "invalid password")
} }
return es.changedPassword(ctx, user, new, false) return es.changedPassword(ctx, user, policy, new, false)
} }
func (es *UserEventstore) changedPassword(ctx context.Context, user *usr_model.User, password string, onetime bool) (*usr_model.Password, error) { func (es *UserEventstore) changedPassword(ctx context.Context, user *usr_model.User, policy *policy_model.PasswordComplexityPolicy, password string, onetime bool) (*usr_model.Password, error) {
//TODO: check password policy pw := &usr_model.Password{SecretString: password}
secret, err := crypto.Hash([]byte(password), es.PasswordAlg) err := pw.HashPasswordIfExisting(policy, es.PasswordAlg, onetime)
if err != nil { if err != nil {
return nil, err return nil, err
} }
repoPassword := &model.Password{Secret: secret, ChangeRequired: onetime} repoPassword := model.PasswordFromModel(pw)
repoUser := model.UserFromModel(user) repoUser := model.UserFromModel(user)
agg := PasswordChangeAggregate(es.AggregateCreator(), repoUser, repoPassword) agg := PasswordChangeAggregate(es.AggregateCreator(), repoUser, repoPassword)
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, agg) err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, agg)

View File

@ -2,6 +2,7 @@ package eventsourcing
import ( import (
"context" "context"
policy_model "github.com/caos/zitadel/internal/policy/model"
"net" "net"
"testing" "testing"
"time" "time"
@ -83,9 +84,10 @@ func TestUserByID(t *testing.T) {
func TestCreateUser(t *testing.T) { func TestCreateUser(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
type args struct { type args struct {
es *UserEventstore es *UserEventstore
ctx context.Context ctx context.Context
user *model.User user *model.User
policy *policy_model.PasswordComplexityPolicy
} }
type res struct { type res struct {
user *model.User user *model.User
@ -113,6 +115,7 @@ func TestCreateUser(t *testing.T) {
IsEmailVerified: true, IsEmailVerified: true,
}, },
}, },
policy: &policy_model.PasswordComplexityPolicy{},
}, },
res: res{ res: res{
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1}, user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1},
@ -143,6 +146,7 @@ func TestCreateUser(t *testing.T) {
IsEmailVerified: true, IsEmailVerified: true,
}, },
}, },
policy: &policy_model.PasswordComplexityPolicy{},
}, },
res: res{ res: res{
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1}, user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1},
@ -178,6 +182,7 @@ func TestCreateUser(t *testing.T) {
IsPhoneVerified: true, IsPhoneVerified: true,
}, },
}, },
policy: &policy_model.PasswordComplexityPolicy{},
}, },
res: res{ res: res{
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1}, user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1},
@ -214,6 +219,7 @@ func TestCreateUser(t *testing.T) {
IsEmailVerified: true, IsEmailVerified: true,
}, },
}, },
policy: &policy_model.PasswordComplexityPolicy{},
}, },
res: res{ res: res{
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1}, user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1},
@ -231,6 +237,18 @@ func TestCreateUser(t *testing.T) {
}, },
{ {
name: "create user invalid", name: "create user invalid",
args: args{
es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
policy: &policy_model.PasswordComplexityPolicy{},
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "create user policy nil",
args: args{ args: args{
es: GetMockManipulateUser(ctrl), es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
@ -243,7 +261,7 @@ func TestCreateUser(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.CreateUser(tt.args.ctx, tt.args.user) result, err := tt.args.es.CreateUser(tt.args.ctx, tt.args.user, tt.args.policy)
if tt.res.errFunc == nil && result.AggregateID == "" { if tt.res.errFunc == nil && result.AggregateID == "" {
t.Errorf("result has no id") t.Errorf("result has no id")
@ -275,6 +293,7 @@ func TestRegisterUser(t *testing.T) {
ctx context.Context ctx context.Context
user *model.User user *model.User
resourceOwner string resourceOwner string
policy *policy_model.PasswordComplexityPolicy
} }
type res struct { type res struct {
user *model.User user *model.User
@ -304,6 +323,7 @@ func TestRegisterUser(t *testing.T) {
SecretString: "Password", SecretString: "Password",
}, },
}, },
policy: &policy_model.PasswordComplexityPolicy{},
resourceOwner: "ResourceOwner", resourceOwner: "ResourceOwner",
}, },
res: res{ res: res{
@ -336,6 +356,7 @@ func TestRegisterUser(t *testing.T) {
SecretString: "Password", SecretString: "Password",
}, },
}, },
policy: &policy_model.PasswordComplexityPolicy{},
resourceOwner: "ResourceOwner", resourceOwner: "ResourceOwner",
}, },
res: res{ res: res{
@ -357,6 +378,7 @@ func TestRegisterUser(t *testing.T) {
es: GetMockManipulateUser(ctrl), es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1}}, user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1}},
policy: &policy_model.PasswordComplexityPolicy{},
resourceOwner: "ResourceOwner", resourceOwner: "ResourceOwner",
}, },
res: res{ res: res{
@ -378,6 +400,7 @@ func TestRegisterUser(t *testing.T) {
EmailAddress: "EmailAddress", EmailAddress: "EmailAddress",
}, },
}, },
policy: &policy_model.PasswordComplexityPolicy{},
resourceOwner: "ResourceOwner", resourceOwner: "ResourceOwner",
}, },
res: res{ res: res{
@ -386,6 +409,27 @@ func TestRegisterUser(t *testing.T) {
}, },
{ {
name: "no resourceowner", name: "no resourceowner",
args: args{
es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1},
Profile: &model.Profile{
UserName: "EmailAddress",
FirstName: "FirstName",
LastName: "LastName",
},
Email: &model.Email{
EmailAddress: "EmailAddress",
},
},
policy: &policy_model.PasswordComplexityPolicy{},
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "no policy",
args: args{ args: args{
es: GetMockManipulateUser(ctrl), es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
@ -407,7 +451,7 @@ func TestRegisterUser(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.RegisterUser(tt.args.ctx, tt.args.user, tt.args.resourceOwner) result, err := tt.args.es.RegisterUser(tt.args.ctx, tt.args.user, tt.args.policy, tt.args.resourceOwner)
if tt.res.errFunc == nil && result.AggregateID == "" { if tt.res.errFunc == nil && result.AggregateID == "" {
t.Errorf("result has no id") t.Errorf("result has no id")
@ -1029,6 +1073,7 @@ func TestSetOneTimePassword(t *testing.T) {
type args struct { type args struct {
es *UserEventstore es *UserEventstore
ctx context.Context ctx context.Context
policy *policy_model.PasswordComplexityPolicy
password *model.Password password *model.Password
} }
type res struct { type res struct {
@ -1045,6 +1090,7 @@ func TestSetOneTimePassword(t *testing.T) {
args: args{ args: args{
es: GetMockManipulateUserWithPasswordCodeGen(ctrl, repo_model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}}), es: GetMockManipulateUserWithPasswordCodeGen(ctrl, repo_model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}}),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SecretString: "Password"}, password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SecretString: "Password"},
}, },
res: res{ res: res{
@ -1056,6 +1102,7 @@ func TestSetOneTimePassword(t *testing.T) {
args: args{ args: args{
es: GetMockManipulateUser(ctrl), es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: ""}, SecretString: "Password"}, password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: ""}, SecretString: "Password"},
}, },
res: res{ res: res{
@ -1067,6 +1114,7 @@ func TestSetOneTimePassword(t *testing.T) {
args: args{ args: args{
es: GetMockManipulateUserNoEvents(ctrl), es: GetMockManipulateUserNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SecretString: "Password"}, password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SecretString: "Password"},
}, },
res: res{ res: res{
@ -1076,7 +1124,7 @@ func TestSetOneTimePassword(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.SetOneTimePassword(tt.args.ctx, tt.args.password) result, err := tt.args.es.SetOneTimePassword(tt.args.ctx, tt.args.policy, tt.args.password)
if tt.res.errFunc == nil && result.AggregateID == "" { if tt.res.errFunc == nil && result.AggregateID == "" {
t.Errorf("result has no id") t.Errorf("result has no id")
@ -1226,6 +1274,7 @@ func TestSetPassword(t *testing.T) {
type args struct { type args struct {
es *UserEventstore es *UserEventstore
ctx context.Context ctx context.Context
policy *policy_model.PasswordComplexityPolicy
userID string userID string
code string code string
password string password string
@ -1253,6 +1302,7 @@ func TestSetPassword(t *testing.T) {
}, },
), ),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
userID: "userID", userID: "userID",
code: "code", code: "code",
password: "password", password: "password",
@ -1264,6 +1314,7 @@ func TestSetPassword(t *testing.T) {
args: args{ args: args{
es: GetMockManipulateUser(ctrl), es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
userID: "", userID: "",
code: "code", code: "code",
password: "password", password: "password",
@ -1277,6 +1328,7 @@ func TestSetPassword(t *testing.T) {
args: args{ args: args{
es: GetMockManipulateUserNoEvents(ctrl), es: GetMockManipulateUserNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
userID: "userID", userID: "userID",
code: "code", code: "code",
password: "password", password: "password",
@ -1294,6 +1346,7 @@ func TestSetPassword(t *testing.T) {
}, },
), ),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
userID: "userID", userID: "userID",
code: "code", code: "code",
password: "password", password: "password",
@ -1317,6 +1370,7 @@ func TestSetPassword(t *testing.T) {
}, },
), ),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
userID: "userID", userID: "userID",
code: "code", code: "code",
password: "password", password: "password",
@ -1328,7 +1382,7 @@ func TestSetPassword(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
err := tt.args.es.SetPassword(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.password) err := tt.args.es.SetPassword(tt.args.ctx, tt.args.policy, tt.args.userID, tt.args.code, tt.args.password)
if tt.res.errFunc == nil && err != nil { if tt.res.errFunc == nil && err != nil {
t.Errorf("result has error: %v", err) t.Errorf("result has error: %v", err)
@ -1345,6 +1399,7 @@ func TestChangePassword(t *testing.T) {
type args struct { type args struct {
es *UserEventstore es *UserEventstore
ctx context.Context ctx context.Context
policy *policy_model.PasswordComplexityPolicy
userID string userID string
old string old string
new string new string
@ -1372,6 +1427,7 @@ func TestChangePassword(t *testing.T) {
}, },
), ),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
userID: "userID", userID: "userID",
old: "old", old: "old",
new: "new", new: "new",
@ -1385,6 +1441,7 @@ func TestChangePassword(t *testing.T) {
args: args{ args: args{
es: GetMockManipulateUser(ctrl), es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
userID: "", userID: "",
old: "old", old: "old",
new: "new", new: "new",
@ -1398,6 +1455,7 @@ func TestChangePassword(t *testing.T) {
args: args{ args: args{
es: GetMockManipulateUserNoEvents(ctrl), es: GetMockManipulateUserNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
userID: "userID", userID: "userID",
old: "old", old: "old",
new: "new", new: "new",
@ -1415,6 +1473,7 @@ func TestChangePassword(t *testing.T) {
}, },
), ),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
userID: "userID", userID: "userID",
old: "old", old: "old",
new: "new", new: "new",
@ -1437,6 +1496,7 @@ func TestChangePassword(t *testing.T) {
}, },
), ),
ctx: auth.NewMockContext("orgID", "userID"), ctx: auth.NewMockContext("orgID", "userID"),
policy: &policy_model.PasswordComplexityPolicy{},
userID: "userID", userID: "userID",
old: "old", old: "old",
new: "new", new: "new",
@ -1445,10 +1505,32 @@ func TestChangePassword(t *testing.T) {
errFunc: caos_errs.IsErrorInvalidArgument, errFunc: caos_errs.IsErrorInvalidArgument,
}, },
}, },
{
name: "no policy",
args: args{
es: GetMockManipulateUserWithPasswordAndEmailCodeGen(ctrl,
repo_model.User{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
Password: &repo_model.Password{Secret: &crypto.CryptoValue{
CryptoType: crypto.TypeHash,
Algorithm: "hash",
Crypted: []byte("old"),
}},
},
),
ctx: auth.NewMockContext("orgID", "userID"),
userID: "userID",
old: "old",
new: "new",
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.ChangePassword(tt.args.ctx, tt.args.userID, tt.args.old, tt.args.new) result, err := tt.args.es.ChangePassword(tt.args.ctx, tt.args.policy, tt.args.userID, tt.args.old, tt.args.new)
if tt.res.errFunc == nil && result.AggregateID == "" { if tt.res.errFunc == nil && result.AggregateID == "" {
t.Errorf("result has no id") t.Errorf("result has no id")