mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 16:57:23 +00:00
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:
parent
65a8efeb0e
commit
26c8113930
@ -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)
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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("")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)))
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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) }()
|
||||
|
@ -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
|
||||
}
|
||||
|
52
internal/v2/command/user_human_externalidp.go
Normal file
52
internal/v2/command/user_human_externalidp.go
Normal 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
|
||||
}
|
72
internal/v2/command/user_human_externalidp_model.go
Normal file
72
internal/v2/command/user_human_externalidp_model.go
Normal 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)
|
||||
}
|
@ -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:
|
||||
|
41
internal/v2/command/user_human_otp.go
Normal file
41
internal/v2/command/user_human_otp.go
Normal 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
|
||||
}
|
57
internal/v2/command/user_human_otp_model.go
Normal file
57
internal/v2/command/user_human_otp_model.go
Normal 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)
|
||||
}
|
107
internal/v2/command/user_human_password.go
Normal file
107
internal/v2/command/user_human_password.go
Normal 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
|
||||
}
|
70
internal/v2/command/user_human_password_model.go
Normal file
70
internal/v2/command/user_human_password_model.go
Normal 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)
|
||||
}
|
101
internal/v2/command/user_human_phone.go
Normal file
101
internal/v2/command/user_human_phone.go
Normal 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
|
||||
}
|
88
internal/v2/command/user_human_phone_model.go
Normal file
88
internal/v2/command/user_human_phone_model.go
Normal 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
|
||||
}
|
@ -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)
|
||||
|
50
internal/v2/command/user_human_webauthn.go
Normal file
50
internal/v2/command/user_human_webauthn.go
Normal 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
|
||||
}
|
61
internal/v2/command/user_human_webauthn_model.go
Normal file
61
internal/v2/command/user_human_webauthn_model.go
Normal 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)
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
15
internal/v2/domain/policy.go
Normal file
15
internal/v2/domain/policy.go
Normal 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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user