feat: New event user (#1156)

* feat: change user command side

* feat: change user command side

* feat: use states on write model

* feat: command and query side in auth api

* feat: auth commands

* feat: check external idp id

* feat: user state check

* fix: error messages

* fix: is active state
This commit is contained in:
Fabi 2021-01-07 16:06:45 +01:00 committed by GitHub
parent 65a8efeb0e
commit 26c8113930
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 1242 additions and 442 deletions

View File

@ -162,7 +162,7 @@ func startAPI(ctx context.Context, conf *Config, authZRepo *authz_repo.EsReposit
apis.RegisterServer(ctx, management.CreateServer(command, query, managementRepo, conf.SystemDefaults))
}
if *authEnabled {
apis.RegisterServer(ctx, auth.CreateServer(authRepo))
apis.RegisterServer(ctx, auth.CreateServer(command, query, authRepo))
}
if *oidcEnabled {
op := oidc.NewProvider(ctx, conf.API.OIDC, authRepo, *localDevMode)

View File

@ -1,6 +1,8 @@
package auth
import (
"github.com/caos/zitadel/internal/v2/command"
"github.com/caos/zitadel/internal/v2/query"
"google.golang.org/grpc"
"github.com/caos/zitadel/internal/api/authz"
@ -17,16 +19,20 @@ const (
)
type Server struct {
repo repository.Repository
command *command.CommandSide
query *query.QuerySide
repo repository.Repository
}
type Config struct {
Repository eventsourcing.Config
}
func CreateServer(authRepo repository.Repository) *Server {
func CreateServer(command *command.CommandSide, query *query.QuerySide, authRepo repository.Repository) *Server {
return &Server{
repo: authRepo,
command: command,
query: query,
repo: authRepo,
}
}

View File

@ -2,7 +2,7 @@ package auth
import (
"context"
"github.com/caos/zitadel/internal/api/authz"
"github.com/golang/protobuf/ptypes/empty"
"github.com/caos/zitadel/pkg/grpc/auth"
@ -62,23 +62,24 @@ func (s *Server) GetMyMfas(ctx context.Context, _ *empty.Empty) (*auth.MultiFact
}
func (s *Server) UpdateMyUserProfile(ctx context.Context, request *auth.UpdateUserProfileRequest) (*auth.UserProfile, error) {
profile, err := s.repo.ChangeMyProfile(ctx, updateProfileToModel(ctx, request))
profile, err := s.command.ChangeHumanProfile(ctx, updateProfileToDomain(ctx, request))
if err != nil {
return nil, err
}
return profileFromModel(profile), nil
return profileFromDomain(profile), nil
}
func (s *Server) ChangeMyUserName(ctx context.Context, request *auth.ChangeUserNameRequest) (*empty.Empty, error) {
return &empty.Empty{}, s.repo.ChangeMyUsername(ctx, request.UserName)
ctxData := authz.GetCtxData(ctx)
return &empty.Empty{}, s.command.ChangeUsername(ctx, ctxData.OrgID, ctxData.UserID, request.UserName)
}
func (s *Server) ChangeMyUserEmail(ctx context.Context, request *auth.UpdateUserEmailRequest) (*auth.UserEmail, error) {
email, err := s.repo.ChangeMyEmail(ctx, updateEmailToModel(ctx, request))
email, err := s.command.ChangeHumanEmail(ctx, updateEmailToDomain(ctx, request))
if err != nil {
return nil, err
}
return emailFromModel(email), nil
return emailFromDomain(email), nil
}
func (s *Server) VerifyMyUserEmail(ctx context.Context, request *auth.VerifyMyUserEmailRequest) (*empty.Empty, error) {
@ -92,11 +93,11 @@ func (s *Server) ResendMyEmailVerificationMail(ctx context.Context, _ *empty.Emp
}
func (s *Server) ChangeMyUserPhone(ctx context.Context, request *auth.UpdateUserPhoneRequest) (*auth.UserPhone, error) {
phone, err := s.repo.ChangeMyPhone(ctx, updatePhoneToModel(ctx, request))
phone, err := s.command.ChangeHumanPhone(ctx, updatePhoneToDomain(ctx, request))
if err != nil {
return nil, err
}
return phoneFromModel(phone), nil
return phoneFromDomain(phone), nil
}
func (s *Server) VerifyMyUserPhone(ctx context.Context, request *auth.VerifyUserPhoneRequest) (*empty.Empty, error) {

View File

@ -3,6 +3,7 @@ package auth
import (
"context"
"encoding/json"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/logging"
"github.com/golang/protobuf/ptypes"
@ -52,7 +53,7 @@ func userViewFromModel(user *usr_model.UserView) *auth.UserView {
return userView
}
func profileFromModel(profile *usr_model.Profile) *auth.UserProfile {
func profileFromDomain(profile *domain.Profile) *auth.UserProfile {
creationDate, err := ptypes.TimestampProto(profile.CreationDate)
logging.Log("GRPC-56t5s").OnError(err).Debug("unable to parse timestamp")
@ -69,7 +70,7 @@ func profileFromModel(profile *usr_model.Profile) *auth.UserProfile {
DisplayName: profile.DisplayName,
NickName: profile.NickName,
PreferredLanguage: profile.PreferredLanguage.String(),
Gender: genderFromModel(profile.Gender),
Gender: genderFromDomain(profile.Gender),
}
}
@ -81,36 +82,37 @@ func profileViewFromModel(profile *usr_model.Profile) *auth.UserProfileView {
logging.Log("GRPC-9sujE").OnError(err).Debug("unable to parse timestamp")
return &auth.UserProfileView{
Id: profile.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: profile.Sequence,
FirstName: profile.FirstName,
LastName: profile.LastName,
DisplayName: profile.DisplayName,
NickName: profile.NickName,
PreferredLanguage: profile.PreferredLanguage.String(),
Gender: genderFromModel(profile.Gender),
Id: profile.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: profile.Sequence,
FirstName: profile.FirstName,
LastName: profile.LastName,
DisplayName: profile.DisplayName,
NickName: profile.NickName,
PreferredLanguage: profile.PreferredLanguage.String(),
//TODO: Use converter
Gender: auth.Gender(profile.Gender),
LoginNames: profile.LoginNames,
PreferredLoginName: profile.PreferredLoginName,
}
}
func updateProfileToModel(ctx context.Context, u *auth.UpdateUserProfileRequest) *usr_model.Profile {
func updateProfileToDomain(ctx context.Context, u *auth.UpdateUserProfileRequest) *domain.Profile {
preferredLanguage, err := language.Parse(u.PreferredLanguage)
logging.Log("GRPC-lk73L").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("language malformed")
return &usr_model.Profile{
return &domain.Profile{
ObjectRoot: models.ObjectRoot{AggregateID: authz.GetCtxData(ctx).UserID},
FirstName: u.FirstName,
LastName: u.LastName,
NickName: u.NickName,
PreferredLanguage: preferredLanguage,
Gender: genderToModel(u.Gender),
Gender: genderToDomain(u.Gender),
}
}
func emailFromModel(email *usr_model.Email) *auth.UserEmail {
func emailFromDomain(email *domain.Email) *auth.UserEmail {
creationDate, err := ptypes.TimestampProto(email.CreationDate)
logging.Log("GRPC-sdoi3").OnError(err).Debug("unable to parse timestamp")
@ -144,14 +146,14 @@ func emailViewFromModel(email *usr_model.Email) *auth.UserEmailView {
}
}
func updateEmailToModel(ctx context.Context, e *auth.UpdateUserEmailRequest) *usr_model.Email {
return &usr_model.Email{
func updateEmailToDomain(ctx context.Context, e *auth.UpdateUserEmailRequest) *domain.Email {
return &domain.Email{
ObjectRoot: models.ObjectRoot{AggregateID: authz.GetCtxData(ctx).UserID},
EmailAddress: e.Email,
}
}
func phoneFromModel(phone *usr_model.Phone) *auth.UserPhone {
func phoneFromDomain(phone *domain.Phone) *auth.UserPhone {
creationDate, err := ptypes.TimestampProto(phone.CreationDate)
logging.Log("GRPC-kjn5J").OnError(err).Debug("unable to parse timestamp")
@ -185,8 +187,8 @@ func phoneViewFromModel(phone *usr_model.Phone) *auth.UserPhoneView {
}
}
func updatePhoneToModel(ctx context.Context, e *auth.UpdateUserPhoneRequest) *usr_model.Phone {
return &usr_model.Phone{
func updatePhoneToDomain(ctx context.Context, e *auth.UpdateUserPhoneRequest) *domain.Phone {
return &domain.Phone{
ObjectRoot: models.ObjectRoot{AggregateID: authz.GetCtxData(ctx).UserID},
PhoneNumber: e.Phone,
}
@ -332,29 +334,29 @@ func userStateFromModel(state usr_model.UserState) auth.UserState {
}
}
func genderFromModel(gender usr_model.Gender) auth.Gender {
func genderFromDomain(gender domain.Gender) auth.Gender {
switch gender {
case usr_model.GenderFemale:
case domain.GenderFemale:
return auth.Gender_GENDER_FEMALE
case usr_model.GenderMale:
case domain.GenderMale:
return auth.Gender_GENDER_MALE
case usr_model.GenderDiverse:
case domain.GenderDiverse:
return auth.Gender_GENDER_DIVERSE
default:
return auth.Gender_GENDER_UNSPECIFIED
}
}
func genderToModel(gender auth.Gender) usr_model.Gender {
func genderToDomain(gender auth.Gender) domain.Gender {
switch gender {
case auth.Gender_GENDER_FEMALE:
return usr_model.GenderFemale
return domain.GenderFemale
case auth.Gender_GENDER_MALE:
return usr_model.GenderMale
return domain.GenderMale
case auth.Gender_GENDER_DIVERSE:
return usr_model.GenderDiverse
return domain.GenderDiverse
default:
return usr_model.GenderUnspecified
return domain.GenderUnspecified
}
}

View File

@ -17,16 +17,17 @@ func humanViewFromModel(user *usr_model.HumanView) *auth.HumanView {
DisplayName: user.DisplayName,
NickName: user.NickName,
PreferredLanguage: user.PreferredLanguage,
Gender: genderFromModel(user.Gender),
Email: user.Email,
IsEmailVerified: user.IsEmailVerified,
Phone: user.Phone,
IsPhoneVerified: user.IsPhoneVerified,
Country: user.Country,
Locality: user.Locality,
PostalCode: user.PostalCode,
Region: user.Region,
StreetAddress: user.StreetAddress,
PasswordChanged: passwordChanged,
//TODO: add converter
Gender: auth.Gender(user.Gender),
Email: user.Email,
IsEmailVerified: user.IsEmailVerified,
Phone: user.Phone,
IsPhoneVerified: user.IsPhoneVerified,
Country: user.Country,
Locality: user.Locality,
PostalCode: user.PostalCode,
Region: user.Region,
StreetAddress: user.StreetAddress,
PasswordChanged: passwordChanged,
}
}

View File

@ -52,7 +52,7 @@ func (s *Server) IsUserUnique(ctx context.Context, request *management.UniqueUse
}
func (s *Server) CreateUser(ctx context.Context, in *management.CreateUserRequest) (*management.UserResponse, error) {
user, err := s.command.AddUser(ctx, userCreateToDomain(in))
user, err := s.command.AddUser(ctx, authz.GetCtxData(ctx).OrgID, userCreateToDomain(in))
if err != nil {
return nil, err
}
@ -113,7 +113,7 @@ func (s *Server) GetUserProfile(ctx context.Context, in *management.UserID) (*ma
}
func (s *Server) ChangeUserUserName(ctx context.Context, request *management.UpdateUserUserNameRequest) (*empty.Empty, error) {
return &empty.Empty{}, s.user.ChangeUsername(ctx, request.Id, request.UserName)
return &empty.Empty{}, s.command.ChangeUsername(ctx, authz.GetCtxData(ctx).OrgID, request.Id, request.UserName)
}
func (s *Server) UpdateUserProfile(ctx context.Context, request *management.UpdateUserProfileRequest) (*management.UserProfile, error) {
@ -141,7 +141,7 @@ func (s *Server) ChangeUserEmail(ctx context.Context, request *management.Update
}
func (s *Server) ResendEmailVerificationMail(ctx context.Context, in *management.UserID) (*empty.Empty, error) {
err := s.user.CreateEmailVerificationCode(ctx, in.Id)
err := s.command.CreateHumanEmailVerificationCode(ctx, in.Id)
return &empty.Empty{}, err
}
@ -154,20 +154,20 @@ func (s *Server) GetUserPhone(ctx context.Context, in *management.UserID) (*mana
}
func (s *Server) ChangeUserPhone(ctx context.Context, request *management.UpdateUserPhoneRequest) (*management.UserPhone, error) {
phone, err := s.user.ChangePhone(ctx, updatePhoneToModel(request))
phone, err := s.command.ChangeHumanPhone(ctx, updatePhoneToDomain(request))
if err != nil {
return nil, err
}
return phoneFromModel(phone), nil
return phoneFromDomain(phone), nil
}
func (s *Server) RemoveUserPhone(ctx context.Context, userID *management.UserID) (*empty.Empty, error) {
err := s.user.RemovePhone(ctx, userID.Id)
err := s.command.RemoveHumanPhone(ctx, userID.Id)
return &empty.Empty{}, err
}
func (s *Server) ResendPhoneVerificationCode(ctx context.Context, in *management.UserID) (*empty.Empty, error) {
err := s.user.CreatePhoneVerificationCode(ctx, in.Id)
err := s.command.CreateHumanPhoneVerificationCode(ctx, in.Id)
return &empty.Empty{}, err
}
@ -188,18 +188,16 @@ func (s *Server) UpdateUserAddress(ctx context.Context, request *management.Upda
}
func (s *Server) SendSetPasswordNotification(ctx context.Context, request *management.SetPasswordNotificationRequest) (*empty.Empty, error) {
err := s.user.RequestSetPassword(ctx, request.Id, notifyTypeToModel(request.Type))
err := s.command.RequestSetPassword(ctx, request.Id, notifyTypeToDomain(request.Type))
return &empty.Empty{}, err
}
func (s *Server) SetInitialPassword(ctx context.Context, request *management.PasswordRequest) (*empty.Empty, error) {
_, err := s.user.SetOneTimePassword(ctx, passwordRequestToModel(request))
return &empty.Empty{}, err
return &empty.Empty{}, s.command.SetOneTimePassword(ctx, authz.GetCtxData(ctx).OrgID, request.Id, request.Password)
}
func (s *Server) ResendInitialMail(ctx context.Context, request *management.InitialMailRequest) (*empty.Empty, error) {
err := s.user.ResendInitialMail(ctx, request.Id, request.Email)
return &empty.Empty{}, err
return &empty.Empty{}, s.command.ResendInitialMail(ctx, request.Id, request.Email)
}
func (s *Server) SearchUserExternalIDPs(ctx context.Context, request *management.ExternalIDPSearchRequest) (*management.ExternalIDPSearchResponse, error) {
@ -211,8 +209,7 @@ func (s *Server) SearchUserExternalIDPs(ctx context.Context, request *management
}
func (s *Server) RemoveExternalIDP(ctx context.Context, request *management.ExternalIDPRemoveRequest) (*empty.Empty, error) {
err := s.user.RemoveExternalIDP(ctx, externalIDPRemoveToModel(request))
return &empty.Empty{}, err
return &empty.Empty{}, s.command.RemoveHumanExternalIDP(ctx, externalIDPRemoveToDomain(request))
}
func (s *Server) GetUserMfas(ctx context.Context, userID *management.UserID) (*management.UserMultiFactors, error) {
@ -224,13 +221,11 @@ func (s *Server) GetUserMfas(ctx context.Context, userID *management.UserID) (*m
}
func (s *Server) RemoveMfaOTP(ctx context.Context, userID *management.UserID) (*empty.Empty, error) {
err := s.user.RemoveOTP(ctx, userID.Id)
return &empty.Empty{}, err
return &empty.Empty{}, s.command.RemoveHumanOTP(ctx, userID.Id)
}
func (s *Server) RemoveMfaU2F(ctx context.Context, webAuthNTokenID *management.WebAuthNTokenID) (*empty.Empty, error) {
err := s.user.RemoveU2F(ctx, webAuthNTokenID.UserId, webAuthNTokenID.Id)
return &empty.Empty{}, err
return &empty.Empty{}, s.command.RemoveHumanU2F(ctx, webAuthNTokenID.UserId, webAuthNTokenID.Id)
}
func (s *Server) GetPasswordless(ctx context.Context, userID *management.UserID) (_ *management.WebAuthNTokens, err error) {
@ -242,8 +237,7 @@ func (s *Server) GetPasswordless(ctx context.Context, userID *management.UserID)
}
func (s *Server) RemovePasswordless(ctx context.Context, id *management.WebAuthNTokenID) (*empty.Empty, error) {
err := s.user.RemovePasswordless(ctx, id.UserId, id.Id)
return &empty.Empty{}, err
return &empty.Empty{}, s.command.RemoveHumanPasswordless(ctx, id.UserId, id.Id)
}
func (s *Server) SearchUserMemberships(ctx context.Context, in *management.UserMembershipSearchRequest) (*management.UserMembershipSearchResponse, error) {

View File

@ -76,11 +76,11 @@ func externalIDPSearchRequestToModel(request *management.ExternalIDPSearchReques
}
}
func externalIDPRemoveToModel(idp *management.ExternalIDPRemoveRequest) *usr_model.ExternalIDP {
return &usr_model.ExternalIDP{
ObjectRoot: models.ObjectRoot{AggregateID: idp.UserId},
IDPConfigID: idp.IdpConfigId,
UserID: idp.ExternalUserId,
func externalIDPRemoveToDomain(idp *management.ExternalIDPRemoveRequest) *domain.ExternalIDP {
return &domain.ExternalIDP{
ObjectRoot: models.ObjectRoot{AggregateID: idp.UserId},
IDPConfigID: idp.IdpConfigId,
ExternalUserID: idp.ExternalUserId,
}
}
@ -306,7 +306,7 @@ func updateEmailToDomain(e *management.UpdateUserEmailRequest) *domain.Email {
}
}
func phoneFromModel(phone *usr_model.Phone) *management.UserPhone {
func phoneFromDomain(phone *domain.Phone) *management.UserPhone {
creationDate, err := ptypes.TimestampProto(phone.CreationDate)
logging.Log("GRPC-ps9ws").OnError(err).Debug("unable to parse timestamp")
@ -339,8 +339,8 @@ func phoneViewFromModel(phone *usr_model.Phone) *management.UserPhoneView {
IsPhoneVerified: phone.IsPhoneVerified,
}
}
func updatePhoneToModel(e *management.UpdateUserPhoneRequest) *usr_model.Phone {
return &usr_model.Phone{
func updatePhoneToDomain(e *management.UpdateUserPhoneRequest) *domain.Phone {
return &domain.Phone{
ObjectRoot: models.ObjectRoot{AggregateID: e.Id},
PhoneNumber: e.Phone,
IsPhoneVerified: e.IsPhoneVerified,
@ -510,14 +510,14 @@ func mfaFromModel(mfa *usr_model.MultiFactor) *management.UserMultiFactor {
}
}
func notifyTypeToModel(state management.NotificationType) usr_model.NotificationType {
func notifyTypeToDomain(state management.NotificationType) domain.NotificationType {
switch state {
case management.NotificationType_NOTIFICATIONTYPE_EMAIL:
return usr_model.NotificationTypeEmail
return domain.NotificationTypeEmail
case management.NotificationType_NOTIFICATIONTYPE_SMS:
return usr_model.NotificationTypeSms
return domain.NotificationTypeSms
default:
return usr_model.NotificationTypeEmail
return domain.NotificationTypeEmail
}
}

View File

@ -100,13 +100,6 @@ func (repo *UserRepo) MyProfile(ctx context.Context) (*model.Profile, error) {
return user.GetProfile()
}
func (repo *UserRepo) ChangeMyProfile(ctx context.Context, profile *model.Profile) (*model.Profile, error) {
if err := checkIDs(ctx, profile.ObjectRoot); err != nil {
return nil, err
}
return repo.UserEvents.ChangeProfile(ctx, profile)
}
func (repo *UserRepo) SearchMyExternalIDPs(ctx context.Context, request *model.ExternalIDPSearchRequest) (*model.ExternalIDPSearchResponse, error) {
request.EnsureLimit(repo.SearchLimit)
sequence, seqErr := repo.View.GetLatestExternalIDPSequence("")
@ -154,13 +147,6 @@ func (repo *UserRepo) MyEmail(ctx context.Context) (*model.Email, error) {
return user.GetEmail()
}
func (repo *UserRepo) ChangeMyEmail(ctx context.Context, email *model.Email) (*model.Email, error) {
if err := checkIDs(ctx, email.ObjectRoot); err != nil {
return nil, err
}
return repo.UserEvents.ChangeEmail(ctx, email)
}
func (repo *UserRepo) VerifyEmail(ctx context.Context, userID, code string) error {
return repo.UserEvents.VerifyEmail(ctx, userID, code)
}
@ -388,18 +374,6 @@ func (repo *UserRepo) RemoveMyPasswordless(ctx context.Context, webAuthNTokenID
return repo.UserEvents.RemovePasswordlessToken(ctx, authz.GetCtxData(ctx).UserID, webAuthNTokenID)
}
func (repo *UserRepo) ChangeMyUsername(ctx context.Context, username string) error {
ctxData := authz.GetCtxData(ctx)
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(ctxData.OrgID)
if errors.IsNotFound(err) {
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return err
}
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
return repo.UserEvents.ChangeUsername(ctx, ctxData.UserID, username, orgPolicyView)
}
func (repo *UserRepo) ResendInitVerificationMail(ctx context.Context, userID string) error {
_, err := repo.UserEvents.CreateInitializeUserCodeByID(ctx, userID)
return err

View File

@ -50,10 +50,8 @@ type myUserRepo interface {
MyUser(ctx context.Context) (*model.UserView, error)
MyProfile(ctx context.Context) (*model.Profile, error)
ChangeMyProfile(ctx context.Context, profile *model.Profile) (*model.Profile, error)
MyEmail(ctx context.Context) (*model.Email, error)
ChangeMyEmail(ctx context.Context, email *model.Email) (*model.Email, error)
VerifyMyEmail(ctx context.Context, code string) error
ResendMyEmailVerificationMail(ctx context.Context) error
@ -86,7 +84,5 @@ type myUserRepo interface {
VerifyMyPasswordlessSetup(ctx context.Context, tokenName string, data []byte) error
RemoveMyPasswordless(ctx context.Context, webAuthNTokenID string) error
ChangeMyUsername(ctx context.Context, username string) error
MyUserChanges(ctx context.Context, lastSequence uint64, limit uint64, sortAscending bool) (*model.UserChanges, error)
}

View File

@ -9,8 +9,6 @@ import (
"github.com/caos/zitadel/internal/errors"
caos_errs "github.com/caos/zitadel/internal/errors"
es_int "github.com/caos/zitadel/internal/eventstore"
es_models "github.com/caos/zitadel/internal/eventstore/models"
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model"
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
global_model "github.com/caos/zitadel/internal/model"
@ -60,100 +58,6 @@ func (repo *UserRepo) UserByID(ctx context.Context, id string) (*usr_model.UserV
return model.UserToModel(&userCopy), nil
}
func (repo *UserRepo) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) {
pwPolicy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if err != nil && caos_errs.IsNotFound(err) {
pwPolicy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return nil, err
}
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(pwPolicy)
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if err != nil && errors.IsNotFound(err) {
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return nil, err
}
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
return repo.UserEvents.CreateUser(ctx, user, pwPolicyView, orgPolicyView)
}
func (repo *UserRepo) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) {
policyResourceOwner := authz.GetCtxData(ctx).OrgID
if resourceOwner != "" {
policyResourceOwner = resourceOwner
}
pwPolicy, err := repo.View.PasswordComplexityPolicyByAggregateID(policyResourceOwner)
if err != nil && caos_errs.IsNotFound(err) {
pwPolicy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return nil, err
}
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(pwPolicy)
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if err != nil && errors.IsNotFound(err) {
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return nil, err
}
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
return repo.UserEvents.RegisterUser(ctx, user, pwPolicyView, orgPolicyView, resourceOwner)
}
func (repo *UserRepo) DeactivateUser(ctx context.Context, id string) (*usr_model.User, error) {
return repo.UserEvents.DeactivateUser(ctx, id)
}
func (repo *UserRepo) ReactivateUser(ctx context.Context, id string) (*usr_model.User, error) {
return repo.UserEvents.ReactivateUser(ctx, id)
}
func (repo *UserRepo) LockUser(ctx context.Context, id string) (*usr_model.User, error) {
return repo.UserEvents.LockUser(ctx, id)
}
func (repo *UserRepo) UnlockUser(ctx context.Context, id string) (*usr_model.User, error) {
return repo.UserEvents.UnlockUser(ctx, id)
}
func (repo *UserRepo) RemoveUser(ctx context.Context, id string) error {
aggregates := make([]*es_models.Aggregate, 0)
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if err != nil && errors.IsNotFound(err) {
orgPolicy, err = repo.View.OrgIAMPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return err
}
orgPolicyView := iam_es_model.OrgIAMViewToModel(orgPolicy)
user, agg, err := repo.UserEvents.PrepareRemoveUser(ctx, id, orgPolicyView)
if err != nil {
return err
}
aggregates = append(aggregates, agg...)
// remove user_grants
usergrants, err := repo.View.UserGrantsByUserID(id)
if err != nil {
return err
}
for _, grant := range usergrants {
_, aggs, err := repo.UserGrantEvents.PrepareRemoveUserGrant(ctx, grant.ID, true)
if err != nil {
return err
}
for _, agg := range aggs {
aggregates = append(aggregates, agg)
}
}
return es_sdk.PushAggregates(ctx, repo.Eventstore.PushAggregates, user.AppendEvents, aggregates...)
}
func (repo *UserRepo) SearchUsers(ctx context.Context, request *usr_model.UserSearchRequest) (*usr_model.UserSearchResponse, error) {
request.EnsureLimit(repo.SearchLimit)
sequence, sequenceErr := repo.View.GetLatestUserSequence("")
@ -225,42 +129,10 @@ func (repo *UserRepo) UserMFAs(ctx context.Context, userID string) ([]*usr_model
return mfas, nil
}
func (repo *UserRepo) RemoveOTP(ctx context.Context, userID string) error {
return repo.UserEvents.RemoveOTP(ctx, userID)
}
func (repo *UserRepo) RemoveU2F(ctx context.Context, userID, webAuthNTokenID string) error {
return repo.UserEvents.RemoveU2FToken(ctx, userID, webAuthNTokenID)
}
func (repo *UserRepo) GetPasswordless(ctx context.Context, userID string) ([]*usr_model.WebAuthNToken, error) {
return repo.UserEvents.GetPasswordless(ctx, userID)
}
func (repo *UserRepo) RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error {
return repo.UserEvents.RemovePasswordlessToken(ctx, userID, webAuthNTokenID)
}
func (repo *UserRepo) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) {
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if err != nil && caos_errs.IsNotFound(err) {
policy, err = repo.View.PasswordComplexityPolicyByAggregateID(repo.SystemDefaults.IamID)
}
if err != nil {
return nil, err
}
pwPolicyView := iam_es_model.PasswordComplexityViewToModel(policy)
return repo.UserEvents.SetOneTimePassword(ctx, pwPolicyView, password)
}
func (repo *UserRepo) RequestSetPassword(ctx context.Context, id string, notifyType usr_model.NotificationType) error {
return repo.UserEvents.RequestSetPassword(ctx, id, notifyType)
}
func (repo *UserRepo) ResendInitialMail(ctx context.Context, userID, email string) error {
return repo.UserEvents.ResendInitialMail(ctx, userID, email)
}
func (repo *UserRepo) ProfileByID(ctx context.Context, userID string) (*usr_model.Profile, error) {
user, err := repo.UserByID(ctx, userID)
if err != nil {
@ -293,14 +165,6 @@ func (repo *UserRepo) SearchExternalIDPs(ctx context.Context, request *usr_model
return result, nil
}
func (repo *UserRepo) RemoveExternalIDP(ctx context.Context, externalIDP *usr_model.ExternalIDP) error {
return repo.UserEvents.RemoveExternalIDP(ctx, externalIDP)
}
func (repo *UserRepo) ChangeMachine(ctx context.Context, machine *usr_model.Machine) (*usr_model.Machine, error) {
return repo.UserEvents.ChangeMachine(ctx, machine)
}
func (repo *UserRepo) GetMachineKey(ctx context.Context, userID, keyID string) (*usr_model.MachineKeyView, error) {
key, err := repo.View.MachineKeyByIDs(userID, keyID)
if err != nil {
@ -338,10 +202,6 @@ func (repo *UserRepo) RemoveMachineKey(ctx context.Context, userID, keyID string
return repo.UserEvents.RemoveMachineKey(ctx, userID, keyID)
}
func (repo *UserRepo) ChangeProfile(ctx context.Context, profile *usr_model.Profile) (*usr_model.Profile, error) {
return repo.UserEvents.ChangeProfile(ctx, profile)
}
func (repo *UserRepo) ChangeUsername(ctx context.Context, userID, userName string) error {
orgPolicy, err := repo.View.OrgIAMPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
if err != nil && errors.IsNotFound(err) {
@ -365,14 +225,6 @@ func (repo *UserRepo) EmailByID(ctx context.Context, userID string) (*usr_model.
return user.GetEmail()
}
func (repo *UserRepo) ChangeEmail(ctx context.Context, email *usr_model.Email) (*usr_model.Email, error) {
return repo.UserEvents.ChangeEmail(ctx, email)
}
func (repo *UserRepo) CreateEmailVerificationCode(ctx context.Context, userID string) error {
return repo.UserEvents.CreateEmailVerificationCode(ctx, userID)
}
func (repo *UserRepo) PhoneByID(ctx context.Context, userID string) (*usr_model.Phone, error) {
user, err := repo.UserByID(ctx, userID)
if err != nil {
@ -384,18 +236,6 @@ func (repo *UserRepo) PhoneByID(ctx context.Context, userID string) (*usr_model.
return user.GetPhone()
}
func (repo *UserRepo) ChangePhone(ctx context.Context, email *usr_model.Phone) (*usr_model.Phone, error) {
return repo.UserEvents.ChangePhone(ctx, email)
}
func (repo *UserRepo) RemovePhone(ctx context.Context, userID string) error {
return repo.UserEvents.RemovePhone(ctx, userID)
}
func (repo *UserRepo) CreatePhoneVerificationCode(ctx context.Context, userID string) error {
return repo.UserEvents.CreatePhoneVerificationCode(ctx, userID)
}
func (repo *UserRepo) AddressByID(ctx context.Context, userID string) (*usr_model.Address, error) {
user, err := repo.UserByID(ctx, userID)
if err != nil {
@ -407,10 +247,6 @@ func (repo *UserRepo) AddressByID(ctx context.Context, userID string) (*usr_mode
return user.GetAddress()
}
func (repo *UserRepo) ChangeAddress(ctx context.Context, address *usr_model.Address) (*usr_model.Address, error) {
return repo.UserEvents.ChangeAddress(ctx, address)
}
func (repo *UserRepo) SearchUserMemberships(ctx context.Context, request *usr_model.UserMembershipSearchRequest) (*usr_model.UserMembershipSearchResponse, error) {
request.EnsureLimit(repo.SearchLimit)
sequence, sequenceErr := repo.View.GetLatestUserMembershipSequence("")

View File

@ -8,13 +8,6 @@ import (
type UserRepository interface {
UserByID(ctx context.Context, id string) (*model.UserView, error)
CreateUser(ctx context.Context, user *model.User) (*model.User, error)
RegisterUser(ctx context.Context, user *model.User, resourceOwner string) (*model.User, error)
DeactivateUser(ctx context.Context, id string) (*model.User, error)
ReactivateUser(ctx context.Context, id string) (*model.User, error)
LockUser(ctx context.Context, id string) (*model.User, error)
UnlockUser(ctx context.Context, id string) (*model.User, error)
RemoveUser(ctx context.Context, id string) error
SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error)
GetUserByLoginNameGlobal(ctx context.Context, email string) (*model.UserView, error)
@ -22,43 +15,24 @@ type UserRepository interface {
UserChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*model.UserChanges, error)
ChangeUsername(ctx context.Context, id, username string) error
SetOneTimePassword(ctx context.Context, password *model.Password) (*model.Password, error)
RequestSetPassword(ctx context.Context, id string, notifyType model.NotificationType) error
ProfileByID(ctx context.Context, userID string) (*model.Profile, error)
ChangeProfile(ctx context.Context, profile *model.Profile) (*model.Profile, error)
UserMFAs(ctx context.Context, userID string) ([]*model.MultiFactor, error)
RemoveOTP(ctx context.Context, userID string) error
RemoveU2F(ctx context.Context, userID, webAuthNTokenID string) error
GetPasswordless(ctx context.Context, userID string) ([]*model.WebAuthNToken, error)
RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error
SearchExternalIDPs(ctx context.Context, request *model.ExternalIDPSearchRequest) (*model.ExternalIDPSearchResponse, error)
RemoveExternalIDP(ctx context.Context, externalIDP *model.ExternalIDP) error
SearchMachineKeys(ctx context.Context, request *model.MachineKeySearchRequest) (*model.MachineKeySearchResponse, error)
GetMachineKey(ctx context.Context, userID, keyID string) (*model.MachineKeyView, error)
ChangeMachine(ctx context.Context, machine *model.Machine) (*model.Machine, error)
AddMachineKey(ctx context.Context, key *model.MachineKey) (*model.MachineKey, error)
RemoveMachineKey(ctx context.Context, userID, keyID string) error
EmailByID(ctx context.Context, userID string) (*model.Email, error)
ChangeEmail(ctx context.Context, email *model.Email) (*model.Email, error)
CreateEmailVerificationCode(ctx context.Context, userID string) error
PhoneByID(ctx context.Context, userID string) (*model.Phone, error)
ChangePhone(ctx context.Context, email *model.Phone) (*model.Phone, error)
RemovePhone(ctx context.Context, userID string) error
CreatePhoneVerificationCode(ctx context.Context, userID string) error
AddressByID(ctx context.Context, userID string) (*model.Address, error)
ChangeAddress(ctx context.Context, address *model.Address) (*model.Address, error)
SearchUserMemberships(ctx context.Context, request *model.UserMembershipSearchRequest) (*model.UserMembershipSearchResponse, error)
ResendInitialMail(ctx context.Context, userID, email string) error
}

View File

@ -2,6 +2,7 @@ Errors:
Internal: Es ist ein interner Fehler aufgetreten
NoChangesFound: Keine Änderungen gefunden
OriginNotAllowed: Dieser "Origin" ist nicht freigeschaltet
IDMissing: ID fehlt
User:
NotFound: Benutzer konnte nicht gefunden werden
NotFoundOnOrg: Benutzer konnte in der gewünschten Organisation nicht gefunden werden
@ -19,6 +20,7 @@ Errors:
NotLocked: Benutzer ist nicht gesperrt
NoChanges: Keine Änderungen gefunden
InitCodeNotFound: Kein Initialisierungs Code gefunden
UsernameNotChanged: Benutzername wurde nicht verändert
Profile:
NotFound: Profil nicht gefunden
NotChanged: Profile nicht verändert

View File

@ -2,6 +2,7 @@ Errors:
Internal: An internal error occured
NoChangesFound: No changes
OriginNotAllowed: This "Origin" is not allowed
IDMissing: ID missing
User:
NotFound: User could not be found
NotFoundOnOrg: User could not be found on chosen organisation
@ -19,6 +20,7 @@ Errors:
NotLocked: User is not locked
NoChanges: No changes found
InitCodeNotFound: Initialization Code not found
UsernameNotChanged: Username not changed
Profile:
NotFound: Profile not found
NotChanged: Profile not changed

View File

@ -63,12 +63,12 @@ func (r *CommandSide) ChangeDefaultIDPConfig(ctx context.Context, config *domain
return nil, err
}
if existingIDP.State == domain.IDPConfigStateRemoved || existingIDP.State == domain.IDPConfigStateUnspecified {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotExisting")
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotExisting")
}
changedEvent, hasChanged := existingIDP.NewChangedEvent(ctx, config.IDPConfigID, config.Name, config.StylingType)
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
iamAgg.PushEvents(changedEvent)
@ -86,7 +86,7 @@ func (r *CommandSide) DeactivateDefaultIDPConfig(ctx context.Context, idpID stri
return nil, err
}
if existingIDP.State != domain.IDPConfigStateActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotActive")
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotActive")
}
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
iamAgg.PushEvents(iam_repo.NewIDPConfigDeactivatedEvent(ctx, idpID))
@ -104,7 +104,7 @@ func (r *CommandSide) ReactivateDefaultIDPConfig(ctx context.Context, idpID stri
return nil, err
}
if existingIDP.State != domain.IDPConfigStateInactive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-5Mo0d", "Errors.IAM.IDPConfig.NotInactive")
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-5Mo0d", "Errors.IAM.IDPConfig.NotInactive")
}
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
iamAgg.PushEvents(iam_repo.NewIDPConfigReactivatedEvent(ctx, idpID))

View File

@ -30,7 +30,7 @@ func (r *CommandSide) ChangeDefaultIDPOIDCConfig(ctx context.Context, config *do
return nil, err
}
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}
iamAgg := IAMAggregateFromWriteModel(&existingConfig.WriteModel)

View File

@ -23,7 +23,7 @@ func (r *CommandSide) AddIAMMember(ctx context.Context, member *domain.IAMMember
if err != nil {
return nil, err
}
if addedMember.IsActive {
if addedMember.State == domain.MemberStateActive {
return nil, errors.ThrowAlreadyExists(nil, "IAM-PtXi1", "Errors.IAM.Member.AlreadyExists")
}
@ -95,7 +95,7 @@ func (r *CommandSide) iamMemberWriteModelByID(ctx context.Context, iamID, userID
return nil, err
}
if !writeModel.IsActive {
if writeModel.State == domain.MemberStateUnspecified || writeModel.State == domain.MemberStateRemoved {
return nil, errors.ThrowNotFound(nil, "IAM-D8JxR", "Errors.NotFound")
}

View File

@ -30,7 +30,7 @@ func (r *CommandSide) addDefaultLabelPolicy(ctx context.Context, iamAgg *iam_rep
if err != nil {
return err
}
if addedPolicy.IsActive {
if addedPolicy.State == domain.PolicyStateActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LabelPolicy.AlreadyExists")
}
@ -46,13 +46,13 @@ func (r *CommandSide) ChangeDefaultLabelPolicy(ctx context.Context, policy *doma
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0K9dq", "Errors.IAM.LabelPolicy.NotFound")
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-0K9dq", "Errors.IAM.LabelPolicy.NotFound")
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.PrimaryColor, policy.SecondaryColor)
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)

View File

@ -42,7 +42,7 @@ func (r *CommandSide) addDefaultLoginPolicy(ctx context.Context, iamAgg *iam_rep
if err != nil {
return err
}
if addedPolicy.IsActive {
if addedPolicy.State == domain.PolicyStateActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.AlreadyExists")
}
@ -73,12 +73,12 @@ func (r *CommandSide) changeDefaultLoginPolicy(ctx context.Context, iamAgg *iam_
if err != nil {
return err
}
if !existingPolicy.IsActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-M0sif", "Errors.IAM.LoginPolicy.NotFound")
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return caos_errs.ThrowNotFound(nil, "IAM-M0sif", "Errors.IAM.LoginPolicy.NotFound")
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, domain.PasswordlessType(policy.PasswordlessType))
if !hasChanged {
return caos_errs.ThrowAlreadyExists(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
return caos_errs.ThrowPreconditionFailed(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
}
iamAgg.PushEvents(changedEvent)
@ -92,7 +92,7 @@ func (r *CommandSide) AddIDPProviderToDefaultLoginPolicy(ctx context.Context, id
if err != nil {
return nil, err
}
if idpModel.IsActive {
if idpModel.State == domain.IdentityProviderStateActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.IDP.AlreadyExists")
}
@ -113,8 +113,8 @@ func (r *CommandSide) RemoveIDPProviderFromDefaultLoginPolicy(ctx context.Contex
if err != nil {
return err
}
if !idpModel.IsActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-39fjs", "Errors.IAM.LoginPolicy.IDP.NotExisting")
if idpModel.State == domain.IdentityProviderStateUnspecified || idpModel.State == domain.IdentityProviderStateRemoved {
return caos_errs.ThrowNotFound(nil, "IAM-39fjs", "Errors.IAM.LoginPolicy.IDP.NotExisting")
}
iamAgg := IAMAggregateFromWriteModel(&idpModel.IdentityProviderWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewIdentityProviderRemovedEvent(ctx, idpProvider.IDPConfigID))
@ -143,7 +143,7 @@ func (r *CommandSide) addSecondFactorToDefaultLoginPolicy(ctx context.Context, i
return err
}
if secondFactorModel.IsActive {
if secondFactorModel.State == domain.FactorStateActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.MFA.AlreadyExists")
}
@ -158,8 +158,8 @@ func (r *CommandSide) RemoveSecondFactorFromDefaultLoginPolicy(ctx context.Conte
if err != nil {
return err
}
if !secondFactorModel.IsActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-3M9od", "Errors.IAM.LoginPolicy.MFA.NotExisting")
if secondFactorModel.State == domain.FactorStateUnspecified || secondFactorModel.State == domain.FactorStateRemoved {
return caos_errs.ThrowNotFound(nil, "IAM-3M9od", "Errors.IAM.LoginPolicy.MFA.NotExisting")
}
iamAgg := IAMAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewLoginPolicySecondFactorRemovedEvent(ctx, domain.SecondFactorType(secondFactor)))
@ -187,7 +187,7 @@ func (r *CommandSide) addMultiFactorToDefaultLoginPolicy(ctx context.Context, ia
if err != nil {
return err
}
if multiFactorModel.IsActive {
if multiFactorModel.State == domain.FactorStateActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-3M9od", "Errors.IAM.LoginPolicy.MFA.AlreadyExists")
}
@ -202,8 +202,8 @@ func (r *CommandSide) RemoveMultiFactorFromDefaultLoginPolicy(ctx context.Contex
if err != nil {
return err
}
if multiFactorModel.IsActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-3M9df", "Errors.IAM.LoginPolicy.MFA.NotExisting")
if multiFactorModel.State == domain.FactorStateUnspecified || multiFactorModel.State == domain.FactorStateRemoved {
return caos_errs.ThrowNotFound(nil, "IAM-3M9df", "Errors.IAM.LoginPolicy.MFA.NotExisting")
}
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewLoginPolicyMultiFactorRemovedEvent(ctx, domain.MultiFactorType(multiFactor)))

View File

@ -41,7 +41,7 @@ func (r *CommandSide) addDefaultOrgIAMPolicy(ctx context.Context, iamAgg *iam_re
if err != nil {
return err
}
if addedPolicy.IsActive {
if addedPolicy.State == domain.PolicyStateActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-Lk0dS", "Errors.IAM.OrgIAMPolicy.AlreadyExists")
}
iamAgg.PushEvents(iam_repo.NewOrgIAMPolicyAddedEvent(ctx, policy.UserLoginMustBeDomain))
@ -55,13 +55,13 @@ func (r *CommandSide) ChangeDefaultOrgIAMPolicy(ctx context.Context, policy *dom
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0Pl0d", "Errors.IAM.OrgIAMPolicy.NotFound")
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-0Pl0d", "Errors.IAM.OrgIAMPolicy.NotFound")
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.UserLoginMustBeDomain)
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PolicyOrgIAMWriteModel.WriteModel)

View File

@ -30,7 +30,7 @@ func (r *CommandSide) addDefaultPasswordAgePolicy(ctx context.Context, iamAgg *i
if err != nil {
return err
}
if addedPolicy.IsActive {
if addedPolicy.State == domain.PolicyStateActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-Lk0dS", "Errors.IAM.PasswordAgePolicy.AlreadyExists")
}
@ -45,13 +45,13 @@ func (r *CommandSide) ChangeDefaultPasswordAgePolicy(ctx context.Context, policy
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0oPew", "Errors.IAM.PasswordAgePolicy.NotFound")
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-0oPew", "Errors.IAM.PasswordAgePolicy.NotFound")
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.ExpireWarnDays, policy.MaxAgeDays)
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PasswordAgePolicyWriteModel.WriteModel)

View File

@ -45,7 +45,7 @@ func (r *CommandSide) addDefaultPasswordComplexityPolicy(ctx context.Context, ia
if err != nil {
return err
}
if addedPolicy.IsActive {
if addedPolicy.State == domain.PolicyStateActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-Lk0dS", "Errors.IAM.PasswordComplexityPolicy.AlreadyExists")
}
@ -64,13 +64,13 @@ func (r *CommandSide) ChangeDefaultPasswordComplexityPolicy(ctx context.Context,
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0oPew", "Errors.IAM.PasswordAgePolicy.NotFound")
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-0oPew", "Errors.IAM.PasswordAgePolicy.NotFound")
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.MinLength, policy.HasLowercase, policy.HasUppercase, policy.HasNumber, policy.HasSymbol)
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PasswordComplexityPolicyWriteModel.WriteModel)
iamAgg.PushEvents(changedEvent)

View File

@ -30,7 +30,7 @@ func (r *CommandSide) addDefaultPasswordLockoutPolicy(ctx context.Context, iamAg
if err != nil {
return err
}
if addedPolicy.IsActive {
if addedPolicy.State == domain.PolicyStateActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-0olDf", "Errors.IAM.PasswordLockoutPolicy.AlreadyExists")
}
@ -45,13 +45,13 @@ func (r *CommandSide) ChangeDefaultPasswordLockoutPolicy(ctx context.Context, po
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0oPew", "Errors.IAM.PasswordLockoutPolicy.NotFound")
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "IAM-0oPew", "Errors.IAM.PasswordLockoutPolicy.NotFound")
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.MaxAttempts, policy.ShowLockOutFailures)
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.PasswordLockoutPolicy.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.PasswordLockoutPolicy.NotChanged")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PasswordLockoutPolicyWriteModel.WriteModel)

View File

@ -11,7 +11,7 @@ type IdentityProviderWriteModel struct {
IDPConfigID string
IDPProviderType domain.IdentityProviderType
IsActive bool
State domain.IdentityProviderState
}
func (wm *IdentityProviderWriteModel) Reduce() error {
@ -20,9 +20,9 @@ func (wm *IdentityProviderWriteModel) Reduce() error {
case *policy.IdentityProviderAddedEvent:
wm.IDPConfigID = e.IDPConfigID
wm.IDPProviderType = e.IDPProviderType
wm.IsActive = true
wm.State = domain.IdentityProviderStateActive
case *policy.IdentityProviderRemovedEvent:
wm.IsActive = false
wm.State = domain.IdentityProviderStateRemoved
}
}
return wm.WriteModel.Reduce()

View File

@ -2,15 +2,17 @@ package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/member"
)
type MemberWriteModel struct {
eventstore.WriteModel
UserID string
Roles []string
IsActive bool
UserID string
Roles []string
State domain.MemberState
}
func NewMemberWriteModel(userID string) *MemberWriteModel {
@ -25,12 +27,12 @@ func (wm *MemberWriteModel) Reduce() error {
case *member.MemberAddedEvent:
wm.UserID = e.UserID
wm.Roles = e.Roles
wm.IsActive = true
wm.State = domain.MemberStateActive
case *member.MemberChangedEvent:
wm.Roles = e.Roles
case *member.MemberRemovedEvent:
wm.Roles = nil
wm.IsActive = false
wm.State = domain.MemberStateRemoved
}
}
return wm.WriteModel.Reduce()

View File

@ -14,7 +14,7 @@ func (r *CommandSide) GetOrgIAMPolicy(ctx context.Context, orgID string) (*domai
if err != nil {
return nil, err
}
if policy.IsActive {
if policy.State == domain.PolicyStateActive {
return orgWriteModelToOrgIAMPolicy(policy), nil
}
return r.GetDefaultOrgIAMPolicy(ctx)
@ -26,7 +26,7 @@ func (r *CommandSide) AddOrgIAMPolicy(ctx context.Context, policy *domain.OrgIAM
if err != nil {
return nil, err
}
if addedPolicy.IsActive {
if addedPolicy.State == domain.PolicyStateActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-5M0ds", "Errors.Org.OrgIAMPolicy.AlreadyExists")
}
orgAgg := ORGAggregateFromWriteModel(&addedPolicy.PolicyOrgIAMWriteModel.WriteModel)
@ -45,13 +45,13 @@ func (r *CommandSide) ChangeOrgIAMPolicy(ctx context.Context, policy *domain.Org
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-2N9sd", "Errors.Org.OrgIAMPolicy.NotFound")
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-2N9sd", "Errors.Org.OrgIAMPolicy.NotFound")
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.UserLoginMustBeDomain)
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-3M9ds", "Errors.Org.LabelPolicy.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-3M9ds", "Errors.Org.LabelPolicy.NotChanged")
}
orgAgg := ORGAggregateFromWriteModel(&existingPolicy.PolicyOrgIAMWriteModel.WriteModel)

View File

@ -11,7 +11,7 @@ func (r *CommandSide) GetOrgPasswordComplexityPolicy(ctx context.Context, orgID
if err != nil {
return nil, err
}
if policy.IsActive {
if policy.State == domain.PolicyStateActive {
return orgWriteModelToPasswordComplexityPolicy(policy), nil
}
return r.GetDefaultPasswordComplexityPolicy(ctx)

View File

@ -2,6 +2,7 @@ package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/policy"
)
@ -10,7 +11,8 @@ type LabelPolicyWriteModel struct {
PrimaryColor string
SecondaryColor string
IsActive bool
State domain.PolicyState
}
func (wm *LabelPolicyWriteModel) Reduce() error {
@ -19,7 +21,7 @@ func (wm *LabelPolicyWriteModel) Reduce() error {
case *policy.LabelPolicyAddedEvent:
wm.PrimaryColor = e.PrimaryColor
wm.SecondaryColor = e.SecondaryColor
wm.IsActive = true
wm.State = domain.PolicyStateActive
case *policy.LabelPolicyChangedEvent:
if e.PrimaryColor != nil {
wm.PrimaryColor = *e.PrimaryColor
@ -28,7 +30,7 @@ func (wm *LabelPolicyWriteModel) Reduce() error {
wm.SecondaryColor = *e.SecondaryColor
}
case *policy.LabelPolicyRemovedEvent:
wm.IsActive = false
wm.State = domain.PolicyStateRemoved
}
}
return wm.WriteModel.Reduce()

View File

@ -8,8 +8,8 @@ import (
type SecondFactorWriteModel struct {
eventstore.WriteModel
MFAType domain.SecondFactorType
IsActive bool
MFAType domain.SecondFactorType
State domain.FactorState
}
func (wm *SecondFactorWriteModel) Reduce() error {
@ -17,10 +17,10 @@ func (wm *SecondFactorWriteModel) Reduce() error {
switch e := event.(type) {
case *policy.SecondFactorAddedEvent:
wm.MFAType = e.MFAType
wm.IsActive = true
wm.State = domain.FactorStateActive
case *policy.SecondFactorRemovedEvent:
wm.MFAType = e.MFAType
wm.IsActive = false
wm.State = domain.FactorStateRemoved
}
}
return wm.WriteModel.Reduce()
@ -28,8 +28,8 @@ func (wm *SecondFactorWriteModel) Reduce() error {
type MultiFactoryWriteModel struct {
eventstore.WriteModel
MFAType domain.MultiFactorType
IsActive bool
MFAType domain.MultiFactorType
State domain.FactorState
}
func (wm *MultiFactoryWriteModel) Reduce() error {
@ -37,10 +37,10 @@ func (wm *MultiFactoryWriteModel) Reduce() error {
switch e := event.(type) {
case *policy.MultiFactorAddedEvent:
wm.MFAType = e.MFAType
wm.IsActive = true
wm.State = domain.FactorStateActive
case *policy.MultiFactorRemovedEvent:
wm.MFAType = e.MFAType
wm.IsActive = false
wm.State = domain.FactorStateRemoved
}
}
return wm.WriteModel.Reduce()

View File

@ -14,7 +14,7 @@ type LoginPolicyWriteModel struct {
AllowExternalIDP bool
ForceMFA bool
PasswordlessType domain.PasswordlessType
IsActive bool
State domain.PolicyState
}
func (wm *LoginPolicyWriteModel) Reduce() error {
@ -26,7 +26,7 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
wm.AllowExternalIDP = e.AllowExternalIDP
wm.ForceMFA = e.ForceMFA
wm.PasswordlessType = e.PasswordlessType
wm.IsActive = true
wm.State = domain.PolicyStateActive
case *policy.LoginPolicyChangedEvent:
if e.AllowRegister != nil {
wm.AllowRegister = *e.AllowRegister
@ -44,7 +44,7 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
wm.PasswordlessType = *e.PasswordlessType
}
case *policy.LoginPolicyRemovedEvent:
wm.IsActive = false
wm.State = domain.PolicyStateRemoved
}
}
return wm.WriteModel.Reduce()

View File

@ -2,6 +2,7 @@ package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/policy"
)
@ -9,7 +10,7 @@ type PolicyOrgIAMWriteModel struct {
eventstore.WriteModel
UserLoginMustBeDomain bool
IsActive bool
State domain.PolicyState
}
func (wm *PolicyOrgIAMWriteModel) Reduce() error {
@ -17,7 +18,7 @@ func (wm *PolicyOrgIAMWriteModel) Reduce() error {
switch e := event.(type) {
case *policy.OrgIAMPolicyAddedEvent:
wm.UserLoginMustBeDomain = e.UserLoginMustBeDomain
wm.IsActive = true
wm.State = domain.PolicyStateActive
case *policy.OrgIAMPolicyChangedEvent:
if e.UserLoginMustBeDomain != nil {
wm.UserLoginMustBeDomain = *e.UserLoginMustBeDomain

View File

@ -2,6 +2,7 @@ package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/policy"
)
@ -10,7 +11,7 @@ type PasswordAgePolicyWriteModel struct {
ExpireWarnDays uint64
MaxAgeDays uint64
IsActive bool
State domain.PolicyState
}
func (wm *PasswordAgePolicyWriteModel) Reduce() error {
@ -19,7 +20,7 @@ func (wm *PasswordAgePolicyWriteModel) Reduce() error {
case *policy.PasswordAgePolicyAddedEvent:
wm.ExpireWarnDays = e.ExpireWarnDays
wm.MaxAgeDays = e.MaxAgeDays
wm.IsActive = true
wm.State = domain.PolicyStateActive
case *policy.PasswordAgePolicyChangedEvent:
if e.ExpireWarnDays != nil {
wm.ExpireWarnDays = *e.ExpireWarnDays
@ -28,7 +29,7 @@ func (wm *PasswordAgePolicyWriteModel) Reduce() error {
wm.ExpireWarnDays = *e.ExpireWarnDays
}
case *policy.PasswordAgePolicyRemovedEvent:
wm.IsActive = false
wm.State = domain.PolicyStateRemoved
}
}
return wm.WriteModel.Reduce()

View File

@ -2,6 +2,7 @@ package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/policy"
)
@ -13,7 +14,7 @@ type PasswordComplexityPolicyWriteModel struct {
HasUpperCase bool
HasNumber bool
HasSymbol bool
IsActive bool
State domain.PolicyState
}
func (wm *PasswordComplexityPolicyWriteModel) Reduce() error {
@ -25,7 +26,7 @@ func (wm *PasswordComplexityPolicyWriteModel) Reduce() error {
wm.HasUpperCase = e.HasUpperCase
wm.HasNumber = e.HasNumber
wm.HasSymbol = e.HasSymbol
wm.IsActive = true
wm.State = domain.PolicyStateActive
case *policy.PasswordComplexityPolicyChangedEvent:
if e.MinLength != nil {
wm.MinLength = *e.MinLength
@ -43,7 +44,7 @@ func (wm *PasswordComplexityPolicyWriteModel) Reduce() error {
wm.HasSymbol = *e.HasSymbol
}
case *policy.PasswordComplexityPolicyRemovedEvent:
wm.IsActive = false
wm.State = domain.PolicyStateRemoved
}
}
return wm.WriteModel.Reduce()

View File

@ -2,6 +2,7 @@ package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/policy"
)
@ -10,7 +11,7 @@ type PasswordLockoutPolicyWriteModel struct {
MaxAttempts uint64
ShowLockOutFailures bool
IsActive bool
State domain.PolicyState
}
func (wm *PasswordLockoutPolicyWriteModel) Reduce() error {
@ -19,7 +20,7 @@ func (wm *PasswordLockoutPolicyWriteModel) Reduce() error {
case *policy.PasswordLockoutPolicyAddedEvent:
wm.MaxAttempts = e.MaxAttempts
wm.ShowLockOutFailures = e.ShowLockOutFailures
wm.IsActive = true
wm.State = domain.PolicyStateActive
case *policy.PasswordLockoutPolicyChangedEvent:
if e.MaxAttempts != nil {
wm.MaxAttempts = *e.MaxAttempts
@ -28,7 +29,7 @@ func (wm *PasswordLockoutPolicyWriteModel) Reduce() error {
wm.ShowLockOutFailures = *e.ShowLockOutFailures
}
case *policy.PasswordLockoutPolicyRemovedEvent:
wm.IsActive = false
wm.State = domain.PolicyStateRemoved
}
}
return wm.WriteModel.Reduce()

View File

@ -8,19 +8,19 @@ import (
"github.com/caos/zitadel/internal/v2/repository/user"
)
func (r *CommandSide) AddUser(ctx context.Context, user *domain.User) (*domain.User, error) {
func (r *CommandSide) AddUser(ctx context.Context, orgID string, user *domain.User) (*domain.User, error) {
if !user.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2N9fs", "Errors.User.Invalid")
}
if user.Human != nil {
human, err := r.AddHuman(ctx, user.ResourceOwner, user.UserName, user.Human)
human, err := r.AddHuman(ctx, orgID, user.UserName, user.Human)
if err != nil {
return nil, err
}
return &domain.User{UserName: user.UserName, Human: human}, nil
} else if user.Machine != nil {
machine, err := r.AddMachine(ctx, user.ResourceOwner, user.UserName, user.Machine)
machine, err := r.AddMachine(ctx, orgID, user.UserName, user.Machine)
if err != nil {
return nil, err
}
@ -29,13 +29,13 @@ func (r *CommandSide) AddUser(ctx context.Context, user *domain.User) (*domain.U
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-8K0df", "Errors.User.TypeUndefined")
}
func (r *CommandSide) RegisterUser(ctx context.Context, user *domain.User) (*domain.User, error) {
func (r *CommandSide) RegisterUser(ctx context.Context, orgID string, user *domain.User) (*domain.User, error) {
if !user.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2N9fs", "Errors.User.Invalid")
}
if user.Human != nil {
human, err := r.RegisterHuman(ctx, user.ResourceOwner, user.UserName, user.Human, nil)
human, err := r.RegisterHuman(ctx, orgID, user.UserName, user.Human, nil)
if err != nil {
return nil, err
}
@ -44,13 +44,46 @@ func (r *CommandSide) RegisterUser(ctx context.Context, user *domain.User) (*dom
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-8K0df", "Errors.User.TypeUndefined")
}
func (r *CommandSide) ChangeUsername(ctx context.Context, orgID, userID, userName string) error {
if orgID == "" || userID == "" || userName == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2N9fs", "Errors.IDMissing")
}
existingUser, err := r.userWriteModelByID(ctx, userID)
if err != nil {
return err
}
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-5N9ds", "Errors.User.NotFound")
}
if existingUser.UserName == userName {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UsernameNotChanged")
}
orgIAMPolicy, err := r.GetOrgIAMPolicy(ctx, orgID)
if err != nil {
return err
}
if err := CheckOrgIAMPolicyForUserName(userName, orgIAMPolicy); err != nil {
return err
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(user.NewUsernameChangedEvent(ctx, userName))
//TODO: Check Uniqueness
//TODO: release old username, set new unique username
return r.eventstore.PushAggregate(ctx, existingUser, userAgg)
}
func (r *CommandSide) DeactivateUser(ctx context.Context, userID string) (*domain.User, error) {
if userID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-m0gDf", "Errors.User.UserIDMissing")
}
existingUser, err := r.userWriteModelByID(ctx, userID)
if err != nil {
return nil, err
}
if existingUser.UserState != domain.UserStateUnspecified || existingUser.UserState != domain.UserStateDeleted {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-3M9ds", "Errors.User.NotFound")
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3M9ds", "Errors.User.NotFound")
}
if existingUser.UserState == domain.UserStateInactive {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0sf", "Errors.User.AlreadyInactive")
@ -66,12 +99,15 @@ func (r *CommandSide) DeactivateUser(ctx context.Context, userID string) (*domai
}
func (r *CommandSide) ReactivateUser(ctx context.Context, userID string) (*domain.User, error) {
if userID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M9ds", "Errors.User.UserIDMissing")
}
existingUser, err := r.userWriteModelByID(ctx, userID)
if err != nil {
return nil, err
}
if existingUser.UserState != domain.UserStateUnspecified || existingUser.UserState != domain.UserStateDeleted {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-4M0sd", "Errors.User.NotFound")
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-4M0sd", "Errors.User.NotFound")
}
if existingUser.UserState != domain.UserStateInactive {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0sf", "Errors.User.NotInactive")
@ -87,12 +123,15 @@ func (r *CommandSide) ReactivateUser(ctx context.Context, userID string) (*domai
}
func (r *CommandSide) LockUser(ctx context.Context, userID string) (*domain.User, error) {
if userID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0sd", "Errors.User.UserIDMissing")
}
existingUser, err := r.userWriteModelByID(ctx, userID)
if err != nil {
return nil, err
}
if existingUser.UserState != domain.UserStateUnspecified || existingUser.UserState != domain.UserStateDeleted {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-5M9fs", "Errors.User.NotFound")
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-5M9fs", "Errors.User.NotFound")
}
if existingUser.UserState != domain.UserStateActive && existingUser.UserState != domain.UserStateInitial {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.ShouldBeActiveOrInitial")
@ -108,12 +147,15 @@ func (r *CommandSide) LockUser(ctx context.Context, userID string) (*domain.User
}
func (r *CommandSide) UnlockUser(ctx context.Context, userID string) (*domain.User, error) {
if userID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-M0dse", "Errors.User.UserIDMissing")
}
existingUser, err := r.userWriteModelByID(ctx, userID)
if err != nil {
return nil, err
}
if existingUser.UserState != domain.UserStateUnspecified || existingUser.UserState != domain.UserStateDeleted {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-M0dos", "Errors.User.NotFound")
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-M0dos", "Errors.User.NotFound")
}
if existingUser.UserState != domain.UserStateLocked {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0ds", "Errors.User.NotLocked")
@ -129,16 +171,20 @@ func (r *CommandSide) UnlockUser(ctx context.Context, userID string) (*domain.Us
}
func (r *CommandSide) RemoveUser(ctx context.Context, userID string) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing")
}
existingUser, err := r.userWriteModelByID(ctx, userID)
if err != nil {
return err
}
if existingUser.UserState != domain.UserStateDeleted {
return caos_errs.ThrowAlreadyExists(nil, "COMMAND-5M0od", "Errors.User.NotFound")
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-5M0od", "Errors.User.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(user.NewUserRemovedEvent(ctx))
//TODO: release unqie username
//TODO: remove user grants
return r.eventstore.PushAggregate(ctx, existingUser, userAgg)
}

View File

@ -57,6 +57,13 @@ func writeModelToEmail(wm *HumanEmailWriteModel) *domain.Email {
}
}
func writeModelToPhone(wm *HumanPhoneWriteModel) *domain.Phone {
return &domain.Phone{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
PhoneNumber: wm.Phone,
IsPhoneVerified: wm.IsPhoneVerified,
}
}
func writeModelToAddress(wm *HumanAddressWriteModel) *domain.Address {
return &domain.Address{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),

View File

@ -27,9 +27,13 @@ func (r *CommandSide) AddHuman(ctx context.Context, orgID, username string, huma
addedHuman := NewHumanWriteModel(human.AggregateID)
//TODO: Check Unique Username
human.CheckOrgIAMPolicy(username, orgIAMPolicy)
if err := human.CheckOrgIAMPolicy(username, orgIAMPolicy); err != nil {
return nil, err
}
human.SetNamesAsDisplayname()
human.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg, true)
if err := human.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg, true); err != nil {
return nil, err
}
userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel)
addEvent := user.NewHumanAddedEvent(
@ -107,9 +111,13 @@ func (r *CommandSide) RegisterHuman(ctx context.Context, orgID, username string,
addedHuman := NewHumanWriteModel(human.AggregateID)
//TODO: Check Unique Username or unique external idp
human.CheckOrgIAMPolicy(username, orgIAMPolicy)
if err := human.CheckOrgIAMPolicy(username, orgIAMPolicy); err != nil {
return nil, err
}
human.SetNamesAsDisplayname()
human.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg, true)
if err := human.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg, true); err != nil {
return nil, err
}
userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel)
addEvent := user.NewHumanRegisteredEvent(
@ -144,7 +152,7 @@ func (r *CommandSide) RegisterHuman(ctx context.Context, orgID, username string,
if err != nil {
return nil, err
}
user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry)
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
}
if human.Email != nil && human.EmailAddress != "" && human.IsEmailVerified {
@ -155,7 +163,9 @@ func (r *CommandSide) RegisterHuman(ctx context.Context, orgID, username string,
if err != nil {
return nil, err
}
user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry)
userAgg.PushEvents(user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry))
} else if human.Phone != nil && human.PhoneNumber != "" && human.IsPhoneVerified {
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
}
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
@ -165,3 +175,31 @@ func (r *CommandSide) RegisterHuman(ctx context.Context, orgID, username string,
return writeModelToHuman(addedHuman), nil
}
func (r *CommandSide) ResendInitialMail(ctx context.Context, userID, email string) (err error) {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UserIDMissing")
}
existingEmail, err := r.emailWriteModel(ctx, userID)
if err != nil {
return err
}
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9df", "Errors.User.NotFound")
}
if existingEmail.UserState != domain.UserStateInitial {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.AlreadyInitialised")
}
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
if email != "" && existingEmail.Email != email {
changedEvent, _ := existingEmail.NewChangedEvent(ctx, email)
userAgg.PushEvents(changedEvent)
}
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
if err != nil {
return err
}
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
}

View File

@ -12,12 +12,12 @@ func (r *CommandSide) ChangeHumanAddress(ctx context.Context, address *domain.Ad
if err != nil {
return nil, err
}
if existingAddress.UserState == domain.UserStateUnspecified || existingAddress.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-0pLdo", "Errors.User.Address.NotFound")
if existingAddress.State == domain.AddressStateUnspecified || existingAddress.State == domain.AddressStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-0pLdo", "Errors.User.Address.NotFound")
}
changedEvent, hasChanged := existingAddress.NewChangedEvent(ctx, address.Country, address.Locality, address.PostalCode, address.Region, address.StreetAddress)
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-3M0cs", "Errors.User.Address.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M0cs", "Errors.User.Address.NotChanged")
}
userAgg := UserAggregateFromWriteModel(&existingAddress.WriteModel)
userAgg.PushEvents(changedEvent)

View File

@ -16,7 +16,7 @@ type HumanAddressWriteModel struct {
Region string
StreetAddress string
UserState domain.UserState
State domain.AddressState
}
func NewHumanAddressWriteModel(userID string) *HumanAddressWriteModel {
@ -49,14 +49,14 @@ func (wm *HumanAddressWriteModel) Reduce() error {
wm.PostalCode = e.PostalCode
wm.Region = e.Region
wm.StreetAddress = e.StreetAddress
wm.UserState = domain.UserStateActive
wm.State = domain.AddressStateActive
case *user.HumanRegisteredEvent:
wm.Country = e.Country
wm.Locality = e.Locality
wm.PostalCode = e.PostalCode
wm.Region = e.Region
wm.StreetAddress = e.StreetAddress
wm.UserState = domain.UserStateActive
wm.State = domain.AddressStateActive
case *user.HumanAddressChangedEvent:
if e.Country != nil {
wm.Country = *e.Country
@ -74,7 +74,7 @@ func (wm *HumanAddressWriteModel) Reduce() error {
wm.StreetAddress = *e.StreetAddress
}
case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted
wm.State = domain.AddressStateRemoved
}
}
return wm.WriteModel.Reduce()

View File

@ -5,10 +5,11 @@ import (
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
func (r *CommandSide) ChangeHumanEmail(ctx context.Context, email *domain.Email) (*domain.Email, error) {
if !email.IsValid() {
if !email.IsValid() || email.AggregateID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M9sf", "Errors.Email.Invalid")
}
@ -17,15 +18,25 @@ func (r *CommandSide) ChangeHumanEmail(ctx context.Context, email *domain.Email)
return nil, err
}
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-0Pe4r", "Errors.User.Email.NotFound")
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-0Pe4r", "Errors.User.Email.NotFound")
}
changedEvent, hasChanged := existingEmail.NewChangedEvent(ctx, email.EmailAddress)
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
}
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
userAgg.PushEvents(changedEvent)
if email.IsEmailVerified {
userAgg.PushEvents(user.NewHumanEmailVerifiedEvent(ctx))
} else {
emailCode, err := domain.NewEmailCode(r.emailVerificationCode)
if err != nil {
return nil, err
}
userAgg.PushEvents(user.NewHumanEmailCodeAddedEvent(ctx, emailCode.Code, emailCode.Expiry))
}
err = r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
if err != nil {
return nil, err
@ -34,6 +45,34 @@ func (r *CommandSide) ChangeHumanEmail(ctx context.Context, email *domain.Email)
return writeModelToEmail(existingEmail), nil
}
func (r *CommandSide) CreateHumanEmailVerificationCode(ctx context.Context, userID string) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
}
existingEmail, err := r.emailWriteModel(ctx, userID)
if err != nil {
return err
}
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-0Pe4r", "Errors.User.Email.NotFound")
}
if existingEmail.UserState == domain.UserStateInitial {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-E3fbw", "Errors.User.NotInitialised")
}
if existingEmail.IsEmailVerified {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M9ds", "Errors.User.Email.AlreadyVerified")
}
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
emailCode, err := domain.NewEmailCode(r.emailVerificationCode)
if err != nil {
return err
}
userAgg.PushEvents(user.NewHumanEmailCodeAddedEvent(ctx, emailCode.Code, emailCode.Expiry))
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
}
func (r *CommandSide) emailWriteModel(ctx context.Context, userID string) (writeModel *HumanEmailWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()

View File

@ -44,15 +44,18 @@ func (wm *HumanEmailWriteModel) Reduce() error {
switch e := event.(type) {
case *user.HumanAddedEvent:
wm.Email = e.EmailAddress
wm.UserState = domain.UserStateActive
wm.UserState = domain.UserStateInitial
case *user.HumanRegisteredEvent:
wm.Email = e.EmailAddress
wm.UserState = domain.UserStateActive
wm.UserState = domain.UserStateInitial
case *user.HumanEmailChangedEvent:
wm.Email = e.EmailAddress
wm.IsEmailVerified = false
case *user.HumanEmailVerifiedEvent:
wm.IsEmailVerified = true
if wm.UserState == domain.UserStateInitial {
wm.UserState = domain.UserStateActive
}
case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted
}

View File

@ -0,0 +1,52 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
func (r *CommandSide) RemoveHumanExternalIDP(ctx context.Context, externalIDP *domain.ExternalIDP) error {
return r.removeHumanExternalIDP(ctx, externalIDP, false)
}
func (r *CommandSide) removeHumanExternalIDP(ctx context.Context, externalIDP *domain.ExternalIDP, cascade bool) error {
if externalIDP.IsValid() {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M9ds", "Errors.IDMissing")
}
existingExternalIDP, err := r.externalIDPWriteModelByID(ctx, externalIDP.AggregateID, externalIDP.IDPConfigID, externalIDP.ExternalUserID)
if err != nil {
return err
}
if existingExternalIDP.State == domain.ExternalIDPStateUnspecified || existingExternalIDP.State == domain.ExternalIDPStateRemoved {
return caos_errs.ThrowNotFound(nil, "COMMAND-5M0ds", "Errors.User.ExternalIDP.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingExternalIDP.WriteModel)
if !cascade {
userAgg.PushEvents(
user.NewHumanExternalIDPRemovedEvent(ctx, externalIDP.IDPConfigID, externalIDP.ExternalUserID),
)
} else {
userAgg.PushEvents(
user.NewHumanExternalIDPCascadeRemovedEvent(ctx, externalIDP.IDPConfigID, externalIDP.ExternalUserID),
)
}
//TODO: Release unique externalidp
return r.eventstore.PushAggregate(ctx, existingExternalIDP, userAgg)
}
func (r *CommandSide) externalIDPWriteModelByID(ctx context.Context, userID, idpConfigID, externalUserID string) (writeModel *HumanExternalIDPWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel = NewHumanExternalIDPWriteModel(userID, idpConfigID, externalUserID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}

View File

@ -0,0 +1,72 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
type HumanExternalIDPWriteModel struct {
eventstore.WriteModel
IDPConfigID string
ExternalUserID string
DisplayName string
State domain.ExternalIDPState
}
func NewHumanExternalIDPWriteModel(userID, idpConfigID, externalUserID string) *HumanExternalIDPWriteModel {
return &HumanExternalIDPWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
},
IDPConfigID: idpConfigID,
ExternalUserID: externalUserID,
}
}
func (wm *HumanExternalIDPWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.HumanExternalIDPAddedEvent:
if wm.IDPConfigID == e.IDPConfigID && wm.ExternalUserID == e.UserID {
wm.AppendEvents(e)
}
case *user.HumanExternalIDPRemovedEvent:
if wm.IDPConfigID == e.IDPConfigID && wm.ExternalUserID == e.UserID {
wm.AppendEvents(e)
}
case *user.HumanExternalIDPCascadeRemovedEvent:
if wm.IDPConfigID == e.IDPConfigID && wm.ExternalUserID == e.UserID {
wm.AppendEvents(e)
}
case *user.UserRemovedEvent:
wm.AppendEvents(e)
}
}
}
func (wm *HumanExternalIDPWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanExternalIDPAddedEvent:
wm.IDPConfigID = e.IDPConfigID
wm.DisplayName = e.DisplayName
wm.ExternalUserID = e.UserID
wm.State = domain.ExternalIDPStateActive
case *user.HumanExternalIDPRemovedEvent:
wm.State = domain.ExternalIDPStateRemoved
case *user.HumanExternalIDPCascadeRemovedEvent:
wm.State = domain.ExternalIDPStateRemoved
case *user.UserRemovedEvent:
wm.State = domain.ExternalIDPStateRemoved
}
}
return wm.WriteModel.Reduce()
}
func (wm *HumanExternalIDPWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
}

View File

@ -76,6 +76,8 @@ func (wm *HumanWriteModel) Reduce() error {
wm.reduceHumanAddedEvent(e)
case *user.HumanRegisteredEvent:
wm.reduceHumanRegisteredEvent(e)
case *user.UsernameChangedEvent:
wm.UserName = e.UserName
case *user.HumanProfileChangedEvent:
wm.reduceHumanProfileChangedEvent(e)
case *user.HumanEmailChangedEvent:

View File

@ -0,0 +1,41 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
func (r *CommandSide) RemoveHumanOTP(ctx context.Context, userID string) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing")
}
existingOTP, err := r.otpWriteModelByID(ctx, userID)
if err != nil {
return err
}
if existingOTP.State == domain.OTPStateUnspecified || existingOTP.State == domain.OTPStateRemoved {
return caos_errs.ThrowNotFound(nil, "COMMAND-5M0ds", "Errors.User.OTP.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingOTP.WriteModel)
userAgg.PushEvents(
user.NewHumanOTPRemovedEvent(ctx),
)
return r.eventstore.PushAggregate(ctx, existingOTP, userAgg)
}
func (r *CommandSide) otpWriteModelByID(ctx context.Context, userID string) (writeModel *HumanOTPWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel = NewHumanOTPWriteModel(userID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}

View File

@ -0,0 +1,57 @@
package command
import (
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
type HumanOTPWriteModel struct {
eventstore.WriteModel
Secret *crypto.CryptoValue
State domain.OTPState
}
func NewHumanOTPWriteModel(userID string) *HumanOTPWriteModel {
return &HumanOTPWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
},
}
}
func (wm *HumanOTPWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.HumanOTPAddedEvent:
wm.AppendEvents(e)
case *user.HumanOTPRemovedEvent:
wm.AppendEvents(e)
case *user.UserRemovedEvent:
wm.AppendEvents(e)
}
}
}
func (wm *HumanOTPWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanOTPAddedEvent:
wm.Secret = e.Secret
wm.State = domain.OTPStateActive
case *user.HumanOTPRemovedEvent:
wm.State = domain.OTPStateRemoved
case *user.UserRemovedEvent:
wm.State = domain.OTPStateRemoved
}
}
return wm.WriteModel.Reduce()
}
func (wm *HumanOTPWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
}

View File

@ -0,0 +1,107 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/crypto"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
func (r *CommandSide) SetOneTimePassword(ctx context.Context, orgID, userID, passwordString string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
existingPassword, err := r.passwordWriteModel(ctx, userID)
if err != nil {
return err
}
password := &domain.Password{
SecretString: passwordString,
ChangeRequired: true,
}
return r.changePassword(ctx, orgID, userID, "", password, existingPassword)
}
func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPassword, newPassword, userAgentID string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
existingPassword, err := r.passwordWriteModel(ctx, userID)
if err != nil {
return err
}
if existingPassword.Secret != nil {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Fds3s", "Errors.User.Password.Empty")
}
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
err = crypto.CompareHash(existingPassword.Secret, []byte(oldPassword), r.userPasswordAlg)
spanPasswordComparison.EndWithError(err)
if err != nil {
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-3M0fs", "Errors.User.Password.Invalid")
}
password := &domain.Password{
SecretString: newPassword,
ChangeRequired: true,
}
return r.changePassword(ctx, orgID, userID, userAgentID, password, existingPassword)
}
func (r *CommandSide) changePassword(ctx context.Context, orgID, userID, userAgentID string, password *domain.Password, existingPassword *HumanPasswordWriteModel) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-B8hY3", "Errors.User.UserIDMissing")
}
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-G8dh3", "Errors.User.Email.NotFound")
}
if existingPassword.UserState == domain.UserStateInitial {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-M9dse", "Errors.User.NotInitialised")
}
pwPolicy, err := r.GetOrgPasswordComplexityPolicy(ctx, orgID)
if err != nil {
return err
}
if err := password.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg); err != nil {
return err
}
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
userAgg.PushEvents(user.NewHumanPasswordChangedEvent(ctx, password.SecretCrypto, password.ChangeRequired, userAgentID))
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
}
func (r *CommandSide) RequestSetPassword(ctx context.Context, userID string, notifyType domain.NotificationType) (err error) {
existingHuman, err := r.userWriteModelByID(ctx, userID)
if err != nil {
return err
}
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
return caos_errs.ThrowNotFound(nil, "COMMAND-Hj9ds", "Errors.User.NotFound")
}
if existingHuman.UserState == domain.UserStateInitial {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.NotInitialised")
}
userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
passwordCode, err := domain.NewPasswordCode(r.passwordVerificationCode)
if err != nil {
return err
}
userAgg.PushEvents(user.NewHumanPasswordCodeAddedEvent(ctx, passwordCode.Code, passwordCode.Expiry, notifyType))
return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
}
func (r *CommandSide) passwordWriteModel(ctx context.Context, userID string) (writeModel *HumanPasswordWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel = NewHumanPasswordWriteModel(userID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}

View File

@ -0,0 +1,70 @@
package command
import (
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
type HumanPasswordWriteModel struct {
eventstore.WriteModel
Secret *crypto.CryptoValue
SecretChangeRequired bool
UserState domain.UserState
}
func NewHumanPasswordWriteModel(userID string) *HumanPasswordWriteModel {
return &HumanPasswordWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
},
}
}
func (wm *HumanPasswordWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.HumanPasswordChangedEvent:
wm.AppendEvents(e)
case *user.HumanAddedEvent, *user.HumanRegisteredEvent:
wm.AppendEvents(e)
case *user.HumanEmailVerifiedEvent:
wm.AppendEvents(e)
case *user.UserRemovedEvent:
wm.AppendEvents(e)
}
}
}
func (wm *HumanPasswordWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanAddedEvent:
wm.Secret = e.Secret
wm.SecretChangeRequired = e.ChangeRequired
wm.UserState = domain.UserStateInitial
case *user.HumanRegisteredEvent:
wm.Secret = e.Secret
wm.SecretChangeRequired = e.ChangeRequired
wm.UserState = domain.UserStateActive
case *user.HumanPasswordChangedEvent:
wm.Secret = e.Secret
wm.SecretChangeRequired = e.ChangeRequired
case *user.HumanEmailVerifiedEvent:
if wm.UserState == domain.UserStateInitial {
wm.UserState = domain.UserStateActive
}
case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted
}
}
return wm.WriteModel.Reduce()
}
func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
}

View File

@ -0,0 +1,101 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
func (r *CommandSide) ChangeHumanPhone(ctx context.Context, phone *domain.Phone) (*domain.Phone, error) {
if !phone.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.Phone.Invalid")
}
existingPhone, err := r.phoneWriteModel(ctx, phone.AggregateID)
if err != nil {
return nil, err
}
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-5M0ds", "Errors.User.Phone.NotFound")
}
changedEvent, hasChanged := existingPhone.NewChangedEvent(ctx, phone.PhoneNumber)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-wF94r", "Errors.User.Phone.NotChanged")
}
userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel)
userAgg.PushEvents(changedEvent)
if phone.IsPhoneVerified {
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
} else {
phoneCode, err := domain.NewPhoneCode(r.phoneVerificationCode)
if err != nil {
return nil, err
}
userAgg.PushEvents(user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry))
}
err = r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
if err != nil {
return nil, err
}
return writeModelToPhone(existingPhone), nil
}
func (r *CommandSide) CreateHumanPhoneVerificationCode(ctx context.Context, userID string) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
}
existingPhone, err := r.phoneWriteModel(ctx, userID)
if err != nil {
return err
}
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Phone.NotFound")
}
if existingPhone.IsPhoneVerified {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sf", "Errors.User.Phone.AlreadyVerified")
}
userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel)
phoneCode, err := domain.NewPhoneCode(r.phoneVerificationCode)
if err != nil {
return err
}
userAgg.PushEvents(user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry))
return r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
}
func (r *CommandSide) RemoveHumanPhone(ctx context.Context, userID string) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.User.UserIDMissing")
}
existingPhone, err := r.phoneWriteModel(ctx, userID)
if err != nil {
return err
}
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
return caos_errs.ThrowNotFound(nil, "COMMAND-5M0ds", "Errors.User.Phone.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel)
userAgg.PushEvents(
user.NewHumanPhoneRemovedEvent(ctx),
)
return r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
}
func (r *CommandSide) phoneWriteModel(ctx context.Context, userID string) (writeModel *HumanPhoneWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel = NewHumanPhoneWriteModel(userID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}

View File

@ -0,0 +1,88 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
type HumanPhoneWriteModel struct {
eventstore.WriteModel
Phone string
IsPhoneVerified bool
State domain.PhoneState
}
func NewHumanPhoneWriteModel(userID string) *HumanPhoneWriteModel {
return &HumanPhoneWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
},
}
}
func (wm *HumanPhoneWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.HumanAddedEvent, *user.HumanRegisteredEvent:
wm.AppendEvents(e)
case *user.HumanPhoneChangedEvent:
wm.AppendEvents(e)
case *user.HumanPhoneVerifiedEvent:
wm.AppendEvents(e)
case *user.HumanPhoneRemovedEvent:
wm.AppendEvents(e)
case *user.UserRemovedEvent:
wm.AppendEvents(e)
}
}
}
func (wm *HumanPhoneWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanAddedEvent:
if e.PhoneNumber != "" {
wm.Phone = e.PhoneNumber
wm.State = domain.PhoneStateActive
}
case *user.HumanRegisteredEvent:
if e.PhoneNumber != "" {
wm.Phone = e.PhoneNumber
wm.State = domain.PhoneStateActive
}
case *user.HumanPhoneChangedEvent:
wm.Phone = e.PhoneNumber
wm.IsPhoneVerified = false
wm.State = domain.PhoneStateActive
case *user.HumanPhoneVerifiedEvent:
wm.IsPhoneVerified = true
case *user.HumanPhoneRemovedEvent:
wm.State = domain.PhoneStateRemoved
case *user.UserRemovedEvent:
wm.State = domain.PhoneStateRemoved
}
}
return wm.WriteModel.Reduce()
}
func (wm *HumanPhoneWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
}
func (wm *HumanPhoneWriteModel) NewChangedEvent(
ctx context.Context,
phone string,
) (*user.HumanPhoneChangedEvent, bool) {
hasChanged := false
changedEvent := user.NewHumanPhoneChangedEvent(ctx)
if wm.Phone != phone {
hasChanged = true
changedEvent.PhoneNumber = phone
}
return changedEvent, hasChanged
}

View File

@ -8,7 +8,7 @@ import (
)
func (r *CommandSide) ChangeHumanProfile(ctx context.Context, profile *domain.Profile) (*domain.Profile, error) {
if !profile.IsValid() {
if !profile.IsValid() && profile.AggregateID != "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8io0d", "Errors.User.Profile.Invalid")
}
@ -17,11 +17,11 @@ func (r *CommandSide) ChangeHumanProfile(ctx context.Context, profile *domain.Pr
return nil, err
}
if existingProfile.UserState == domain.UserStateUnspecified || existingProfile.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-3M9sd", "Errors.User.Profile.NotFound")
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.User.Profile.NotFound")
}
changedEvent, hasChanged := existingProfile.NewChangedEvent(ctx, profile.FirstName, profile.LastName, profile.NickName, profile.DisplayName, profile.PreferredLanguage, domain.Gender(profile.Gender))
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-2M0fs", "Errors.User.Profile.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0fs", "Errors.User.Profile.NotChanged")
}
userAgg := UserAggregateFromWriteModel(&existingProfile.WriteModel)
userAgg.PushEvents(changedEvent)

View File

@ -0,0 +1,50 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
func (r *CommandSide) RemoveHumanU2F(ctx context.Context, userID, webAuthNID string) error {
event := user.NewHumanU2FRemovedEvent(ctx, webAuthNID)
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, event)
}
func (r *CommandSide) RemoveHumanPasswordless(ctx context.Context, userID, webAuthNID string) error {
event := user.NewHumanPasswordlessRemovedEvent(ctx, webAuthNID)
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, event)
}
func (r *CommandSide) removeHumanWebAuthN(ctx context.Context, userID, webAuthNID string, event eventstore.EventPusher) error {
if userID == "" || webAuthNID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M9de", "Errors.IDMissing")
}
existingWebAuthN, err := r.webauthNWriteModelByID(ctx, userID, webAuthNID)
if err != nil {
return err
}
if existingWebAuthN.State == domain.WebAuthNStateUnspecified || existingWebAuthN.State == domain.WebAuthNStateRemoved {
return caos_errs.ThrowNotFound(nil, "COMMAND-5M0ds", "Errors.User.ExternalIDP.NotFound")
}
userAgg := UserAggregateFromWriteModel(&existingWebAuthN.WriteModel)
userAgg.PushEvents(event)
return r.eventstore.PushAggregate(ctx, existingWebAuthN, userAgg)
}
func (r *CommandSide) webauthNWriteModelByID(ctx context.Context, userID, webAuthNID string) (writeModel *HumanWebAuthNWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel = NewHumanWebAuthNWriteModel(userID, webAuthNID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}

View File

@ -0,0 +1,61 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
type HumanWebAuthNWriteModel struct {
eventstore.WriteModel
WebauthNTokenID string
State domain.WebAuthNState
}
func NewHumanWebAuthNWriteModel(userID, wbAuthNTokenID string) *HumanWebAuthNWriteModel {
return &HumanWebAuthNWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
},
WebauthNTokenID: wbAuthNTokenID,
}
}
func (wm *HumanWebAuthNWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.HumanWebAuthNAddedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.AppendEvents(e)
}
case *user.HumanWebAuthNRemovedEvent:
if wm.WebauthNTokenID == e.WebAuthNTokenID {
wm.AppendEvents(e)
}
case *user.UserRemovedEvent:
wm.AppendEvents(e)
}
}
}
func (wm *HumanWebAuthNWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanWebAuthNAddedEvent:
wm.WebauthNTokenID = e.WebAuthNTokenID
wm.State = domain.WebAuthNStateActive
case *user.HumanWebAuthNRemovedEvent:
wm.State = domain.WebAuthNStateRemoved
case *user.UserRemovedEvent:
wm.State = domain.WebAuthNStateRemoved
}
}
return wm.WriteModel.Reduce()
}
func (wm *HumanWebAuthNWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
}

View File

@ -45,12 +45,12 @@ func (r *CommandSide) ChangeMachine(ctx context.Context, machine *domain.Machine
return nil, err
}
if existingUser.UserState == domain.UserStateDeleted || existingUser.UserState == domain.UserStateUnspecified {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-5M0od", "Errors.User.NotFound")
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-5M0od", "Errors.User.NotFound")
}
changedEvent, hasChanged := existingUser.NewChangedEvent(ctx, machine.Name, machine.Description)
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(changedEvent)

View File

@ -30,6 +30,8 @@ func (wm *MachineWriteModel) AppendEvents(events ...eventstore.EventReader) {
switch e := event.(type) {
case *user.MachineAddedEvent:
wm.AppendEvents(e)
case *user.UsernameChangedEvent:
wm.AppendEvents(e)
case *user.MachineChangedEvent:
wm.AppendEvents(e)
case *user.UserDeactivatedEvent:
@ -55,6 +57,8 @@ func (wm *MachineWriteModel) Reduce() error {
wm.Name = e.Name
wm.Description = e.Description
wm.UserState = domain.UserStateActive
case *user.UsernameChangedEvent:
wm.UserName = e.UserName
case *user.MachineChangedEvent:
if e.Name != nil {
wm.Name = *e.Name

View File

@ -1,9 +1,11 @@
package command
import (
caos_errors "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
"strings"
)
type UserWriteModel struct {
@ -24,14 +26,12 @@ func NewUserWriteModel(userID string) *UserWriteModel {
func (wm *UserWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.HumanEmailChangedEvent:
wm.AppendEvents(e)
case *user.HumanEmailVerifiedEvent:
wm.AppendEvents(e)
case *user.HumanAddedEvent, *user.HumanRegisteredEvent:
wm.AppendEvents(e)
case *user.MachineAddedEvent:
wm.AppendEvents(e)
case *user.UsernameChangedEvent:
wm.AppendEvents(e)
case *user.UserDeactivatedEvent:
wm.AppendEvents(e)
case *user.UserReactivatedEvent:
@ -56,10 +56,11 @@ func (wm *UserWriteModel) Reduce() error {
case *user.HumanRegisteredEvent:
wm.UserName = e.UserName
wm.UserState = domain.UserStateInitial
case *user.MachineAddedEvent:
wm.UserName = e.UserName
wm.UserState = domain.UserStateActive
case *user.UsernameChangedEvent:
wm.UserName = e.UserName
case *user.UserLockedEvent:
if wm.UserState != domain.UserStateDeleted {
wm.UserState = domain.UserStateLocked
@ -93,3 +94,13 @@ func UserAggregateFromWriteModel(wm *eventstore.WriteModel) *user.Aggregate {
Aggregate: *eventstore.AggregateFromWriteModel(wm, user.AggregateType, user.AggregateVersion),
}
}
func CheckOrgIAMPolicyForUserName(userName string, policy *domain.OrgIAMPolicy) error {
if policy == nil {
return caos_errors.ThrowPreconditionFailed(nil, "COMMAND-3Mb9s", "Errors.Users.OrgIamPolicyNil")
}
if policy.UserLoginMustBeDomain && strings.Contains(userName, "@") {
return caos_errors.ThrowPreconditionFailed(nil, "COMMAND-4M9vs", "Errors.User.EmailAsUsernameNotAllowed")
}
return nil
}

View File

@ -14,3 +14,17 @@ const (
MultiFactorTypeUnspecified MultiFactorType = iota
MultiFactorTypeU2FWithPIN
)
type FactorState int32
const (
FactorStateUnspecified FactorState = iota
FactorStateActive
FactorStateRemoved
factorStateCount
)
func (f FactorState) Valid() bool {
return f >= 0 && f < factorStateCount
}

View File

@ -71,7 +71,8 @@ func (u *Human) SetNamesAsDisplayname() {
func (u *Human) HashPasswordIfExisting(policy *PasswordComplexityPolicy, passwordAlg crypto.HashAlgorithm, onetime bool) error {
if u.Password != nil {
return u.Password.HashPasswordIfExisting(policy, passwordAlg, onetime)
u.Password.ChangeRequired = onetime
return u.Password.HashPasswordIfExisting(policy, passwordAlg)
}
return nil
}

View File

@ -11,3 +11,17 @@ type Address struct {
Region string
StreetAddress string
}
type AddressState int32
const (
AddressStateUnspecified AddressState = iota
AddressStateActive
AddressStateRemoved
addressStateCount
)
func (s AddressState) Valid() bool {
return s >= 0 && s < addressStateCount
}

View File

@ -23,3 +23,14 @@ type EmailCode struct {
func (e *Email) IsValid() bool {
return e.EmailAddress != ""
}
func NewEmailCode(emailGenerator crypto.Generator) (*EmailCode, error) {
emailCodeCrypto, _, err := crypto.NewCode(emailGenerator)
if err != nil {
return nil, err
}
return &EmailCode{
Code: emailCodeCrypto,
Expiry: emailGenerator.Expiry(),
}, nil
}

View File

@ -5,7 +5,25 @@ import es_models "github.com/caos/zitadel/internal/eventstore/models"
type ExternalIDP struct {
es_models.ObjectRoot
IDPConfigID string
UserID string
DisplayName string
IDPConfigID string
ExternalUserID string
DisplayName string
}
func (idp *ExternalIDP) IsValid() bool {
return idp.AggregateID != "" && idp.IDPConfigID != "" && idp.ExternalUserID != ""
}
type ExternalIDPState int32
const (
ExternalIDPStateUnspecified ExternalIDPState = iota
ExternalIDPStateActive
ExternalIDPStateRemoved
externalIDPStateCount
)
func (s ExternalIDPState) Valid() bool {
return s >= 0 && s < externalIDPStateCount
}

View File

@ -13,3 +13,17 @@ type OTP struct {
Url string
State MFAState
}
type OTPState int32
const (
OTPStateUnspecified OTPState = iota
OTPStateActive
OTPStateRemoved
otpStateCount
)
func (s OTPState) Valid() bool {
return s >= 0 && s < otpStateCount
}

View File

@ -23,7 +23,7 @@ type PasswordCode struct {
NotificationType NotificationType
}
func (p *Password) HashPasswordIfExisting(policy *PasswordComplexityPolicy, passwordAlg crypto.HashAlgorithm, onetime bool) error {
func (p *Password) HashPasswordIfExisting(policy *PasswordComplexityPolicy, passwordAlg crypto.HashAlgorithm) error {
if p.SecretString == "" {
return nil
}
@ -38,6 +38,16 @@ func (p *Password) HashPasswordIfExisting(policy *PasswordComplexityPolicy, pass
return err
}
p.SecretCrypto = secret
p.ChangeRequired = onetime
return nil
}
func NewPasswordCode(passwordGenerator crypto.Generator) (*PasswordCode, error) {
passwordCodeCrypto, _, err := crypto.NewCode(passwordGenerator)
if err != nil {
return nil, err
}
return &PasswordCode{
Code: passwordCodeCrypto,
Expiry: passwordGenerator.Expiry(),
}, nil
}

View File

@ -50,3 +50,17 @@ func NewPhoneCode(phoneGenerator crypto.Generator) (*PhoneCode, error) {
Expiry: phoneGenerator.Expiry(),
}, nil
}
type PhoneState int32
const (
PhoneStateUnspecified PhoneState = iota
PhoneStateActive
PhoneStateRemoved
phoneStateCount
)
func (s PhoneState) Valid() bool {
return s >= 0 && s < phoneStateCount
}

View File

@ -38,3 +38,17 @@ const (
UserVerificationRequirementPreferred
UserVerificationRequirementDiscouraged
)
type WebAuthNState int32
const (
WebAuthNStateUnspecified WebAuthNState = iota
WebAuthNStateActive
WebAuthNStateRemoved
webAuthNStateCount
)
func (s WebAuthNState) Valid() bool {
return s >= 0 && s < webAuthNStateCount
}

View File

@ -14,3 +14,17 @@ type IAMMember struct {
func (i *IAMMember) IsValid() bool {
return i.AggregateID != "" && i.UserID != "" && len(i.Roles) != 0
}
type MemberState int32
const (
MemberStateUnspecified MemberState = iota
MemberStateActive
MemberStateRemoved
memberStateCount
)
func (f MemberState) Valid() bool {
return f >= 0 && f < memberStateCount
}

View File

@ -0,0 +1,15 @@
package domain
type PolicyState int32
const (
PolicyStateUnspecified PolicyState = iota
PolicyStateActive
PolicyStateRemoved
policyStateCount
)
func (f PolicyState) Valid() bool {
return f >= 0 && f < policyStateCount
}

View File

@ -12,3 +12,17 @@ const (
func (f IdentityProviderType) Valid() bool {
return f >= 0 && f < identityProviderCount
}
type IdentityProviderState int32
const (
IdentityProviderStateUnspecified IdentityProviderState = iota
IdentityProviderStateActive
IdentityProviderStateRemoved
idpProviderState
)
func (s IdentityProviderState) Valid() bool {
return s >= 0 && s < idpProviderState
}

View File

@ -61,6 +61,7 @@ type HumanExternalIDPAddedEvent struct {
eventstore.BaseEvent `json:"-"`
IDPConfigID string `json:"idpConfigId,omitempty"`
UserID string `json:"userId,omitempty"`
DisplayName string `json:"displayName,omitempty"`
}
@ -96,19 +97,21 @@ type HumanExternalIDPRemovedEvent struct {
eventstore.BaseEvent `json:"-"`
IDPConfigID string `json:"idpConfigId"`
UserID string `json:"userId,omitempty"`
}
func (e *HumanExternalIDPRemovedEvent) Data() interface{} {
return e
}
func NewHumanExternalIDPRemovedEvent(ctx context.Context, idpConfigID string) *HumanExternalIDPRemovedEvent {
func NewHumanExternalIDPRemovedEvent(ctx context.Context, idpConfigID, externalUserID string) *HumanExternalIDPRemovedEvent {
return &HumanExternalIDPRemovedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanExternalIDPRemovedType,
),
IDPConfigID: idpConfigID,
UserID: externalUserID,
}
}
@ -129,19 +132,21 @@ type HumanExternalIDPCascadeRemovedEvent struct {
eventstore.BaseEvent `json:"-"`
IDPConfigID string `json:"idpConfigId"`
UserID string `json:"userId,omitempty"`
}
func (e *HumanExternalIDPCascadeRemovedEvent) Data() interface{} {
return e
}
func NewHumanExternalIDPCascadeRemovedEvent(ctx context.Context, idpConfigID string) *HumanExternalIDPCascadeRemovedEvent {
func NewHumanExternalIDPCascadeRemovedEvent(ctx context.Context, idpConfigID, externalUserID string) *HumanExternalIDPCascadeRemovedEvent {
return &HumanExternalIDPCascadeRemovedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanExternalIDPCascadeRemovedType,
),
IDPConfigID: idpConfigID,
UserID: externalUserID,
}
}

View File

@ -7,7 +7,6 @@ import (
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/eventstore/v2/repository"
"github.com/caos/zitadel/internal/v2/domain"
)
const (
@ -23,7 +22,6 @@ type HumanOTPAddedEvent struct {
eventstore.BaseEvent `json:"-"`
Secret *crypto.CryptoValue `json:"otpSecret,omitempty"`
State domain.MFAState `json:"-"`
}
func (e *HumanOTPAddedEvent) Data() interface{} {
@ -44,7 +42,6 @@ func NewHumanOTPAddedEvent(ctx context.Context,
func HumanOTPAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
otpAdded := &HumanOTPAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
State: domain.MFAStateNotReady,
}
err := json.Unmarshal(event.Data, otpAdded)
if err != nil {
@ -55,7 +52,6 @@ func HumanOTPAddedEventMapper(event *repository.Event) (eventstore.EventReader,
type HumanOTPVerifiedEvent struct {
eventstore.BaseEvent `json:"-"`
State domain.MFAState `json:"-"`
}
func (e *HumanOTPVerifiedEvent) Data() interface{} {
@ -74,7 +70,6 @@ func NewHumanOTPVerifiedEvent(ctx context.Context) *HumanOTPVerifiedEvent {
func HumanOTPVerifiedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
return &HumanOTPVerifiedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
State: domain.MFAStateReady,
}, nil
}

View File

@ -25,6 +25,7 @@ type HumanPasswordChangedEvent struct {
Secret *crypto.CryptoValue `json:"secret,omitempty"`
ChangeRequired bool `json:"changeRequired"`
UserAgentID string `json:"userAgentID,omitempty"`
}
func (e *HumanPasswordChangedEvent) Data() interface{} {
@ -35,6 +36,7 @@ func NewHumanPasswordChangedEvent(
ctx context.Context,
secret *crypto.CryptoValue,
changeRequired bool,
userAgentID string,
) *HumanPasswordChangedEvent {
return &HumanPasswordChangedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
@ -43,6 +45,7 @@ func NewHumanPasswordChangedEvent(
),
Secret: secret,
ChangeRequired: changeRequired,
UserAgentID: userAgentID,
}
}

View File

@ -30,13 +30,12 @@ func (e *HumanPhoneChangedEvent) Data() interface{} {
return e
}
func NewHumanPhoneChangedEvent(ctx context.Context, phone string) *HumanPhoneChangedEvent {
func NewHumanPhoneChangedEvent(ctx context.Context) *HumanPhoneChangedEvent {
return &HumanPhoneChangedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
HumanPhoneChangedType,
),
PhoneNumber: phone,
}
}