mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 04:07:31 +00:00
feat: Login, OP Support and Auth Queries (#177)
* fix: change oidc config * fix: change oidc config secret * begin models * begin repo * fix: implement grpc app funcs * fix: add application requests * fix: converter * fix: converter * fix: converter and generate clientid * fix: tests * feat: project grant aggregate * feat: project grant * fix: project grant check if role existing * fix: project grant requests * fix: project grant fixes * fix: project grant member model * fix: project grant member aggregate * fix: project grant member eventstore * fix: project grant member requests * feat: user model * begin repo * repo models and more * feat: user command side * lots of functions * user command side * profile requests * commit before rebase on user * save * local config with gopass and more * begin new auth command (user centric) * Update internal/user/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/address.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/address.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/email.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/email.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/email.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/mfa.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/mfa.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/password.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/password.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/password.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/phone.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/phone.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/phone.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/usergrant/repository/eventsourcing/model/user_grant.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/usergrant/repository/eventsourcing/model/user_grant.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/usergrant/repository/eventsourcing/user_grant.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/user_test.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * Update internal/user/repository/eventsourcing/eventstore_mock_test.go Co-Authored-By: Livio Amstutz <livio.a@gmail.com> * changes from mr review * save files into basedir * changes from mr review * changes from mr review * move to auth request * Update internal/usergrant/repository/eventsourcing/cache.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * Update internal/usergrant/repository/eventsourcing/cache.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * changes requested on mr * fix generate codes * fix return if no events * password code * email verification step * more steps * lot of mfa * begin tests * more next steps * auth api * auth api (user) * auth api (user) * auth api (user) * differ requests * merge * tests * fix compilation error * mock for id generator * Update internal/user/repository/eventsourcing/model/password.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * Update internal/user/repository/eventsourcing/model/user.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * requests of mr * check email * begin separation of command and query * otp * change packages * some cleanup and fixes * tests for auth request / next steps * add VerificationLifetimes to config and make it run * tests * fix code challenge validation * cleanup * fix merge * begin view * repackaging tests and configs * fix startup config for auth * add migration * add PromptSelectAccount * fix copy / paste * remove user_agent files * fixes * fix sequences in user_session * token commands * token queries and signout * fix * fix set password test * add token handler and table * handle session init * add session state * add user view test cases * change VerifyMyMfaOTP * some fixes * fix user repo in auth api * cleanup * add user session view test * fix merge * begin oidc * user agent and more * config * keys * key command and query * add login statics * key handler * start login * login handlers * lot of fixes * merge oidc * add missing exports * add missing exports * fix some bugs * authrequestid in htmls * getrequest * update auth request * fix userid check * add username to authrequest * fix user session and auth request handling * fix UserSessionsByAgentID * fix auth request tests * fix user session on UserPasswordChanged and MfaOtpRemoved * fix MfaTypesSetupPossible * handle mfa * fill username * auth request query checks new events * fix userSessionByIDs * fix tokens * fix userSessionByIDs test * add user selection * init code * user code creation date * add init user step * add verification failed types * add verification failures * verify init code * user init code handle * user init code handle * fix userSessionByIDs * update logging * user agent cookie * browserinfo from request * add DeleteAuthRequest * add static login files to binary * add login statik to build * move generate to separate file and remove statik.go files * remove static dirs from startup.yaml * generate into separate namespaces * merge master * auth request code * auth request type mapping * fix keys * improve tokens * improve register and basic styling * fix ailerons font * improve password reset * add audience to token * all oidc apps as audience * fix test nextStep * fix email texts * remove "not set" * lot of style changes * improve copy to clipboard * fix footer * add cookie handler * remove placeholders * fix compilation after merge * fix auth config * remove comments * typo * use new secrets store * change default pws to match default policy * fixes * add todo * enable login * fix db name * Auth queries (#179) * my usersession * org structure/ auth handlers * working user grant spooler * auth internal user grants * search my project orgs * remove permissions file * my zitadel permissions * my zitadel permissions * remove unused code * authz * app searches in view * token verification * fix user grant load * fix tests * fix tests * read configs * remove unused const * remove todos * env variables * app_name * working authz * search projects * global resourceowner * Update internal/api/auth/permissions.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * Update internal/api/auth/permissions.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * model2 rename * at least it works * check token expiry * search my user grants * remove token table from authz Co-authored-by: Livio Amstutz <livio.a@gmail.com> * fix test * fix ports and enable console Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com> Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Silvan <silvan.reusser@gmail.com>
This commit is contained in:
@@ -9,7 +9,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
//TODO: How do we get region?
|
||||
defaultRegion = "CH"
|
||||
)
|
||||
|
||||
|
@@ -8,18 +8,19 @@ import (
|
||||
)
|
||||
|
||||
type UserSessionView struct {
|
||||
ID string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
State req_model.UserSessionState
|
||||
ResourceOwner string
|
||||
UserAgentID string
|
||||
UserID string
|
||||
UserName string
|
||||
PasswordVerification time.Time
|
||||
MfaSoftwareVerification time.Time
|
||||
MfaHardwareVerification time.Time
|
||||
Sequence uint64
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
State req_model.UserSessionState
|
||||
ResourceOwner string
|
||||
UserAgentID string
|
||||
UserID string
|
||||
UserName string
|
||||
PasswordVerification time.Time
|
||||
MfaSoftwareVerification time.Time
|
||||
MfaSoftwareVerificationType req_model.MfaType
|
||||
MfaHardwareVerification time.Time
|
||||
MfaHardwareVerificationType req_model.MfaType
|
||||
Sequence uint64
|
||||
}
|
||||
|
||||
type UserSessionSearchRequest struct {
|
||||
@@ -34,7 +35,6 @@ type UserSessionSearchKey int32
|
||||
|
||||
const (
|
||||
USERSESSIONSEARCHKEY_UNSPECIFIED UserSessionSearchKey = iota
|
||||
USERSESSIONSEARCHKEY_SESSION_ID
|
||||
USERSESSIONSEARCHKEY_USER_AGENT_ID
|
||||
USERSESSIONSEARCHKEY_USER_ID
|
||||
USERSESSIONSEARCHKEY_STATE
|
||||
|
@@ -36,6 +36,7 @@ type UserView struct {
|
||||
OTPState MfaState
|
||||
MfaMaxSetUp req_model.MfaLevel
|
||||
MfaInitSkipped time.Time
|
||||
InitRequired bool
|
||||
Sequence uint64
|
||||
}
|
||||
|
||||
@@ -88,6 +89,8 @@ func (r *UserSearchRequest) AppendMyOrgQuery(orgID string) {
|
||||
func (u *UserView) MfaTypesSetupPossible(level req_model.MfaLevel) []req_model.MfaType {
|
||||
types := make([]req_model.MfaType, 0)
|
||||
switch level {
|
||||
default:
|
||||
fallthrough
|
||||
case req_model.MfaLevelSoftware:
|
||||
if u.OTPState != MFASTATE_READY {
|
||||
types = append(types, req_model.MfaTypeOTP)
|
||||
|
@@ -89,6 +89,14 @@ func (es *UserEventstore) UserByID(ctx context.Context, id string) (*usr_model.U
|
||||
return model.UserToModel(user), nil
|
||||
}
|
||||
|
||||
func (es *UserEventstore) UserEventsByID(ctx context.Context, id string, sequence uint64) ([]*es_models.Event, error) {
|
||||
query, err := UserByIDQuery(id, sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return es.FilterEvents(ctx, query)
|
||||
}
|
||||
|
||||
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()
|
||||
if !user.IsValid() {
|
||||
@@ -317,6 +325,39 @@ func (es *UserEventstore) InitCodeSent(ctx context.Context, userID string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es *UserEventstore) VerifyInitCode(ctx context.Context, policy *policy_model.PasswordComplexityPolicy, userID, verificationCode, password string) error {
|
||||
if userID == "" || verificationCode == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-lo9fd", "userId or Code empty")
|
||||
}
|
||||
pw := &usr_model.Password{SecretString: password}
|
||||
err := pw.HashPasswordIfExisting(policy, es.PasswordAlg, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
existing, err := es.UserByID(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existing.InitCode == nil {
|
||||
return caos_errs.ThrowNotFound(nil, "EVENT-spo9W", "code not found")
|
||||
}
|
||||
repoPassword := model.PasswordFromModel(pw)
|
||||
repoExisting := model.UserFromModel(existing)
|
||||
var updateAggregate func(ctx context.Context) (*es_models.Aggregate, error)
|
||||
if err := crypto.VerifyCode(existing.InitCode.CreationDate, existing.InitCode.Expiry, existing.InitCode.Code, verificationCode, es.InitializeUserCode); err != nil {
|
||||
updateAggregate = InitCodeCheckFailedAggregate(es.AggregateCreator(), repoExisting)
|
||||
} else {
|
||||
updateAggregate = InitCodeVerifiedAggregate(es.AggregateCreator(), repoExisting, repoPassword)
|
||||
}
|
||||
err = es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, updateAggregate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
es.userCache.cacheUser(repoExisting)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es *UserEventstore) SkipMfaInit(ctx context.Context, userID string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-dic8s", "userID missing")
|
||||
@@ -572,17 +613,23 @@ func (es *UserEventstore) VerifyEmail(ctx context.Context, userID, verificationC
|
||||
if existing.EmailCode == nil {
|
||||
return caos_errs.ThrowNotFound(nil, "EVENT-lso9w", "code not found")
|
||||
}
|
||||
if err := crypto.VerifyCode(existing.EmailCode.CreationDate, existing.EmailCode.Expiry, existing.EmailCode.Code, verificationCode, es.EmailVerificationCode); err != nil {
|
||||
|
||||
err = crypto.VerifyCode(existing.EmailCode.CreationDate, existing.EmailCode.Expiry, existing.EmailCode.Code, verificationCode, es.EmailVerificationCode)
|
||||
if err == nil {
|
||||
return es.setEmailVerifyResult(ctx, existing, EmailVerifiedAggregate)
|
||||
}
|
||||
if err := es.setEmailVerifyResult(ctx, existing, EmailVerificationFailedAggregate); err != nil {
|
||||
return err
|
||||
}
|
||||
return caos_errs.ThrowInvalidArgument(err, "EVENT-dtGaa", "invalid code")
|
||||
}
|
||||
|
||||
func (es *UserEventstore) setEmailVerifyResult(ctx context.Context, existing *usr_model.User, check func(aggCreator *es_models.AggregateCreator, existing *model.User) es_sdk.AggregateFunc) error {
|
||||
repoExisting := model.UserFromModel(existing)
|
||||
updateAggregate := EmailVerifiedAggregate(es.AggregateCreator(), repoExisting)
|
||||
err = es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, updateAggregate)
|
||||
err := es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, check(es.AggregateCreator(), repoExisting))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
es.userCache.cacheUser(repoExisting)
|
||||
return nil
|
||||
}
|
||||
@@ -693,17 +740,23 @@ func (es *UserEventstore) VerifyPhone(ctx context.Context, userID, verificationC
|
||||
if existing.PhoneCode == nil {
|
||||
return caos_errs.ThrowNotFound(nil, "EVENT-slp0s", "code not found")
|
||||
}
|
||||
if err := crypto.VerifyCode(existing.PhoneCode.CreationDate, existing.PhoneCode.Expiry, existing.PhoneCode.Code, verificationCode, es.PhoneVerificationCode); err != nil {
|
||||
|
||||
err = crypto.VerifyCode(existing.PhoneCode.CreationDate, existing.PhoneCode.Expiry, existing.PhoneCode.Code, verificationCode, es.PhoneVerificationCode)
|
||||
if err == nil {
|
||||
return es.setPhoneVerifyResult(ctx, existing, PhoneVerifiedAggregate)
|
||||
}
|
||||
if err := es.setPhoneVerifyResult(ctx, existing, PhoneVerificationFailedAggregate); err != nil {
|
||||
return err
|
||||
}
|
||||
return caos_errs.ThrowInvalidArgument(err, "EVENT-dsf4G", "invalid code")
|
||||
}
|
||||
|
||||
func (es *UserEventstore) setPhoneVerifyResult(ctx context.Context, existing *usr_model.User, check func(aggCreator *es_models.AggregateCreator, existing *model.User) es_sdk.AggregateFunc) error {
|
||||
repoExisting := model.UserFromModel(existing)
|
||||
updateAggregate := PhoneVerifiedAggregate(es.AggregateCreator(), repoExisting)
|
||||
err = es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, updateAggregate)
|
||||
err := es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, check(es.AggregateCreator(), repoExisting))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
es.userCache.cacheUser(repoExisting)
|
||||
return nil
|
||||
}
|
||||
|
@@ -246,13 +246,13 @@ func GetMockManipulateLockedUser(ctrl *gomock.Controller) *UserEventstore {
|
||||
return GetMockedEventstore(ctrl, mockEs)
|
||||
}
|
||||
|
||||
func GetMockManipulateUserWithInitCode(ctrl *gomock.Controller) *UserEventstore {
|
||||
user := model.User{
|
||||
Profile: &model.Profile{
|
||||
UserName: "UserName",
|
||||
},
|
||||
}
|
||||
code := model.InitUserCode{Expiry: time.Hour * 30}
|
||||
func GetMockManipulateUserWithInitCode(ctrl *gomock.Controller, user model.User) *UserEventstore {
|
||||
code := model.InitUserCode{Code: &crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("code"),
|
||||
}}
|
||||
dataUser, _ := json.Marshal(user)
|
||||
dataCode, _ := json.Marshal(code)
|
||||
events := []*es_models.Event{
|
||||
@@ -263,7 +263,7 @@ func GetMockManipulateUserWithInitCode(ctrl *gomock.Controller) *UserEventstore
|
||||
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
||||
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
|
||||
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
|
||||
return GetMockedEventstore(ctrl, mockEs)
|
||||
return GetMockedEventstoreWithPw(ctrl, mockEs, true, false, false, true)
|
||||
}
|
||||
|
||||
func GetMockManipulateUserWithEmailCode(ctrl *gomock.Controller) *UserEventstore {
|
||||
@@ -362,6 +362,7 @@ func GetMockManipulateUserVerifiedPhone(ctrl *gomock.Controller) *UserEventstore
|
||||
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
|
||||
return GetMockedEventstore(ctrl, mockEs)
|
||||
}
|
||||
|
||||
func GetMockManipulateUserFull(ctrl *gomock.Controller) *UserEventstore {
|
||||
user := model.User{
|
||||
Profile: &model.Profile{
|
||||
@@ -446,3 +447,12 @@ func GetMockManipulateUserNoEvents(ctrl *gomock.Controller) *UserEventstore {
|
||||
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
|
||||
return GetMockedEventstore(ctrl, mockEs)
|
||||
}
|
||||
|
||||
func GetMockManipulateUserNoEventsWithPw(ctrl *gomock.Controller) *UserEventstore {
|
||||
events := []*es_models.Event{}
|
||||
mockEs := mock.NewMockEventstore(ctrl)
|
||||
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
||||
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
|
||||
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
|
||||
return GetMockedEventstoreWithPw(ctrl, mockEs, false, false, false, true)
|
||||
}
|
||||
|
@@ -753,7 +753,13 @@ func TestGetInitCodeByID(t *testing.T) {
|
||||
{
|
||||
name: "get by id, ok",
|
||||
args: args{
|
||||
es: GetMockManipulateUserWithInitCode(ctrl),
|
||||
es: GetMockManipulateUserWithInitCode(ctrl,
|
||||
repo_model.User{
|
||||
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
Profile: &repo_model.Profile{
|
||||
UserName: "UserName",
|
||||
},
|
||||
}),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
|
||||
},
|
||||
@@ -791,8 +797,8 @@ func TestGetInitCodeByID(t *testing.T) {
|
||||
if tt.res.errFunc == nil && result.AggregateID == "" {
|
||||
t.Errorf("result has no id")
|
||||
}
|
||||
if tt.res.errFunc == nil && result.Expiry != tt.res.code.Expiry {
|
||||
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.code.Expiry, result.Expiry)
|
||||
if tt.res.errFunc == nil && result == nil {
|
||||
t.Error("got wrong result code should not be nil", result)
|
||||
}
|
||||
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
@@ -858,8 +864,8 @@ func TestCreateInitCode(t *testing.T) {
|
||||
if tt.res.errFunc == nil && result.AggregateID == "" {
|
||||
t.Errorf("result has no id")
|
||||
}
|
||||
if tt.res.errFunc == nil && result.Expiry != tt.res.code.Expiry {
|
||||
t.Errorf("got wrong result expiry: expected: %v, actual: %v ", tt.res.code.Expiry, result.Expiry)
|
||||
if tt.res.errFunc == nil && result == nil {
|
||||
t.Errorf("got wrong result code is nil")
|
||||
}
|
||||
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
@@ -929,6 +935,135 @@ func TestInitCodeSent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitCodeVerify(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
type args struct {
|
||||
es *UserEventstore
|
||||
ctx context.Context
|
||||
policy *policy_model.PasswordComplexityPolicy
|
||||
userID string
|
||||
verifyCode string
|
||||
password string
|
||||
}
|
||||
type res struct {
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "verify init code, no pw",
|
||||
args: args{
|
||||
es: GetMockManipulateUserWithInitCode(ctrl,
|
||||
repo_model.User{
|
||||
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
Email: &repo_model.Email{
|
||||
EmailAddress: "EmailAddress",
|
||||
},
|
||||
},
|
||||
),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
policy: &policy_model.PasswordComplexityPolicy{},
|
||||
verifyCode: "code",
|
||||
userID: "userID",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "verify init code, pw",
|
||||
args: args{
|
||||
es: GetMockManipulateUserWithInitCode(ctrl,
|
||||
repo_model.User{
|
||||
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
Email: &repo_model.Email{
|
||||
EmailAddress: "EmailAddress",
|
||||
IsEmailVerified: true,
|
||||
},
|
||||
},
|
||||
),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
policy: &policy_model.PasswordComplexityPolicy{},
|
||||
userID: "userID",
|
||||
verifyCode: "code",
|
||||
password: "password",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "verify init code, email and pw",
|
||||
args: args{
|
||||
es: GetMockManipulateUserWithInitCode(ctrl,
|
||||
repo_model.User{
|
||||
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
|
||||
Email: &repo_model.Email{
|
||||
EmailAddress: "EmailAddress",
|
||||
},
|
||||
},
|
||||
),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
policy: &policy_model.PasswordComplexityPolicy{},
|
||||
userID: "userID",
|
||||
verifyCode: "code",
|
||||
password: "password",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty userid",
|
||||
args: args{
|
||||
es: GetMockManipulateUser(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
policy: &policy_model.PasswordComplexityPolicy{},
|
||||
userID: "",
|
||||
verifyCode: "code",
|
||||
password: "password",
|
||||
},
|
||||
res: res{
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password policy not matched",
|
||||
args: args{
|
||||
es: GetMockManipulateUser(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
policy: &policy_model.PasswordComplexityPolicy{HasNumber: true},
|
||||
userID: "userID",
|
||||
verifyCode: "code",
|
||||
password: "password",
|
||||
},
|
||||
res: res{
|
||||
errFunc: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existing user not found",
|
||||
args: args{
|
||||
es: GetMockManipulateUserNoEventsWithPw(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
policy: &policy_model.PasswordComplexityPolicy{},
|
||||
userID: "userID",
|
||||
password: "password",
|
||||
verifyCode: "code",
|
||||
},
|
||||
res: res{
|
||||
errFunc: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.args.es.VerifyInitCode(tt.args.ctx, tt.args.policy, tt.args.userID, tt.args.verifyCode, tt.args.password)
|
||||
|
||||
if tt.res.errFunc == nil && err != nil {
|
||||
t.Errorf("should not have err: %v", err)
|
||||
}
|
||||
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkipMfaInit(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
type args struct {
|
||||
@@ -1980,12 +2115,24 @@ func TestVerifyEmail(t *testing.T) {
|
||||
},
|
||||
res: res{},
|
||||
},
|
||||
{
|
||||
name: "verify email code wrong",
|
||||
args: args{
|
||||
es: GetMockManipulateUserWithEmailCode(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
userID: "AggregateID",
|
||||
code: "wrong",
|
||||
},
|
||||
res: res{
|
||||
errFunc: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty userid",
|
||||
args: args{
|
||||
es: GetMockManipulateUser(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
code: "Code",
|
||||
code: "code",
|
||||
},
|
||||
res: res{
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
@@ -2008,7 +2155,7 @@ func TestVerifyEmail(t *testing.T) {
|
||||
es: GetMockManipulateUserNoEvents(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
userID: "AggregateID",
|
||||
code: "Code",
|
||||
code: "code",
|
||||
},
|
||||
res: res{
|
||||
errFunc: caos_errs.IsNotFound,
|
||||
@@ -2346,6 +2493,18 @@ func TestVerifyPhone(t *testing.T) {
|
||||
},
|
||||
res: res{},
|
||||
},
|
||||
{
|
||||
name: "verify code wrong",
|
||||
args: args{
|
||||
es: GetMockManipulateUserWithPhoneCode(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
userID: "AggregateID",
|
||||
code: "wrong",
|
||||
},
|
||||
res: res{
|
||||
errFunc: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty userid",
|
||||
args: args{
|
||||
|
@@ -94,6 +94,7 @@ func (a *Email) setData(event *es_models.Event) error {
|
||||
|
||||
func (a *EmailCode) SetData(event *es_models.Event) error {
|
||||
a.ObjectRoot.AppendEvent(event)
|
||||
a.CreationDate = event.CreationDate
|
||||
if err := json.Unmarshal(event.Data, a); err != nil {
|
||||
logging.Log("EVEN-lo9s").WithError(err).Error("could not unmarshal event data")
|
||||
return caos_errs.ThrowInternal(err, "MODEL-s8uws", "could not unmarshal event")
|
||||
|
@@ -74,9 +74,10 @@ func (pw *Password) setData(event *es_models.Event) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PasswordCode) SetData(event *es_models.Event) error {
|
||||
a.ObjectRoot.AppendEvent(event)
|
||||
if err := json.Unmarshal(event.Data, a); err != nil {
|
||||
func (c *PasswordCode) SetData(event *es_models.Event) error {
|
||||
c.ObjectRoot.AppendEvent(event)
|
||||
c.CreationDate = event.CreationDate
|
||||
if err := json.Unmarshal(event.Data, c); err != nil {
|
||||
logging.Log("EVEN-lo0y2").WithError(err).Error("could not unmarshal event data")
|
||||
return caos_errs.ThrowInternal(err, "MODEL-q21dr", "could not unmarshal event")
|
||||
}
|
||||
|
@@ -90,9 +90,10 @@ func (p *Phone) setData(event *es_models.Event) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PhoneCode) SetData(event *es_models.Event) error {
|
||||
a.ObjectRoot.AppendEvent(event)
|
||||
if err := json.Unmarshal(event.Data, a); err != nil {
|
||||
func (c *PhoneCode) SetData(event *es_models.Event) error {
|
||||
c.ObjectRoot.AppendEvent(event)
|
||||
c.CreationDate = event.CreationDate
|
||||
if err := json.Unmarshal(event.Data, c); err != nil {
|
||||
logging.Log("EVEN-sk8ws").WithError(err).Error("could not unmarshal event data")
|
||||
return caos_errs.ThrowInternal(err, "MODEL-7hdj3", "could not unmarshal event")
|
||||
}
|
||||
|
@@ -7,10 +7,12 @@ const (
|
||||
UserUserNameAggregate models.AggregateType = "user.username"
|
||||
UserEmailAggregate models.AggregateType = "user.email"
|
||||
|
||||
UserAdded models.EventType = "user.added"
|
||||
UserRegistered models.EventType = "user.selfregistered"
|
||||
InitializedUserCodeAdded models.EventType = "user.initialization.code.added"
|
||||
InitializedUserCodeSent models.EventType = "user.initialization.code.sent"
|
||||
UserAdded models.EventType = "user.added"
|
||||
UserRegistered models.EventType = "user.selfregistered"
|
||||
InitializedUserCodeAdded models.EventType = "user.initialization.code.added"
|
||||
InitializedUserCodeSent models.EventType = "user.initialization.code.sent"
|
||||
InitializedUserCheckSucceeded models.EventType = "user.initialization.check.succeeded"
|
||||
InitializedUserCheckFailed models.EventType = "user.initialization.check.failed"
|
||||
|
||||
UserUserNameReserved models.EventType = "user.username.reserved"
|
||||
UserUserNameReleased models.EventType = "user.username.released"
|
||||
@@ -29,15 +31,17 @@ const (
|
||||
UserPasswordCheckSucceeded models.EventType = "user.password.check.succeeded"
|
||||
UserPasswordCheckFailed models.EventType = "user.password.check.failed"
|
||||
|
||||
UserEmailChanged models.EventType = "user.email.changed"
|
||||
UserEmailVerified models.EventType = "user.email.verified"
|
||||
UserEmailCodeAdded models.EventType = "user.email.code.added"
|
||||
UserEmailCodeSent models.EventType = "user.email.code.sent"
|
||||
UserEmailChanged models.EventType = "user.email.changed"
|
||||
UserEmailVerified models.EventType = "user.email.verified"
|
||||
UserEmailVerificationFailed models.EventType = "user.email.verification.failed"
|
||||
UserEmailCodeAdded models.EventType = "user.email.code.added"
|
||||
UserEmailCodeSent models.EventType = "user.email.code.sent"
|
||||
|
||||
UserPhoneChanged models.EventType = "user.phone.changed"
|
||||
UserPhoneVerified models.EventType = "user.phone.verified"
|
||||
UserPhoneCodeAdded models.EventType = "user.phone.code.added"
|
||||
UserPhoneCodeSent models.EventType = "user.phone.code.sent"
|
||||
UserPhoneChanged models.EventType = "user.phone.changed"
|
||||
UserPhoneVerified models.EventType = "user.phone.verified"
|
||||
UserPhoneVerificationFailed models.EventType = "user.phone.verification.failed"
|
||||
UserPhoneCodeAdded models.EventType = "user.phone.code.added"
|
||||
UserPhoneCodeSent models.EventType = "user.phone.code.sent"
|
||||
|
||||
UserProfileChanged models.EventType = "user.profile.changed"
|
||||
UserAddressChanged models.EventType = "user.address.changed"
|
||||
|
@@ -248,6 +248,38 @@ func UserInitCodeSentAggregate(aggCreator *es_models.AggregateCreator, existing
|
||||
}
|
||||
}
|
||||
|
||||
func InitCodeVerifiedAggregate(aggCreator *es_models.AggregateCreator, existing *model.User, password *model.Password) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existing.Email != nil && !existing.Email.IsEmailVerified {
|
||||
agg, err = agg.AppendEvent(model.UserEmailVerified, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if password != nil {
|
||||
agg, err = agg.AppendEvent(model.UserPasswordChanged, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return agg.AppendEvent(model.InitializedUserCheckSucceeded, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func InitCodeCheckFailedAggregate(aggCreator *es_models.AggregateCreator, existing *model.User) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return agg.AppendEvent(model.InitializedUserCheckFailed, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func SkipMfaAggregate(aggCreator *es_models.AggregateCreator, existing *model.User) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||
@@ -378,7 +410,7 @@ func EmailChangeAggregate(ctx context.Context, aggCreator *es_models.AggregateCr
|
||||
return append(aggregates, agg), nil
|
||||
}
|
||||
|
||||
func EmailVerifiedAggregate(aggCreator *es_models.AggregateCreator, existing *model.User) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
func EmailVerifiedAggregate(aggCreator *es_models.AggregateCreator, existing *model.User) es_sdk.AggregateFunc {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||
if err != nil {
|
||||
@@ -388,6 +420,16 @@ func EmailVerifiedAggregate(aggCreator *es_models.AggregateCreator, existing *mo
|
||||
}
|
||||
}
|
||||
|
||||
func EmailVerificationFailedAggregate(aggCreator *es_models.AggregateCreator, existing *model.User) es_sdk.AggregateFunc {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return agg.AppendEvent(model.UserEmailVerificationFailed, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func EmailVerificationCodeAggregate(aggCreator *es_models.AggregateCreator, existing *model.User, code *model.EmailCode) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
if code == nil {
|
||||
@@ -443,7 +485,8 @@ func PhoneChangeAggregate(aggCreator *es_models.AggregateCreator, existing *mode
|
||||
return agg, nil
|
||||
}
|
||||
}
|
||||
func PhoneVerifiedAggregate(aggCreator *es_models.AggregateCreator, existing *model.User) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
|
||||
func PhoneVerifiedAggregate(aggCreator *es_models.AggregateCreator, existing *model.User) es_sdk.AggregateFunc {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||
if err != nil {
|
||||
@@ -453,6 +496,16 @@ func PhoneVerifiedAggregate(aggCreator *es_models.AggregateCreator, existing *mo
|
||||
}
|
||||
}
|
||||
|
||||
func PhoneVerificationFailedAggregate(aggCreator *es_models.AggregateCreator, existing *model.User) es_sdk.AggregateFunc {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
agg, err := UserAggregate(ctx, aggCreator, existing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return agg.AppendEvent(model.UserPhoneVerificationFailed, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func PhoneVerificationCodeAggregate(aggCreator *es_models.AggregateCreator, existing *model.User, code *model.PhoneCode) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
if code == nil {
|
||||
|
@@ -716,7 +716,91 @@ func TestInitCodeSentAggregate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkipMfaAggregate(t *testing.T) {
|
||||
func TestInitCodeVerifiedAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
existing *model.User
|
||||
password *model.Password
|
||||
aggCreator *models.AggregateCreator
|
||||
}
|
||||
type res struct {
|
||||
eventLen int
|
||||
eventTypes []models.EventType
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "user init code only email verify",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.User{ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
|
||||
Profile: &model.Profile{UserName: "UserName"},
|
||||
Email: &model.Email{EmailAddress: "EmailAddress"},
|
||||
},
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 2,
|
||||
eventTypes: []models.EventType{model.UserEmailVerified, model.InitializedUserCheckSucceeded},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user init code only password",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.User{ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
|
||||
Profile: &model.Profile{UserName: "UserName"},
|
||||
Email: &model.Email{EmailAddress: "EmailAddress", IsEmailVerified: true},
|
||||
},
|
||||
password: &model.Password{ChangeRequired: false},
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 2,
|
||||
eventTypes: []models.EventType{model.UserPasswordChanged, model.InitializedUserCheckSucceeded},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user init code email and pw",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.User{ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
|
||||
Profile: &model.Profile{UserName: "UserName"},
|
||||
Email: &model.Email{EmailAddress: "EmailAddress"},
|
||||
},
|
||||
password: &model.Password{ChangeRequired: false},
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 3,
|
||||
eventTypes: []models.EventType{model.UserEmailVerified, model.UserPasswordChanged, model.InitializedUserCheckSucceeded},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := InitCodeVerifiedAggregate(tt.args.aggCreator, tt.args.existing, tt.args.password)(tt.args.ctx)
|
||||
|
||||
if tt.res.errFunc == nil && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
}
|
||||
for i := 0; i < tt.res.eventLen; i++ {
|
||||
if tt.res.errFunc == nil && agg.Events[i].Type != tt.res.eventTypes[i] {
|
||||
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventTypes[i], agg.Events[i].Type.String())
|
||||
}
|
||||
}
|
||||
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitCodeCheckFailedAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
existing *model.User
|
||||
@@ -764,6 +848,54 @@ func TestSkipMfaAggregate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkipMfaAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
existing *model.User
|
||||
aggCreator *models.AggregateCreator
|
||||
}
|
||||
type res struct {
|
||||
eventLen int
|
||||
eventType models.EventType
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "init code check failed",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.User{ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
|
||||
Profile: &model.Profile{UserName: "UserName"},
|
||||
},
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventType: model.InitializedUserCheckFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := InitCodeCheckFailedAggregate(tt.args.aggCreator, tt.args.existing)(tt.args.ctx)
|
||||
|
||||
if tt.res.errFunc == nil && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
}
|
||||
if tt.res.errFunc == nil && agg.Events[0].Type != tt.res.eventType {
|
||||
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String())
|
||||
}
|
||||
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangePasswordAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -1113,7 +1245,7 @@ func TestChangeEmailAggregate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifiyEmailAggregate(t *testing.T) {
|
||||
func TestVerifyEmailAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
existing *model.User
|
||||
@@ -1161,6 +1293,54 @@ func TestVerifiyEmailAggregate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerificationFailedEmailAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
existing *model.User
|
||||
aggCreator *models.AggregateCreator
|
||||
}
|
||||
type res struct {
|
||||
eventLen int
|
||||
eventTypes []models.EventType
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "user email verification failed aggregate ok",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.User{ObjectRoot: models.ObjectRoot{AggregateID: "ID"}},
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventTypes: []models.EventType{model.UserEmailVerificationFailed},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := EmailVerificationFailedAggregate(tt.args.aggCreator, tt.args.existing)(tt.args.ctx)
|
||||
|
||||
if tt.res.errFunc == nil && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
}
|
||||
for i := 0; i < tt.res.eventLen; i++ {
|
||||
if tt.res.errFunc == nil && agg.Events[i].Type != tt.res.eventTypes[i] {
|
||||
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventTypes[i], agg.Events[i].Type.String())
|
||||
}
|
||||
}
|
||||
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateEmailCodeAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -1391,7 +1571,7 @@ func TestChangePhoneAggregate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifiyPhoneAggregate(t *testing.T) {
|
||||
func TestVerifyPhoneAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
existing *model.User
|
||||
@@ -1441,6 +1621,56 @@ func TestVerifiyPhoneAggregate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerificationFailedPhoneAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
existing *model.User
|
||||
aggCreator *models.AggregateCreator
|
||||
}
|
||||
type res struct {
|
||||
eventLen int
|
||||
eventTypes []models.EventType
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "user phone verification failed aggregate ok",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.User{ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
|
||||
Phone: &model.Phone{PhoneNumber: "PhoneNumber"},
|
||||
},
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventTypes: []models.EventType{model.UserPhoneVerificationFailed},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := PhoneVerificationFailedAggregate(tt.args.aggCreator, tt.args.existing)(tt.args.ctx)
|
||||
|
||||
if tt.res.errFunc == nil && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
}
|
||||
for i := 0; i < tt.res.eventLen; i++ {
|
||||
if tt.res.errFunc == nil && agg.Events[i].Type != tt.res.eventTypes[i] {
|
||||
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventTypes[i], agg.Events[i].Type.String())
|
||||
}
|
||||
}
|
||||
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePhoneCodeAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
|
@@ -54,6 +54,7 @@ type UserView struct {
|
||||
OTPState int32 `json:"-" gorm:"column:otp_state"`
|
||||
MfaMaxSetUp int32 `json:"-" gorm:"column:mfa_max_set_up"`
|
||||
MfaInitSkipped time.Time `json:"-" gorm:"column:mfa_init_skipped"`
|
||||
InitRequired bool `json:"-" gorm:"column:init_required"`
|
||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||
}
|
||||
|
||||
@@ -87,6 +88,7 @@ func UserFromModel(user *model.UserView) *UserView {
|
||||
OTPState: int32(user.OTPState),
|
||||
MfaMaxSetUp: int32(user.MfaMaxSetUp),
|
||||
MfaInitSkipped: user.MfaInitSkipped,
|
||||
InitRequired: user.InitRequired,
|
||||
Sequence: user.Sequence,
|
||||
}
|
||||
}
|
||||
@@ -121,6 +123,7 @@ func UserToModel(user *UserView) *model.UserView {
|
||||
OTPState: model.MfaState(user.OTPState),
|
||||
MfaMaxSetUp: req_model.MfaLevel(user.MfaMaxSetUp),
|
||||
MfaInitSkipped: user.MfaInitSkipped,
|
||||
InitRequired: user.InitRequired,
|
||||
Sequence: user.Sequence,
|
||||
}
|
||||
}
|
||||
@@ -177,6 +180,10 @@ func (u *UserView) AppendEvent(event *models.Event) (err error) {
|
||||
u.OTPState = int32(model.MFASTATE_UNSPECIFIED)
|
||||
case es_model.MfaInitSkipped:
|
||||
u.MfaInitSkipped = event.CreationDate
|
||||
case es_model.InitializedUserCodeAdded:
|
||||
u.InitRequired = true
|
||||
case es_model.InitializedUserCheckSucceeded:
|
||||
u.InitRequired = false
|
||||
}
|
||||
u.ComputeObject()
|
||||
return err
|
||||
@@ -203,6 +210,7 @@ func (u *UserView) setPasswordData(event *models.Event) error {
|
||||
}
|
||||
u.PasswordSet = password.Secret != nil
|
||||
u.PasswordChangeRequired = password.ChangeRequired
|
||||
u.PasswordChanged = event.CreationDate
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
UserSessionKeySessionID = "id"
|
||||
UserSessionKeyUserAgentID = "user_agent_id"
|
||||
UserSessionKeyUserID = "user_id"
|
||||
UserSessionKeyState = "state"
|
||||
@@ -22,18 +21,19 @@ const (
|
||||
)
|
||||
|
||||
type UserSessionView struct {
|
||||
ID string `json:"-" gorm:"column:id;primary_key"`
|
||||
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
||||
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
||||
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
|
||||
State int32 `json:"-" gorm:"column:state"`
|
||||
UserAgentID string `json:"userAgentID" gorm:"column:user_agent_id"`
|
||||
UserID string `json:"userID" gorm:"column:user_id"`
|
||||
UserName string `json:"userName" gorm:"column:user_name"`
|
||||
PasswordVerification time.Time `json:"-" gorm:"column:password_verification"`
|
||||
MfaSoftwareVerification time.Time `json:"-" gorm:"column:mfa_software_verification"`
|
||||
MfaHardwareVerification time.Time `json:"-" gorm:"column:mfa_hardware_verification"`
|
||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
||||
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
||||
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
|
||||
State int32 `json:"-" gorm:"column:state"`
|
||||
UserAgentID string `json:"userAgentID" gorm:"column:user_agent_id;primary_key"`
|
||||
UserID string `json:"userID" gorm:"column:user_id;primary_key"`
|
||||
UserName string `json:"userName" gorm:"column:user_name"`
|
||||
PasswordVerification time.Time `json:"-" gorm:"column:password_verification"`
|
||||
MfaSoftwareVerification time.Time `json:"-" gorm:"column:mfa_software_verification"`
|
||||
MfaSoftwareVerificationType int32 `json:"-" gorm:"column:mfa_software_verification_type"`
|
||||
MfaHardwareVerification time.Time `json:"-" gorm:"column:mfa_hardware_verification"`
|
||||
MfaHardwareVerificationType int32 `json:"-" gorm:"column:mfa_hardware_verification_type"`
|
||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||
}
|
||||
|
||||
func UserSessionFromEvent(event *models.Event) (*UserSessionView, error) {
|
||||
@@ -47,18 +47,19 @@ func UserSessionFromEvent(event *models.Event) (*UserSessionView, error) {
|
||||
|
||||
func UserSessionToModel(userSession *UserSessionView) *model.UserSessionView {
|
||||
return &model.UserSessionView{
|
||||
ID: userSession.ID,
|
||||
ChangeDate: userSession.ChangeDate,
|
||||
CreationDate: userSession.CreationDate,
|
||||
ResourceOwner: userSession.ResourceOwner,
|
||||
State: req_model.UserSessionState(userSession.State),
|
||||
UserAgentID: userSession.UserAgentID,
|
||||
UserID: userSession.UserID,
|
||||
UserName: userSession.UserName,
|
||||
PasswordVerification: userSession.PasswordVerification,
|
||||
MfaSoftwareVerification: userSession.MfaSoftwareVerification,
|
||||
MfaHardwareVerification: userSession.MfaHardwareVerification,
|
||||
Sequence: userSession.Sequence,
|
||||
ChangeDate: userSession.ChangeDate,
|
||||
CreationDate: userSession.CreationDate,
|
||||
ResourceOwner: userSession.ResourceOwner,
|
||||
State: req_model.UserSessionState(userSession.State),
|
||||
UserAgentID: userSession.UserAgentID,
|
||||
UserID: userSession.UserID,
|
||||
UserName: userSession.UserName,
|
||||
PasswordVerification: userSession.PasswordVerification,
|
||||
MfaSoftwareVerification: userSession.MfaSoftwareVerification,
|
||||
MfaSoftwareVerificationType: req_model.MfaType(userSession.MfaSoftwareVerificationType),
|
||||
MfaHardwareVerification: userSession.MfaHardwareVerification,
|
||||
MfaHardwareVerificationType: req_model.MfaType(userSession.MfaHardwareVerificationType),
|
||||
Sequence: userSession.Sequence,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +81,7 @@ func (v *UserSessionView) AppendEvent(event *models.Event) {
|
||||
v.PasswordVerification = time.Time{}
|
||||
case es_model.MfaOtpCheckSucceeded:
|
||||
v.MfaSoftwareVerification = event.CreationDate
|
||||
v.MfaSoftwareVerificationType = int32(req_model.MfaTypeOTP)
|
||||
case es_model.MfaOtpCheckFailed,
|
||||
es_model.MfaOtpRemoved:
|
||||
v.MfaSoftwareVerification = time.Time{}
|
||||
|
@@ -51,8 +51,6 @@ func (req UserSessionSearchQuery) GetValue() interface{} {
|
||||
|
||||
func (key UserSessionSearchKey) ToColumnName() string {
|
||||
switch usr_model.UserSessionSearchKey(key) {
|
||||
case usr_model.USERSESSIONSEARCHKEY_SESSION_ID:
|
||||
return UserSessionKeySessionID
|
||||
case usr_model.USERSESSIONSEARCHKEY_USER_AGENT_ID:
|
||||
return UserSessionKeyUserAgentID
|
||||
case usr_model.USERSESSIONSEARCHKEY_USER_ID:
|
||||
|
@@ -9,13 +9,6 @@ import (
|
||||
"github.com/caos/zitadel/internal/view"
|
||||
)
|
||||
|
||||
func UserSessionByID(db *gorm.DB, table, sessionID string) (*model.UserSessionView, error) {
|
||||
userSession := new(model.UserSessionView)
|
||||
query := view.PrepareGetByKey(table, model.UserSessionSearchKey(usr_model.USERSESSIONSEARCHKEY_SESSION_ID), sessionID)
|
||||
err := query(db, userSession)
|
||||
return userSession, err
|
||||
}
|
||||
|
||||
func UserSessionByIDs(db *gorm.DB, table, agentID, userID string) (*model.UserSessionView, error) {
|
||||
userSession := new(model.UserSessionView)
|
||||
userAgentQuery := model.UserSessionSearchQuery{
|
||||
@@ -33,6 +26,20 @@ func UserSessionByIDs(db *gorm.DB, table, agentID, userID string) (*model.UserSe
|
||||
return userSession, err
|
||||
}
|
||||
|
||||
func UserSessionsByUserID(db *gorm.DB, table, userID string) ([]*model.UserSessionView, error) {
|
||||
userSessions := make([]*model.UserSessionView, 0)
|
||||
userAgentQuery := &usr_model.UserSessionSearchQuery{
|
||||
Key: usr_model.USERSESSIONSEARCHKEY_USER_ID,
|
||||
Method: global_model.SEARCHMETHOD_EQUALS,
|
||||
Value: userID,
|
||||
}
|
||||
query := view.PrepareSearchQuery(table, model.UserSessionSearchRequest{
|
||||
Queries: []*usr_model.UserSessionSearchQuery{userAgentQuery},
|
||||
})
|
||||
_, err := query(db, &userSessions)
|
||||
return userSessions, err
|
||||
}
|
||||
|
||||
func UserSessionsByAgentID(db *gorm.DB, table, agentID string) ([]*model.UserSessionView, error) {
|
||||
userSessions := make([]*model.UserSessionView, 0)
|
||||
userAgentQuery := &usr_model.UserSessionSearchQuery{
|
||||
@@ -43,7 +50,7 @@ func UserSessionsByAgentID(db *gorm.DB, table, agentID string) ([]*model.UserSes
|
||||
query := view.PrepareSearchQuery(table, model.UserSessionSearchRequest{
|
||||
Queries: []*usr_model.UserSessionSearchQuery{userAgentQuery},
|
||||
})
|
||||
_, err := query(db, userSessions)
|
||||
_, err := query(db, &userSessions)
|
||||
return userSessions, err
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user