feat: New event user (#1156)

* feat: change user command side

* feat: change user command side

* feat: use states on write model

* feat: command and query side in auth api

* feat: auth commands

* feat: check external idp id

* feat: user state check

* fix: error messages

* fix: is active state
This commit is contained in:
Fabi
2021-01-07 16:06:45 +01:00
committed by GitHub
parent 65a8efeb0e
commit 26c8113930
71 changed files with 1242 additions and 442 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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