mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 22:37:24 +00:00
fix: make user creation errors helpful (#5382)
* fix: make user creation errors helpful * fix linting and unit testing errors * fix linting * make zitadel config reusable * fix human validations * translate ssr errors * make zitadel config reusable * cover more translations for ssr * handle email validation message centrally * fix unit tests * fix linting * align signatures * use more precise wording * handle phone validation message centrally * fix: return specific profile errors * docs: edit comments * fix unit tests --------- Co-authored-by: Silvan <silvan.reusser@gmail.com>
This commit is contained in:
parent
9ff810eb92
commit
e00cc187fa
@ -91,11 +91,11 @@ func (mig *FirstInstance) Execute(ctx context.Context) error {
|
|||||||
if !mig.instanceSetup.DomainPolicy.UserLoginMustBeDomain && !strings.Contains(mig.instanceSetup.Org.Human.Username, "@") {
|
if !mig.instanceSetup.DomainPolicy.UserLoginMustBeDomain && !strings.Contains(mig.instanceSetup.Org.Human.Username, "@") {
|
||||||
mig.instanceSetup.Org.Human.Username = mig.instanceSetup.Org.Human.Username + "@" + domain.NewIAMDomainName(mig.instanceSetup.Org.Name, mig.instanceSetup.CustomDomain)
|
mig.instanceSetup.Org.Human.Username = mig.instanceSetup.Org.Human.Username + "@" + domain.NewIAMDomainName(mig.instanceSetup.Org.Name, mig.instanceSetup.CustomDomain)
|
||||||
}
|
}
|
||||||
mig.instanceSetup.Org.Human.Email.Address = strings.TrimSpace(mig.instanceSetup.Org.Human.Email.Address)
|
mig.instanceSetup.Org.Human.Email.Address = mig.instanceSetup.Org.Human.Email.Address.Normalize()
|
||||||
if mig.instanceSetup.Org.Human.Email.Address == "" {
|
if mig.instanceSetup.Org.Human.Email.Address == "" {
|
||||||
mig.instanceSetup.Org.Human.Email.Address = mig.instanceSetup.Org.Human.Username
|
mig.instanceSetup.Org.Human.Email.Address = domain.EmailAddress(mig.instanceSetup.Org.Human.Username)
|
||||||
if !strings.Contains(mig.instanceSetup.Org.Human.Email.Address, "@") {
|
if !strings.Contains(string(mig.instanceSetup.Org.Human.Email.Address), "@") {
|
||||||
mig.instanceSetup.Org.Human.Email.Address = mig.instanceSetup.Org.Human.Username + "@" + domain.NewIAMDomainName(mig.instanceSetup.Org.Name, mig.instanceSetup.CustomDomain)
|
mig.instanceSetup.Org.Human.Email.Address = domain.EmailAddress(mig.instanceSetup.Org.Human.Username + "@" + domain.NewIAMDomainName(mig.instanceSetup.Org.Name, mig.instanceSetup.CustomDomain))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,8 @@ ExternalSecure: false
|
|||||||
|
|
||||||
Database:
|
Database:
|
||||||
cockroach:
|
cockroach:
|
||||||
Host: db
|
# This makes the e2e config reusable with an out-of-docker zitadel process and an /etc/hosts entry
|
||||||
|
Host: host.docker.internal
|
||||||
|
|
||||||
TLS:
|
TLS:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
@ -3,7 +3,8 @@ ExternalSecure: false
|
|||||||
|
|
||||||
Database:
|
Database:
|
||||||
cockroach:
|
cockroach:
|
||||||
Host: db
|
# This makes the e2e config reusable with an out-of-docker zitadel process and an /etc/hosts entry
|
||||||
|
Host: host.docker.internal
|
||||||
|
|
||||||
TLS:
|
TLS:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
@ -156,9 +156,9 @@ type human struct {
|
|||||||
AvatarKey string
|
AvatarKey string
|
||||||
PreferredLanguage string
|
PreferredLanguage string
|
||||||
Gender domain.Gender
|
Gender domain.Gender
|
||||||
Email string
|
Email domain.EmailAddress
|
||||||
IsEmailVerified bool
|
IsEmailVerified bool
|
||||||
Phone string
|
Phone domain.PhoneNumber
|
||||||
IsPhoneVerified bool
|
IsPhoneVerified bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,13 +564,13 @@ func (s *Server) getUsers(ctx context.Context, org string, withPasswords bool, w
|
|||||||
}
|
}
|
||||||
if user.Human.Email != "" {
|
if user.Human.Email != "" {
|
||||||
dataUser.User.Email = &management_pb.ImportHumanUserRequest_Email{
|
dataUser.User.Email = &management_pb.ImportHumanUserRequest_Email{
|
||||||
Email: user.Human.Email,
|
Email: string(user.Human.Email),
|
||||||
IsEmailVerified: user.Human.IsEmailVerified,
|
IsEmailVerified: user.Human.IsEmailVerified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if user.Human.Phone != "" {
|
if user.Human.Phone != "" {
|
||||||
dataUser.User.Phone = &management_pb.ImportHumanUserRequest_Phone{
|
dataUser.User.Phone = &management_pb.ImportHumanUserRequest_Phone{
|
||||||
Phone: user.Human.Phone,
|
Phone: string(user.Human.Phone),
|
||||||
IsPhoneVerified: user.Human.IsPhoneVerified,
|
IsPhoneVerified: user.Human.IsPhoneVerified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
user_grpc "github.com/zitadel/zitadel/internal/api/grpc/user"
|
user_grpc "github.com/zitadel/zitadel/internal/api/grpc/user"
|
||||||
"github.com/zitadel/zitadel/internal/command"
|
"github.com/zitadel/zitadel/internal/command"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
admin_grpc "github.com/zitadel/zitadel/pkg/grpc/admin"
|
admin_grpc "github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ func setUpOrgHumanToCommand(human *admin_grpc.SetUpOrgRequest_Human) *command.Ad
|
|||||||
|
|
||||||
func setUpOrgHumanEmailToDomain(email *admin_grpc.SetUpOrgRequest_Human_Email) command.Email {
|
func setUpOrgHumanEmailToDomain(email *admin_grpc.SetUpOrgRequest_Human_Email) command.Email {
|
||||||
return command.Email{
|
return command.Email{
|
||||||
Address: email.Email,
|
Address: domain.EmailAddress(email.Email),
|
||||||
Verified: email.IsEmailVerified,
|
Verified: email.IsEmailVerified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,7 +40,7 @@ func setUpOrgHumanPhoneToDomain(phone *admin_grpc.SetUpOrgRequest_Human_Phone) c
|
|||||||
return command.Phone{}
|
return command.Phone{}
|
||||||
}
|
}
|
||||||
return command.Phone{
|
return command.Phone{
|
||||||
Number: phone.Phone,
|
Number: domain.PhoneNumber(phone.Phone),
|
||||||
Verified: phone.IsPhoneVerified,
|
Verified: phone.IsPhoneVerified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,6 @@ import (
|
|||||||
func UpdateMyEmailToDomain(ctx context.Context, email *auth.SetMyEmailRequest) *domain.Email {
|
func UpdateMyEmailToDomain(ctx context.Context, email *auth.SetMyEmailRequest) *domain.Email {
|
||||||
return &domain.Email{
|
return &domain.Email{
|
||||||
ObjectRoot: ctxToObjectRoot(ctx),
|
ObjectRoot: ctxToObjectRoot(ctx),
|
||||||
EmailAddress: email.Email,
|
EmailAddress: domain.EmailAddress(email.Email),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,6 @@ import (
|
|||||||
func UpdateMyPhoneToDomain(ctx context.Context, phone *auth.SetMyPhoneRequest) *domain.Phone {
|
func UpdateMyPhoneToDomain(ctx context.Context, phone *auth.SetMyPhoneRequest) *domain.Phone {
|
||||||
return &domain.Phone{
|
return &domain.Phone{
|
||||||
ObjectRoot: ctxToObjectRoot(ctx),
|
ObjectRoot: ctxToObjectRoot(ctx),
|
||||||
PhoneNumber: phone.Phone,
|
PhoneNumber: domain.PhoneNumber(phone.Phone),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,7 @@ func AddHumanUserRequestToAddHuman(req *mgmt_pb.AddHumanUserRequest) *command.Ad
|
|||||||
NickName: req.Profile.NickName,
|
NickName: req.Profile.NickName,
|
||||||
DisplayName: req.Profile.DisplayName,
|
DisplayName: req.Profile.DisplayName,
|
||||||
Email: command.Email{
|
Email: command.Email{
|
||||||
Address: req.Email.Email,
|
Address: domain.EmailAddress(req.Email.Email),
|
||||||
Verified: req.Email.IsEmailVerified,
|
Verified: req.Email.IsEmailVerified,
|
||||||
},
|
},
|
||||||
PreferredLanguage: lang,
|
PreferredLanguage: lang,
|
||||||
@ -221,7 +221,7 @@ func AddHumanUserRequestToAddHuman(req *mgmt_pb.AddHumanUserRequest) *command.Ad
|
|||||||
}
|
}
|
||||||
if req.Phone != nil {
|
if req.Phone != nil {
|
||||||
human.Phone = command.Phone{
|
human.Phone = command.Phone{
|
||||||
Number: req.Phone.Phone,
|
Number: domain.PhoneNumber(req.Phone.Phone),
|
||||||
Verified: req.Phone.IsPhoneVerified,
|
Verified: req.Phone.IsPhoneVerified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -446,7 +446,7 @@ func (s *Server) ResendHumanInitialization(ctx context.Context, req *mgmt_pb.Res
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
details, err := s.command.ResendInitialMail(ctx, req.UserId, req.Email, authz.GetCtxData(ctx).OrgID, initCodeGenerator)
|
details, err := s.command.ResendInitialMail(ctx, req.UserId, domain.EmailAddress(req.Email), authz.GetCtxData(ctx).OrgID, initCodeGenerator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -91,37 +91,6 @@ func ListUserMetadataToDomain(req *mgmt_pb.ListUserMetadataRequest) (*query.User
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddHumanUserRequestToDomain(req *mgmt_pb.AddHumanUserRequest) *domain.Human {
|
|
||||||
h := &domain.Human{
|
|
||||||
Username: req.UserName,
|
|
||||||
}
|
|
||||||
preferredLanguage, err := language.Parse(req.Profile.PreferredLanguage)
|
|
||||||
logging.Log("MANAG-M029f").OnError(err).Debug("language malformed")
|
|
||||||
h.Profile = &domain.Profile{
|
|
||||||
FirstName: req.Profile.FirstName,
|
|
||||||
LastName: req.Profile.LastName,
|
|
||||||
NickName: req.Profile.NickName,
|
|
||||||
DisplayName: req.Profile.DisplayName,
|
|
||||||
PreferredLanguage: preferredLanguage,
|
|
||||||
Gender: user_grpc.GenderToDomain(req.Profile.Gender),
|
|
||||||
}
|
|
||||||
h.Email = &domain.Email{
|
|
||||||
EmailAddress: req.Email.Email,
|
|
||||||
IsEmailVerified: req.Email.IsEmailVerified,
|
|
||||||
}
|
|
||||||
if req.Phone != nil {
|
|
||||||
h.Phone = &domain.Phone{
|
|
||||||
PhoneNumber: req.Phone.Phone,
|
|
||||||
IsPhoneVerified: req.Phone.IsPhoneVerified,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if req.InitialPassword != "" {
|
|
||||||
h.Password = &domain.Password{SecretString: req.InitialPassword, ChangeRequired: true}
|
|
||||||
}
|
|
||||||
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func ImportHumanUserRequestToDomain(req *mgmt_pb.ImportHumanUserRequest) (human *domain.Human, passwordless bool, links []*domain.UserIDPLink) {
|
func ImportHumanUserRequestToDomain(req *mgmt_pb.ImportHumanUserRequest) (human *domain.Human, passwordless bool, links []*domain.UserIDPLink) {
|
||||||
human = &domain.Human{
|
human = &domain.Human{
|
||||||
Username: req.UserName,
|
Username: req.UserName,
|
||||||
@ -137,12 +106,12 @@ func ImportHumanUserRequestToDomain(req *mgmt_pb.ImportHumanUserRequest) (human
|
|||||||
Gender: user_grpc.GenderToDomain(req.Profile.Gender),
|
Gender: user_grpc.GenderToDomain(req.Profile.Gender),
|
||||||
}
|
}
|
||||||
human.Email = &domain.Email{
|
human.Email = &domain.Email{
|
||||||
EmailAddress: req.Email.Email,
|
EmailAddress: domain.EmailAddress(req.Email.Email),
|
||||||
IsEmailVerified: req.Email.IsEmailVerified,
|
IsEmailVerified: req.Email.IsEmailVerified,
|
||||||
}
|
}
|
||||||
if req.Phone != nil {
|
if req.Phone != nil {
|
||||||
human.Phone = &domain.Phone{
|
human.Phone = &domain.Phone{
|
||||||
PhoneNumber: req.Phone.Phone,
|
PhoneNumber: domain.PhoneNumber(req.Phone.Phone),
|
||||||
IsPhoneVerified: req.Phone.IsPhoneVerified,
|
IsPhoneVerified: req.Phone.IsPhoneVerified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,7 +168,7 @@ func UpdateHumanEmailRequestToDomain(ctx context.Context, req *mgmt_pb.UpdateHum
|
|||||||
AggregateID: req.UserId,
|
AggregateID: req.UserId,
|
||||||
ResourceOwner: authz.GetCtxData(ctx).OrgID,
|
ResourceOwner: authz.GetCtxData(ctx).OrgID,
|
||||||
},
|
},
|
||||||
EmailAddress: req.Email,
|
EmailAddress: domain.EmailAddress(req.Email),
|
||||||
IsEmailVerified: req.IsEmailVerified,
|
IsEmailVerified: req.IsEmailVerified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,7 +176,7 @@ func UpdateHumanEmailRequestToDomain(ctx context.Context, req *mgmt_pb.UpdateHum
|
|||||||
func UpdateHumanPhoneRequestToDomain(req *mgmt_pb.UpdateHumanPhoneRequest) *domain.Phone {
|
func UpdateHumanPhoneRequestToDomain(req *mgmt_pb.UpdateHumanPhoneRequest) *domain.Phone {
|
||||||
return &domain.Phone{
|
return &domain.Phone{
|
||||||
ObjectRoot: models.ObjectRoot{AggregateID: req.UserId},
|
ObjectRoot: models.ObjectRoot{AggregateID: req.UserId},
|
||||||
PhoneNumber: req.Phone,
|
PhoneNumber: domain.PhoneNumber(req.Phone),
|
||||||
IsPhoneVerified: req.IsPhoneVerified,
|
IsPhoneVerified: req.IsPhoneVerified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ func CreateInstancePbToSetupInstance(req *system_pb.CreateInstanceRequest, defau
|
|||||||
func createInstancePbToAddHuman(req *system_pb.CreateInstanceRequest_Human, defaultHuman command.AddHuman, userLoginMustBeDomain bool, org, externalDomain string) *command.AddHuman {
|
func createInstancePbToAddHuman(req *system_pb.CreateInstanceRequest_Human, defaultHuman command.AddHuman, userLoginMustBeDomain bool, org, externalDomain string) *command.AddHuman {
|
||||||
user := defaultHuman
|
user := defaultHuman
|
||||||
if req.Email != nil {
|
if req.Email != nil {
|
||||||
user.Email.Address = req.Email.Email
|
user.Email.Address = domain.EmailAddress(req.Email.Email)
|
||||||
user.Email.Verified = req.Email.IsEmailVerified
|
user.Email.Verified = req.Email.IsEmailVerified
|
||||||
}
|
}
|
||||||
if req.Profile != nil {
|
if req.Profile != nil {
|
||||||
@ -164,7 +164,7 @@ func AddInstancePbToSetupInstance(req *system_pb.AddInstanceRequest, defaultInst
|
|||||||
instance.Org.Human = new(command.AddHuman)
|
instance.Org.Human = new(command.AddHuman)
|
||||||
}
|
}
|
||||||
if req.OwnerEmail.Email != "" {
|
if req.OwnerEmail.Email != "" {
|
||||||
instance.Org.Human.Email.Address = req.OwnerEmail.Email
|
instance.Org.Human.Email.Address = domain.EmailAddress(req.OwnerEmail.Email)
|
||||||
instance.Org.Human.Email.Verified = req.OwnerEmail.IsEmailVerified
|
instance.Org.Human.Email.Verified = req.OwnerEmail.IsEmailVerified
|
||||||
}
|
}
|
||||||
if req.OwnerProfile != nil {
|
if req.OwnerProfile != nil {
|
||||||
|
@ -58,11 +58,11 @@ func HumanToPb(view *query.Human, assetPrefix, owner string) *user_pb.Human {
|
|||||||
AvatarUrl: domain.AvatarURL(assetPrefix, owner, view.AvatarKey),
|
AvatarUrl: domain.AvatarURL(assetPrefix, owner, view.AvatarKey),
|
||||||
},
|
},
|
||||||
Email: &user_pb.Email{
|
Email: &user_pb.Email{
|
||||||
Email: view.Email,
|
Email: string(view.Email),
|
||||||
IsEmailVerified: view.IsEmailVerified,
|
IsEmailVerified: view.IsEmailVerified,
|
||||||
},
|
},
|
||||||
Phone: &user_pb.Phone{
|
Phone: &user_pb.Phone{
|
||||||
Phone: view.Phone,
|
Phone: string(view.Phone),
|
||||||
IsPhoneVerified: view.IsPhoneVerified,
|
IsPhoneVerified: view.IsPhoneVerified,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -91,7 +91,7 @@ func ProfileToPb(profile *query.Profile, assetPrefix string) *user_pb.Profile {
|
|||||||
|
|
||||||
func EmailToPb(email *query.Email) *user_pb.Email {
|
func EmailToPb(email *query.Email) *user_pb.Email {
|
||||||
return &user_pb.Email{
|
return &user_pb.Email{
|
||||||
Email: email.Email,
|
Email: string(email.Email),
|
||||||
IsEmailVerified: email.IsVerified,
|
IsEmailVerified: email.IsVerified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ func PhoneToPb(phone *query.Phone) *user_pb.Phone {
|
|||||||
|
|
||||||
func ModelEmailToPb(email *query.Email) *user_pb.Email {
|
func ModelEmailToPb(email *query.Email) *user_pb.Email {
|
||||||
return &user_pb.Email{
|
return &user_pb.Email{
|
||||||
Email: email.Email,
|
Email: string(email.Email),
|
||||||
IsEmailVerified: email.IsVerified,
|
IsEmailVerified: email.IsVerified,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,7 +268,7 @@ func (o *OPStorage) setUserinfo(ctx context.Context, userInfo oidc.UserInfoSette
|
|||||||
if user.Human == nil {
|
if user.Human == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
userInfo.SetEmail(user.Human.Email, user.Human.IsEmailVerified)
|
userInfo.SetEmail(string(user.Human.Email), user.Human.IsEmailVerified)
|
||||||
case oidc.ScopeProfile:
|
case oidc.ScopeProfile:
|
||||||
userInfo.SetPreferredUsername(user.PreferredLoginName)
|
userInfo.SetPreferredUsername(user.PreferredLoginName)
|
||||||
userInfo.SetUpdatedAt(user.ChangeDate)
|
userInfo.SetUpdatedAt(user.ChangeDate)
|
||||||
@ -287,7 +287,7 @@ func (o *OPStorage) setUserinfo(ctx context.Context, userInfo oidc.UserInfoSette
|
|||||||
if user.Human == nil {
|
if user.Human == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
userInfo.SetPhone(user.Human.Phone, user.Human.IsPhoneVerified)
|
userInfo.SetPhone(string(user.Human.Phone), user.Human.IsPhoneVerified)
|
||||||
case oidc.ScopeAddress:
|
case oidc.ScopeAddress:
|
||||||
//TODO: handle address for human users as soon as implemented
|
//TODO: handle address for human users as soon as implemented
|
||||||
case ScopeUserMetaData:
|
case ScopeUserMetaData:
|
||||||
|
@ -154,7 +154,7 @@ func setUserinfo(user *query.User, userinfo models.AttributeSetter, attributes [
|
|||||||
if user.Human == nil {
|
if user.Human == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userinfo.SetEmail(user.Human.Email)
|
userinfo.SetEmail(string(user.Human.Email))
|
||||||
userinfo.SetSurname(user.Human.LastName)
|
userinfo.SetSurname(user.Human.LastName)
|
||||||
userinfo.SetGivenName(user.Human.FirstName)
|
userinfo.SetGivenName(user.Human.FirstName)
|
||||||
userinfo.SetFullName(user.Human.DisplayName)
|
userinfo.SetFullName(user.Human.DisplayName)
|
||||||
@ -164,7 +164,7 @@ func setUserinfo(user *query.User, userinfo models.AttributeSetter, attributes [
|
|||||||
switch attribute {
|
switch attribute {
|
||||||
case provider.AttributeEmail:
|
case provider.AttributeEmail:
|
||||||
if user.Human != nil {
|
if user.Human != nil {
|
||||||
userinfo.SetEmail(user.Human.Email)
|
userinfo.SetEmail(string(user.Human.Email))
|
||||||
}
|
}
|
||||||
case provider.AttributeSurname:
|
case provider.AttributeSurname:
|
||||||
if user.Human != nil {
|
if user.Human != nil {
|
||||||
|
@ -55,13 +55,13 @@ func (l *Login) runPostExternalAuthenticationActions(
|
|||||||
actions.SetFields("setPreferredUsername", func(username string) {
|
actions.SetFields("setPreferredUsername", func(username string) {
|
||||||
user.PreferredUsername = username
|
user.PreferredUsername = username
|
||||||
}),
|
}),
|
||||||
actions.SetFields("setEmail", func(email string) {
|
actions.SetFields("setEmail", func(email domain.EmailAddress) {
|
||||||
user.Email = email
|
user.Email = email
|
||||||
}),
|
}),
|
||||||
actions.SetFields("setEmailVerified", func(verified bool) {
|
actions.SetFields("setEmailVerified", func(verified bool) {
|
||||||
user.IsEmailVerified = verified
|
user.IsEmailVerified = verified
|
||||||
}),
|
}),
|
||||||
actions.SetFields("setPhone", func(phone string) {
|
actions.SetFields("setPhone", func(phone domain.PhoneNumber) {
|
||||||
user.Phone = phone
|
user.Phone = phone
|
||||||
}),
|
}),
|
||||||
actions.SetFields("setPhoneVerified", func(verified bool) {
|
actions.SetFields("setPhoneVerified", func(verified bool) {
|
||||||
@ -222,7 +222,7 @@ func (l *Login) runPreCreationActions(
|
|||||||
actions.SetFields("setUsername", func(username string) {
|
actions.SetFields("setUsername", func(username string) {
|
||||||
user.Username = username
|
user.Username = username
|
||||||
}),
|
}),
|
||||||
actions.SetFields("setEmail", func(email string) {
|
actions.SetFields("setEmail", func(email domain.EmailAddress) {
|
||||||
if user.Email == nil {
|
if user.Email == nil {
|
||||||
user.Email = &domain.Email{}
|
user.Email = &domain.Email{}
|
||||||
}
|
}
|
||||||
@ -234,11 +234,11 @@ func (l *Login) runPreCreationActions(
|
|||||||
}
|
}
|
||||||
user.Email.IsEmailVerified = verified
|
user.Email.IsEmailVerified = verified
|
||||||
}),
|
}),
|
||||||
actions.SetFields("setPhone", func(email string) {
|
actions.SetFields("setPhone", func(phone domain.PhoneNumber) {
|
||||||
if user.Phone == nil {
|
if user.Phone == nil {
|
||||||
user.Phone = &domain.Phone{}
|
user.Phone = &domain.Phone{}
|
||||||
}
|
}
|
||||||
user.Phone.PhoneNumber = email
|
user.Phone.PhoneNumber = phone
|
||||||
}),
|
}),
|
||||||
actions.SetFields("setPhoneVerified", func(verified bool) {
|
actions.SetFields("setPhoneVerified", func(verified bool) {
|
||||||
if user.Phone == nil {
|
if user.Phone == nil {
|
||||||
|
@ -60,28 +60,28 @@ type externalNotFoundOptionData struct {
|
|||||||
ShowUsername bool
|
ShowUsername bool
|
||||||
ShowUsernameSuffix bool
|
ShowUsernameSuffix bool
|
||||||
OrgRegister bool
|
OrgRegister bool
|
||||||
ExternalEmail string
|
ExternalEmail domain.EmailAddress
|
||||||
ExternalEmailVerified bool
|
ExternalEmailVerified bool
|
||||||
ExternalPhone string
|
ExternalPhone domain.PhoneNumber
|
||||||
ExternalPhoneVerified bool
|
ExternalPhoneVerified bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type externalRegisterFormData struct {
|
type externalRegisterFormData struct {
|
||||||
ExternalIDPConfigID string `schema:"external-idp-config-id"`
|
ExternalIDPConfigID string `schema:"external-idp-config-id"`
|
||||||
ExternalIDPExtUserID string `schema:"external-idp-ext-user-id"`
|
ExternalIDPExtUserID string `schema:"external-idp-ext-user-id"`
|
||||||
ExternalIDPDisplayName string `schema:"external-idp-display-name"`
|
ExternalIDPDisplayName string `schema:"external-idp-display-name"`
|
||||||
ExternalEmail string `schema:"external-email"`
|
ExternalEmail domain.EmailAddress `schema:"external-email"`
|
||||||
ExternalEmailVerified bool `schema:"external-email-verified"`
|
ExternalEmailVerified bool `schema:"external-email-verified"`
|
||||||
Email string `schema:"email"`
|
Email domain.EmailAddress `schema:"email"`
|
||||||
Username string `schema:"username"`
|
Username string `schema:"username"`
|
||||||
Firstname string `schema:"firstname"`
|
Firstname string `schema:"firstname"`
|
||||||
Lastname string `schema:"lastname"`
|
Lastname string `schema:"lastname"`
|
||||||
Nickname string `schema:"nickname"`
|
Nickname string `schema:"nickname"`
|
||||||
ExternalPhone string `schema:"external-phone"`
|
ExternalPhone domain.PhoneNumber `schema:"external-phone"`
|
||||||
ExternalPhoneVerified bool `schema:"external-phone-verified"`
|
ExternalPhoneVerified bool `schema:"external-phone-verified"`
|
||||||
Phone string `schema:"phone"`
|
Phone domain.PhoneNumber `schema:"phone"`
|
||||||
Language string `schema:"language"`
|
Language string `schema:"language"`
|
||||||
TermsConfirm bool `schema:"terms-confirm"`
|
TermsConfirm bool `schema:"terms-confirm"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleExternalLoginStep is called as nextStep
|
// handleExternalLoginStep is called as nextStep
|
||||||
@ -815,7 +815,7 @@ func mapExternalNotFoundOptionFormDataToLoginUser(formData *externalNotFoundOpti
|
|||||||
IDPConfigID: formData.ExternalIDPConfigID,
|
IDPConfigID: formData.ExternalIDPConfigID,
|
||||||
ExternalUserID: formData.ExternalIDPExtUserID,
|
ExternalUserID: formData.ExternalIDPExtUserID,
|
||||||
PreferredUsername: formData.Username,
|
PreferredUsername: formData.Username,
|
||||||
DisplayName: formData.Email,
|
DisplayName: string(formData.Email),
|
||||||
FirstName: formData.Firstname,
|
FirstName: formData.Firstname,
|
||||||
LastName: formData.Lastname,
|
LastName: formData.Lastname,
|
||||||
NickName: formData.Nickname,
|
NickName: formData.Nickname,
|
||||||
|
@ -16,14 +16,14 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type registerFormData struct {
|
type registerFormData struct {
|
||||||
Email string `schema:"email"`
|
Email domain.EmailAddress `schema:"email"`
|
||||||
Username string `schema:"username"`
|
Username string `schema:"username"`
|
||||||
Firstname string `schema:"firstname"`
|
Firstname string `schema:"firstname"`
|
||||||
Lastname string `schema:"lastname"`
|
Lastname string `schema:"lastname"`
|
||||||
Language string `schema:"language"`
|
Language string `schema:"language"`
|
||||||
Password string `schema:"register-password"`
|
Password string `schema:"register-password"`
|
||||||
Password2 string `schema:"register-password-confirmation"`
|
Password2 string `schema:"register-password-confirmation"`
|
||||||
TermsConfirm bool `schema:"terms-confirm"`
|
TermsConfirm bool `schema:"terms-confirm"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type registerData struct {
|
type registerData struct {
|
||||||
|
@ -94,7 +94,7 @@ func (l *Login) passLoginHintToRegistration(r *http.Request, authReq *domain.Aut
|
|||||||
if authReq == nil {
|
if authReq == nil {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
data.Email = authReq.LoginHint
|
data.Email = domain.EmailAddress(authReq.LoginHint)
|
||||||
domainPolicy, err := l.getOrgDomainPolicy(r, authReq.RequestedOrgID)
|
domainPolicy, err := l.getOrgDomainPolicy(r, authReq.RequestedOrgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.WithFields("authRequest", authReq.ID, "org", authReq.RequestedOrgID).Error("unable to load domain policy for registration loginHint")
|
logging.WithFields("authRequest", authReq.ID, "org", authReq.RequestedOrgID).Error("unable to load domain policy for registration loginHint")
|
||||||
|
@ -14,14 +14,14 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type registerOrgFormData struct {
|
type registerOrgFormData struct {
|
||||||
RegisterOrgName string `schema:"orgname"`
|
RegisterOrgName string `schema:"orgname"`
|
||||||
Email string `schema:"email"`
|
Email domain.EmailAddress `schema:"email"`
|
||||||
Username string `schema:"username"`
|
Username string `schema:"username"`
|
||||||
Firstname string `schema:"firstname"`
|
Firstname string `schema:"firstname"`
|
||||||
Lastname string `schema:"lastname"`
|
Lastname string `schema:"lastname"`
|
||||||
Password string `schema:"register-password"`
|
Password string `schema:"register-password"`
|
||||||
Password2 string `schema:"register-password-confirmation"`
|
Password2 string `schema:"register-password-confirmation"`
|
||||||
TermsConfirm bool `schema:"terms-confirm"`
|
TermsConfirm bool `schema:"terms-confirm"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type registerOrgData struct {
|
type registerOrgData struct {
|
||||||
@ -121,7 +121,7 @@ func (l *Login) renderRegisterOrg(w http.ResponseWriter, r *http.Request, authRe
|
|||||||
|
|
||||||
func (d registerOrgFormData) toUserDomain() *domain.Human {
|
func (d registerOrgFormData) toUserDomain() *domain.Human {
|
||||||
if d.Username == "" {
|
if d.Username == "" {
|
||||||
d.Username = d.Email
|
d.Username = string(d.Email)
|
||||||
}
|
}
|
||||||
return &domain.Human{
|
return &domain.Human{
|
||||||
Username: d.Username,
|
Username: d.Username,
|
||||||
@ -140,7 +140,7 @@ func (d registerOrgFormData) toUserDomain() *domain.Human {
|
|||||||
|
|
||||||
func (d registerOrgFormData) toCommandOrg() *command.OrgSetup {
|
func (d registerOrgFormData) toCommandOrg() *command.OrgSetup {
|
||||||
if d.Username == "" {
|
if d.Username == "" {
|
||||||
d.Username = d.Email
|
d.Username = string(d.Email)
|
||||||
}
|
}
|
||||||
return &command.OrgSetup{
|
return &command.OrgSetup{
|
||||||
Name: d.RegisterOrgName,
|
Name: d.RegisterOrgName,
|
||||||
|
@ -328,6 +328,33 @@ Errors:
|
|||||||
Invalid: Userdaten sind ungültig
|
Invalid: Userdaten sind ungültig
|
||||||
DomainNotAllowedAsUsername: Domäne ist bereits reserviert und kann nicht verwendet werden
|
DomainNotAllowedAsUsername: Domäne ist bereits reserviert und kann nicht verwendet werden
|
||||||
NotAllowedToLink: Der Benutzer darf nicht mit einem externen Login Provider verlinkt werden
|
NotAllowedToLink: Der Benutzer darf nicht mit einem externen Login Provider verlinkt werden
|
||||||
|
Profile:
|
||||||
|
NotFound: Profil nicht gefunden
|
||||||
|
NotChanged: Profil nicht verändert
|
||||||
|
Empty: Profil ist leer
|
||||||
|
FirstNameEmpty: Vorname im Profil ist leer
|
||||||
|
LastNameEmpty: Nachname im Profil ist leer
|
||||||
|
IDMissing: Profil ID fehlt
|
||||||
|
Email:
|
||||||
|
NotFound: Email nicht gefunden
|
||||||
|
Invalid: Email ist ungültig
|
||||||
|
AlreadyVerified: Email ist bereits verifiziert
|
||||||
|
NotChanged: Email wurde nicht geändert
|
||||||
|
Empty: Email ist leer
|
||||||
|
IDMissing: Email ID fehlt
|
||||||
|
Phone:
|
||||||
|
NotFound: Telefonnummer nicht gefunden
|
||||||
|
Invalid: Telefonnummer ist ungültig
|
||||||
|
AlreadyVerified: Telefonnummer bereits verifiziert
|
||||||
|
Empty: Telefonnummer ist leer
|
||||||
|
NotChanged: Telefonnummer wurde nicht geändert
|
||||||
|
Address:
|
||||||
|
NotFound: Adresse nicht gefunden
|
||||||
|
NotChanged: Adresse wurde nicht geändert
|
||||||
|
Username:
|
||||||
|
AlreadyExists: Benutzername ist bereits vergeben
|
||||||
|
Reserved: Benutzername ist bereits vergeben
|
||||||
|
Empty: Benutzername ist leer
|
||||||
Password:
|
Password:
|
||||||
ConfirmationWrong: Passwort Bestätigung stimmt nicht überein
|
ConfirmationWrong: Passwort Bestätigung stimmt nicht überein
|
||||||
Empty: Passwort ist leer
|
Empty: Passwort ist leer
|
||||||
|
@ -328,6 +328,33 @@ Errors:
|
|||||||
Invalid: Invalid userdata
|
Invalid: Invalid userdata
|
||||||
DomainNotAllowedAsUsername: Domain is already reserved and cannot be used
|
DomainNotAllowedAsUsername: Domain is already reserved and cannot be used
|
||||||
NotAllowedToLink: User is not allowed to link with external login provider
|
NotAllowedToLink: User is not allowed to link with external login provider
|
||||||
|
Profile:
|
||||||
|
NotFound: Profile not found
|
||||||
|
NotChanged: Profile not changed
|
||||||
|
Empty: Profile is empty
|
||||||
|
FirstNameEmpty: First name in profile is empty
|
||||||
|
LastNameEmpty: Last name in profile is empty
|
||||||
|
IDMissing: Profile ID is missing
|
||||||
|
Email:
|
||||||
|
NotFound: Email not found
|
||||||
|
Invalid: Email is invalid
|
||||||
|
AlreadyVerified: Email is already verified
|
||||||
|
NotChanged: Email not changed
|
||||||
|
Empty: Email is empty
|
||||||
|
IDMissing: Email ID is missing
|
||||||
|
Phone:
|
||||||
|
NotFound: Phone not found
|
||||||
|
Invalid: Phone is invalid
|
||||||
|
AlreadyVerified: Phone already verified
|
||||||
|
Empty: Phone is empty
|
||||||
|
NotChanged: Phone not changed
|
||||||
|
Address:
|
||||||
|
NotFound: Address not found
|
||||||
|
NotChanged: Address not changed
|
||||||
|
Username:
|
||||||
|
AlreadyExists: Username already taken
|
||||||
|
Reserved: Username is already taken
|
||||||
|
Empty: Username is empty
|
||||||
Password:
|
Password:
|
||||||
ConfirmationWrong: Passwordconfirmation is wrong
|
ConfirmationWrong: Passwordconfirmation is wrong
|
||||||
Empty: Password is empty
|
Empty: Password is empty
|
||||||
|
@ -328,6 +328,33 @@ Errors:
|
|||||||
Invalid: Données utilisateur non valides
|
Invalid: Données utilisateur non valides
|
||||||
DomainNotAllowedAsUsername: Le domaine est déjà réservé et ne peut pas être utilisé.
|
DomainNotAllowedAsUsername: Le domaine est déjà réservé et ne peut pas être utilisé.
|
||||||
NotAllowedToLink: L'utilisateur n'est pas autorisé à établir un lien avec un fournisseur de connexion externe
|
NotAllowedToLink: L'utilisateur n'est pas autorisé à établir un lien avec un fournisseur de connexion externe
|
||||||
|
Profile:
|
||||||
|
NotFound: Profil non trouvé
|
||||||
|
NotChanged: Le profil n'a pas changé
|
||||||
|
Empty: Profil est vide
|
||||||
|
FirstNameEmpty: Le prénom dans le profil est vide
|
||||||
|
LastNameEmpty: Le nom de famille dans le profil est vide
|
||||||
|
IDMissing: Profil ID manquant
|
||||||
|
Email:
|
||||||
|
NotFound: Email non trouvé
|
||||||
|
Invalid: L'email n'est pas valide
|
||||||
|
AlreadyVerified: L'adresse électronique est déjà vérifiée
|
||||||
|
NotChanged: L'adresse électronique n'a pas changé
|
||||||
|
Empty: Email est vide
|
||||||
|
IDMissing: Email ID manquant
|
||||||
|
Phone:
|
||||||
|
Notfound: Téléphone non trouvé
|
||||||
|
Invalid: Le téléphone n'est pas valide
|
||||||
|
AlreadyVerified: Téléphone déjà vérifié
|
||||||
|
Empty: Téléphone est vide
|
||||||
|
NotChanged: Téléphone n'a pas changé
|
||||||
|
Address:
|
||||||
|
NotFound: Adresse non trouvée
|
||||||
|
NotChanged: L'adresse n'a pas changé
|
||||||
|
Username:
|
||||||
|
AlreadyExists: Nom d'utilisateur déjà pris
|
||||||
|
Reserved: Le nom d'utilisateur est déjà pris
|
||||||
|
Empty: Le nom d'utilisateur est vide
|
||||||
Password:
|
Password:
|
||||||
ConfirmationWrong: La confirmation du mot de passe est erronée
|
ConfirmationWrong: La confirmation du mot de passe est erronée
|
||||||
Empty: Le mot de passe est vide
|
Empty: Le mot de passe est vide
|
||||||
|
@ -328,6 +328,33 @@ Errors:
|
|||||||
Invalid: I dati del utente non sono validi
|
Invalid: I dati del utente non sono validi
|
||||||
DomainNotAllowedAsUsername: Il dominio è già riservato e non può essere utilizzato
|
DomainNotAllowedAsUsername: Il dominio è già riservato e non può essere utilizzato
|
||||||
NotAllowedToLink: L'utente non è autorizzato a collegarsi con un provider di accesso esterno
|
NotAllowedToLink: L'utente non è autorizzato a collegarsi con un provider di accesso esterno
|
||||||
|
Profile:
|
||||||
|
NotFound: Profilo non trovato
|
||||||
|
NotChanged: Profilo non cambiato
|
||||||
|
Empty: Profilo è vuoto
|
||||||
|
FirstNameEmpty: Il nome nel profilo è vuoto
|
||||||
|
LastNameEmpty: Il cognome nel profilo è vuoto
|
||||||
|
IDMissing: Profilo ID mancante
|
||||||
|
Email:
|
||||||
|
NotFound: Email non trovata
|
||||||
|
Invalid: L'e-mail non è valida
|
||||||
|
AlreadyVerified: L'e-mail è già verificata
|
||||||
|
NotChanged: Email non cambiata
|
||||||
|
Empty: Email è vuota
|
||||||
|
IDMissing: Email ID mancante
|
||||||
|
Phone:
|
||||||
|
NotFound: Telefono non trovato
|
||||||
|
Invalid: Il telefono non è valido
|
||||||
|
AlreadyVerified: Telefono già verificato
|
||||||
|
Empty: Il telefono è vuoto
|
||||||
|
NotChanged: Telefono non cambiato
|
||||||
|
Address:
|
||||||
|
NotFound: Indirizzo non trovato
|
||||||
|
NotChanged: Indirizzo non cambiato
|
||||||
|
Username:
|
||||||
|
AlreadyExists: Nome utente già preso
|
||||||
|
Reserved: Il nome utente è già preso
|
||||||
|
Empty: Il nome utente è vuoto
|
||||||
Password:
|
Password:
|
||||||
ConfirmationWrong: La conferma della password è sbagliata
|
ConfirmationWrong: La conferma della password è sbagliata
|
||||||
Empty: La password è vuota
|
Empty: La password è vuota
|
||||||
|
@ -328,6 +328,33 @@ Errors:
|
|||||||
Invalid: Nieprawidłowe dane użytkownika
|
Invalid: Nieprawidłowe dane użytkownika
|
||||||
DomainNotAllowedAsUsername: Domena jest już zarezerwowana i nie może być użyta
|
DomainNotAllowedAsUsername: Domena jest już zarezerwowana i nie może być użyta
|
||||||
NotAllowedToLink: Użytkownik nie jest upoważniony do łączenia z zewnętrznym dostawcą logowania
|
NotAllowedToLink: Użytkownik nie jest upoważniony do łączenia z zewnętrznym dostawcą logowania
|
||||||
|
Profile:
|
||||||
|
NotFound: Profil nie znaleziony
|
||||||
|
NotChanged: Profil nie zmieniony
|
||||||
|
Empty: Profil jest pusty
|
||||||
|
FirstNameEmpty: Imię w profilu jest puste
|
||||||
|
LastNameEmpty: Nazwisko w profilu jest puste
|
||||||
|
IDMissing: Profil ID brakuje
|
||||||
|
Email:
|
||||||
|
NotFound: Adres e-mail nie znaleziony
|
||||||
|
Invalid: Adres e-mail jest nieprawidłowy
|
||||||
|
AlreadyVerified: Adres e-mail jest już zweryfikowany
|
||||||
|
NotChanged: Adres e-mail nie zmieniony
|
||||||
|
Empty: Adres e-mail jest pusty
|
||||||
|
IDMissing: Adres e-mail ID brakuje
|
||||||
|
Phone:
|
||||||
|
NotFound: Numer telefonu nie znaleziony
|
||||||
|
Invalid: Numer telefonu jest nieprawidłowy
|
||||||
|
AlreadyVerified: Numer telefonu już zweryfikowany
|
||||||
|
Empty: Numer telefonu jest pusty
|
||||||
|
NotChanged: Numer telefonu nie zmieniony
|
||||||
|
Address:
|
||||||
|
NotFound: Adres nie znaleziony
|
||||||
|
NotChanged: Adres nie zmieniony
|
||||||
|
Username:
|
||||||
|
AlreadyExists: Nazwa użytkownika jest już zajęta
|
||||||
|
Reserved: Nazwa użytkownika jest już zajęta
|
||||||
|
Empty: Nazwa użytkownika jest pusty
|
||||||
Password:
|
Password:
|
||||||
ConfirmationWrong: Potwierdzenie hasła jest niepoprawne
|
ConfirmationWrong: Potwierdzenie hasła jest niepoprawne
|
||||||
Empty: Hasło jest puste
|
Empty: Hasło jest puste
|
||||||
|
@ -328,6 +328,33 @@ Errors:
|
|||||||
Invalid: 无效的用户数据
|
Invalid: 无效的用户数据
|
||||||
DomainNotAllowedAsUsername: 域名已存在,但无法使用
|
DomainNotAllowedAsUsername: 域名已存在,但无法使用
|
||||||
NotAllowedToLink: 不允许用户使用外部身份提供者注册
|
NotAllowedToLink: 不允许用户使用外部身份提供者注册
|
||||||
|
Profile:
|
||||||
|
NotFound: 未找到个人资料
|
||||||
|
NotChanged: 个人资料未更改
|
||||||
|
Empty: 简介是空的
|
||||||
|
FirstNameEmpty: 简介中的名字是空的
|
||||||
|
LastNameEmpty: 简介中的姓氏是空的
|
||||||
|
IDMissing: 简介ID丢失
|
||||||
|
Email:
|
||||||
|
NotFound: 电子邮件没有找到
|
||||||
|
Invalid: 电子邮件无效
|
||||||
|
AlreadyVerified: 电子邮件已经过验证
|
||||||
|
NotChanged: 电子邮件未更改
|
||||||
|
Empty: 电子邮件是空的
|
||||||
|
IDMissing: 电子邮件ID丢失
|
||||||
|
Phone:
|
||||||
|
NotFound: 手机号码未找到
|
||||||
|
Invalid: 手机号码无效
|
||||||
|
AlreadyVerified: 手机号码已经验证
|
||||||
|
Empty: 电话号码是空的
|
||||||
|
NotChanged: 电话号码没有改变
|
||||||
|
Address:
|
||||||
|
NotFound: 找不到地址
|
||||||
|
NotChanged: 地址没有改变
|
||||||
|
Username:
|
||||||
|
AlreadyExists: 用户名已被使用
|
||||||
|
Reserved: 用户名已被使用
|
||||||
|
Empty: 用户名是空的
|
||||||
Password:
|
Password:
|
||||||
ConfirmationWrong: 密码不一致
|
ConfirmationWrong: 密码不一致
|
||||||
Empty: 密码为空
|
Empty: 密码为空
|
||||||
|
@ -10,12 +10,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Email struct {
|
type Email struct {
|
||||||
Address string
|
Address domain.EmailAddress
|
||||||
Verified bool
|
Verified bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Email) Valid() bool {
|
func (e *Email) Validate() error {
|
||||||
return e.Address != "" && domain.EmailRegex.MatchString(e.Address)
|
return e.Address.Validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newEmailCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (value *crypto.CryptoValue, expiry time.Duration, err error) {
|
func newEmailCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (value *crypto.CryptoValue, expiry time.Duration, err error) {
|
||||||
|
@ -4,30 +4,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ttacon/libphonenumber"
|
|
||||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Phone struct {
|
type Phone struct {
|
||||||
Number string
|
Number domain.PhoneNumber
|
||||||
Verified bool
|
Verified bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatPhoneNumber(number string) (string, error) {
|
|
||||||
if number == "" {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
phoneNr, err := libphonenumber.Parse(number, libphonenumber.UNKNOWN_REGION)
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.ThrowInvalidArgument(nil, "EVENT-so0wa", "Errors.User.Phone.Invalid")
|
|
||||||
}
|
|
||||||
number = libphonenumber.Format(phoneNr, libphonenumber.E164)
|
|
||||||
return number, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPhoneCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (value *crypto.CryptoValue, expiry time.Duration, err error) {
|
func newPhoneCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (value *crypto.CryptoValue, expiry time.Duration, err error) {
|
||||||
return newCryptoCodeWithExpiry(ctx, filter, domain.SecretGeneratorTypeVerifyPhoneCode, alg)
|
return newCryptoCodeWithExpiry(ctx, filter, domain.SecretGeneratorTypeVerifyPhoneCode, alg)
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,13 @@ package command
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/errors"
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFormatPhoneNumber(t *testing.T) {
|
func TestFormatPhoneNumber(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
number string
|
number domain.PhoneNumber
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -44,10 +45,9 @@ func TestFormatPhoneNumber(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
formatted, err := FormatPhoneNumber(tt.args.number)
|
normalized, err := tt.args.number.Normalize()
|
||||||
|
if tt.errFunc == nil && tt.result.Number != normalized {
|
||||||
if tt.errFunc == nil && tt.result.Number != formatted {
|
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.Number, normalized)
|
||||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.args.number, formatted)
|
|
||||||
}
|
}
|
||||||
if tt.errFunc != nil && !tt.errFunc(err) {
|
if tt.errFunc != nil && !tt.errFunc(err) {
|
||||||
t.Errorf("got wrong err: %v ", err)
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
@ -104,29 +104,31 @@ func (c *Commands) AddHuman(ctx context.Context, resourceOwner string, human *Ad
|
|||||||
|
|
||||||
type humanCreationCommand interface {
|
type humanCreationCommand interface {
|
||||||
eventstore.Command
|
eventstore.Command
|
||||||
AddPhoneData(phoneNumber string)
|
AddPhoneData(phoneNumber domain.PhoneNumber)
|
||||||
AddPasswordData(secret *crypto.CryptoValue, changeRequired bool)
|
AddPasswordData(secret *crypto.CryptoValue, changeRequired bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddHumanCommand(a *user.Aggregate, human *AddHuman, passwordAlg crypto.HashAlgorithm, codeAlg crypto.EncryptionAlgorithm) preparation.Validation {
|
func AddHumanCommand(a *user.Aggregate, human *AddHuman, passwordAlg crypto.HashAlgorithm, codeAlg crypto.EncryptionAlgorithm) preparation.Validation {
|
||||||
return func() (_ preparation.CreateCommands, err error) {
|
return func() (_ preparation.CreateCommands, err error) {
|
||||||
if !human.Email.Valid() {
|
if err := human.Email.Validate(); err != nil {
|
||||||
return nil, errors.ThrowInvalidArgument(nil, "USER-Ec7dM", "Errors.Invalid.Argument")
|
return nil, err
|
||||||
}
|
}
|
||||||
if human.Username = strings.TrimSpace(human.Username); human.Username == "" {
|
if human.Username = strings.TrimSpace(human.Username); human.Username == "" {
|
||||||
return nil, errors.ThrowInvalidArgument(nil, "V2-zzad3", "Errors.Invalid.Argument")
|
return nil, errors.ThrowInvalidArgument(nil, "V2-zzad3", "Errors.Invalid.Argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
if human.FirstName = strings.TrimSpace(human.FirstName); human.FirstName == "" {
|
if human.FirstName = strings.TrimSpace(human.FirstName); human.FirstName == "" {
|
||||||
return nil, errors.ThrowInvalidArgument(nil, "USER-UCej2", "Errors.Invalid.Argument")
|
return nil, errors.ThrowInvalidArgument(nil, "USER-UCej2", "Errors.User.Profile.FirstNameEmpty")
|
||||||
}
|
}
|
||||||
if human.LastName = strings.TrimSpace(human.LastName); human.LastName == "" {
|
if human.LastName = strings.TrimSpace(human.LastName); human.LastName == "" {
|
||||||
return nil, errors.ThrowInvalidArgument(nil, "USER-DiAq8", "Errors.Invalid.Argument")
|
return nil, errors.ThrowInvalidArgument(nil, "USER-4hB7d", "Errors.User.Profile.LastNameEmpty")
|
||||||
}
|
}
|
||||||
human.ensureDisplayName()
|
human.ensureDisplayName()
|
||||||
|
|
||||||
if human.Phone.Number, err = FormatPhoneNumber(human.Phone.Number); err != nil {
|
if human.Phone.Number != "" {
|
||||||
return nil, errors.ThrowInvalidArgument(nil, "USER-tD6ax", "Errors.Invalid.Argument")
|
if human.Phone.Number, err = human.Phone.Number.Normalize(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||||
@ -387,19 +389,12 @@ func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domai
|
|||||||
return writeModelToHuman(registeredHuman), nil
|
return writeModelToHuman(registeredHuman), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Human, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) {
|
|
||||||
if orgID == "" || !human.IsValid() {
|
|
||||||
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-67Ms8", "Errors.User.Invalid")
|
|
||||||
}
|
|
||||||
if human.Password != nil && human.Password.SecretString != "" {
|
|
||||||
human.Password.ChangeRequired = true
|
|
||||||
}
|
|
||||||
return c.createHuman(ctx, orgID, human, nil, false, false, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.Human, passwordless bool, links []*domain.UserIDPLink, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator crypto.Generator) (events []eventstore.Command, humanWriteModel *HumanWriteModel, passwordlessCodeWriteModel *HumanPasswordlessInitCodeWriteModel, code string, err error) {
|
func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.Human, passwordless bool, links []*domain.UserIDPLink, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator crypto.Generator) (events []eventstore.Command, humanWriteModel *HumanWriteModel, passwordlessCodeWriteModel *HumanPasswordlessInitCodeWriteModel, code string, err error) {
|
||||||
if orgID == "" || !human.IsValid() {
|
if orgID == "" {
|
||||||
return nil, nil, nil, "", errors.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.User.Invalid")
|
return nil, nil, nil, "", errors.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.Org.Empty")
|
||||||
|
}
|
||||||
|
if err := human.Normalize(); err != nil {
|
||||||
|
return nil, nil, nil, "", err
|
||||||
}
|
}
|
||||||
events, humanWriteModel, err = c.createHuman(ctx, orgID, human, links, false, passwordless, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
|
events, humanWriteModel, err = c.createHuman(ctx, orgID, human, links, false, passwordless, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -421,10 +416,16 @@ func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domai
|
|||||||
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-JKefw", "Errors.User.Invalid")
|
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-JKefw", "Errors.User.Invalid")
|
||||||
}
|
}
|
||||||
if human.Username = strings.TrimSpace(human.Username); human.Username == "" {
|
if human.Username = strings.TrimSpace(human.Username); human.Username == "" {
|
||||||
human.Username = human.EmailAddress
|
human.Username = string(human.EmailAddress)
|
||||||
}
|
}
|
||||||
if orgID == "" || !human.IsValid() || link == nil && (human.Password == nil || human.Password.SecretString == "") {
|
if orgID == "" {
|
||||||
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-9dk45", "Errors.User.Invalid")
|
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-hYsVH", "Errors.Org.Empty")
|
||||||
|
}
|
||||||
|
if err := human.Normalize(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if link == nil && (human.Password == nil || human.Password.SecretString == "") {
|
||||||
|
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-X23na", "Errors.User.Password.Empty")
|
||||||
}
|
}
|
||||||
if human.Password != nil && human.Password.SecretString != "" {
|
if human.Password != nil && human.Password.SecretString != "" {
|
||||||
human.Password.ChangeRequired = false
|
human.Password.ChangeRequired = false
|
||||||
@ -441,7 +442,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
human.Username = strings.TrimSpace(human.Username)
|
human.Username = strings.TrimSpace(human.Username)
|
||||||
human.EmailAddress = strings.TrimSpace(human.EmailAddress)
|
human.EmailAddress = human.EmailAddress.Normalize()
|
||||||
if !domainPolicy.UserLoginMustBeDomain {
|
if !domainPolicy.UserLoginMustBeDomain {
|
||||||
index := strings.LastIndex(human.Username, "@")
|
index := strings.LastIndex(human.Username, "@")
|
||||||
if index > 1 {
|
if index > 1 {
|
||||||
|
@ -13,8 +13,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email, emailCodeGenerator crypto.Generator) (*domain.Email, error) {
|
func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email, emailCodeGenerator crypto.Generator) (*domain.Email, error) {
|
||||||
if !email.IsValid() || email.AggregateID == "" {
|
if email.AggregateID == "" {
|
||||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M9sf", "Errors.Email.Invalid")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-0Gzs3", "Errors.User.Email.IDMissing")
|
||||||
|
}
|
||||||
|
if err := email.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
existingEmail, err := c.emailWriteModel(ctx, email.AggregateID, email.ResourceOwner)
|
existingEmail, err := c.emailWriteModel(ctx, email.AggregateID, email.ResourceOwner)
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
type HumanEmailWriteModel struct {
|
type HumanEmailWriteModel struct {
|
||||||
eventstore.WriteModel
|
eventstore.WriteModel
|
||||||
|
|
||||||
Email string
|
Email domain.EmailAddress
|
||||||
IsEmailVerified bool
|
IsEmailVerified bool
|
||||||
|
|
||||||
Code *crypto.CryptoValue
|
Code *crypto.CryptoValue
|
||||||
@ -95,7 +95,7 @@ func (wm *HumanEmailWriteModel) Query() *eventstore.SearchQueryBuilder {
|
|||||||
func (wm *HumanEmailWriteModel) NewChangedEvent(
|
func (wm *HumanEmailWriteModel) NewChangedEvent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
aggregate *eventstore.Aggregate,
|
aggregate *eventstore.Aggregate,
|
||||||
email string,
|
email domain.EmailAddress,
|
||||||
) (*user.HumanEmailChangedEvent, bool) {
|
) (*user.HumanEmailChangedEvent, bool) {
|
||||||
if wm.Email == email {
|
if wm.Email == email {
|
||||||
return nil, false
|
return nil, false
|
||||||
|
@ -11,8 +11,8 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/repository/user"
|
"github.com/zitadel/zitadel/internal/repository/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
//ResendInitialMail resend inital mail and changes email if provided
|
// ResendInitialMail resend initial mail and changes email if provided
|
||||||
func (c *Commands) ResendInitialMail(ctx context.Context, userID, email, resourceOwner string, initCodeGenerator crypto.Generator) (objectDetails *domain.ObjectDetails, err error) {
|
func (c *Commands) ResendInitialMail(ctx context.Context, userID string, email domain.EmailAddress, resourceOwner string, initCodeGenerator crypto.Generator) (objectDetails *domain.ObjectDetails, err error) {
|
||||||
if userID == "" {
|
if userID == "" {
|
||||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing")
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
type HumanInitCodeWriteModel struct {
|
type HumanInitCodeWriteModel struct {
|
||||||
eventstore.WriteModel
|
eventstore.WriteModel
|
||||||
|
|
||||||
Email string
|
Email domain.EmailAddress
|
||||||
IsEmailVerified bool
|
IsEmailVerified bool
|
||||||
|
|
||||||
Code *crypto.CryptoValue
|
Code *crypto.CryptoValue
|
||||||
@ -92,7 +92,7 @@ func (wm *HumanInitCodeWriteModel) Query() *eventstore.SearchQueryBuilder {
|
|||||||
func (wm *HumanInitCodeWriteModel) NewChangedEvent(
|
func (wm *HumanInitCodeWriteModel) NewChangedEvent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
aggregate *eventstore.Aggregate,
|
aggregate *eventstore.Aggregate,
|
||||||
email string,
|
email domain.EmailAddress,
|
||||||
) (*user.HumanEmailChangedEvent, bool) {
|
) (*user.HumanEmailChangedEvent, bool) {
|
||||||
changedEvent := user.NewHumanEmailChangedEvent(ctx, aggregate, email)
|
changedEvent := user.NewHumanEmailChangedEvent(ctx, aggregate, email)
|
||||||
return changedEvent, wm.Email != email
|
return changedEvent, wm.Email != email
|
||||||
|
@ -289,7 +289,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
|||||||
r := &Commands{
|
r := &Commands{
|
||||||
eventstore: tt.fields.eventstore,
|
eventstore: tt.fields.eventstore,
|
||||||
}
|
}
|
||||||
got, err := r.ResendInitialMail(tt.args.ctx, tt.args.userID, tt.args.email, tt.args.resourceOwner, tt.args.secretGenerator)
|
got, err := r.ResendInitialMail(tt.args.ctx, tt.args.userID, domain.EmailAddress(tt.args.email), tt.args.resourceOwner, tt.args.secretGenerator)
|
||||||
if tt.res.err == nil {
|
if tt.res.err == nil {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,10 @@ type HumanWriteModel struct {
|
|||||||
Gender domain.Gender
|
Gender domain.Gender
|
||||||
Avatar string
|
Avatar string
|
||||||
|
|
||||||
Email string
|
Email domain.EmailAddress
|
||||||
IsEmailVerified bool
|
IsEmailVerified bool
|
||||||
|
|
||||||
Phone string
|
Phone domain.PhoneNumber
|
||||||
IsPhoneVerified bool
|
IsPhoneVerified bool
|
||||||
|
|
||||||
Country string
|
Country string
|
||||||
|
@ -69,7 +69,7 @@ func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string
|
|||||||
|
|
||||||
accountName := domain.GenerateLoginName(human.GetUsername(), org.PrimaryDomain, orgPolicy.UserLoginMustBeDomain)
|
accountName := domain.GenerateLoginName(human.GetUsername(), org.PrimaryDomain, orgPolicy.UserLoginMustBeDomain)
|
||||||
if accountName == "" {
|
if accountName == "" {
|
||||||
accountName = human.EmailAddress
|
accountName = string(human.EmailAddress)
|
||||||
}
|
}
|
||||||
key, secret, err := domain.NewOTPKey(c.multifactors.OTP.Issuer, accountName, c.multifactors.OTP.CryptoMFA)
|
key, secret, err := domain.NewOTPKey(c.multifactors.OTP.Issuer, accountName, c.multifactors.OTP.CryptoMFA)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -14,10 +14,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) ChangeHumanPhone(ctx context.Context, phone *domain.Phone, resourceOwner string, phoneCodeGenerator crypto.Generator) (*domain.Phone, error) {
|
func (c *Commands) ChangeHumanPhone(ctx context.Context, phone *domain.Phone, resourceOwner string, phoneCodeGenerator crypto.Generator) (*domain.Phone, error) {
|
||||||
if !phone.IsValid() {
|
if err := phone.Normalize(); err != nil {
|
||||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-6M0ds", "Errors.Phone.Invalid")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
existingPhone, err := c.phoneWriteModelByID(ctx, phone.AggregateID, resourceOwner)
|
existingPhone, err := c.phoneWriteModelByID(ctx, phone.AggregateID, resourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
type HumanPhoneWriteModel struct {
|
type HumanPhoneWriteModel struct {
|
||||||
eventstore.WriteModel
|
eventstore.WriteModel
|
||||||
|
|
||||||
Phone string
|
Phone domain.PhoneNumber
|
||||||
IsPhoneVerified bool
|
IsPhoneVerified bool
|
||||||
|
|
||||||
Code *crypto.CryptoValue
|
Code *crypto.CryptoValue
|
||||||
@ -107,7 +107,7 @@ func (wm *HumanPhoneWriteModel) Query() *eventstore.SearchQueryBuilder {
|
|||||||
func (wm *HumanPhoneWriteModel) NewChangedEvent(
|
func (wm *HumanPhoneWriteModel) NewChangedEvent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
aggregate *eventstore.Aggregate,
|
aggregate *eventstore.Aggregate,
|
||||||
phone string,
|
phone domain.PhoneNumber,
|
||||||
) (*user.HumanPhoneChangedEvent, bool) {
|
) (*user.HumanPhoneChangedEvent, bool) {
|
||||||
changedEvent := user.NewHumanPhoneChangedEvent(ctx, aggregate, phone)
|
changedEvent := user.NewHumanPhoneChangedEvent(ctx, aggregate, phone)
|
||||||
return changedEvent, phone != wm.Phone
|
return changedEvent, phone != wm.Phone
|
||||||
|
@ -9,10 +9,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (c *Commands) ChangeHumanProfile(ctx context.Context, profile *domain.Profile) (*domain.Profile, error) {
|
func (c *Commands) ChangeHumanProfile(ctx context.Context, profile *domain.Profile) (*domain.Profile, error) {
|
||||||
if !profile.IsValid() && profile.AggregateID != "" {
|
if profile.AggregateID == "" {
|
||||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8io0d", "Errors.User.Profile.Invalid")
|
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-AwbEB", "Errors.User.Profile.IDMissing")
|
||||||
|
}
|
||||||
|
if err := profile.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
existingProfile, err := c.profileWriteModelByID(ctx, profile.AggregateID, profile.ResourceOwner)
|
existingProfile, err := c.profileWriteModelByID(ctx, profile.AggregateID, profile.ResourceOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -3445,7 +3445,7 @@ func newAddHumanEvent(password string, changeRequired bool, phone string) *user.
|
|||||||
changeRequired)
|
changeRequired)
|
||||||
}
|
}
|
||||||
if phone != "" {
|
if phone != "" {
|
||||||
event.AddPhoneData(phone)
|
event.AddPhoneData(domain.PhoneNumber(phone))
|
||||||
}
|
}
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
@ -3473,7 +3473,7 @@ func newRegisterHumanEvent(username, password string, changeRequired bool, phone
|
|||||||
changeRequired)
|
changeRequired)
|
||||||
}
|
}
|
||||||
if phone != "" {
|
if phone != "" {
|
||||||
event.AddPhoneData(phone)
|
event.AddPhoneData(domain.PhoneNumber(phone))
|
||||||
}
|
}
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
@ -3503,7 +3503,7 @@ func TestAddHumanCommand(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: Want{
|
want: Want{
|
||||||
ValidationErr: errors.ThrowInvalidArgument(nil, "USER-Ec7dM", "Errors.Invalid.Argument"),
|
ValidationErr: errors.ThrowInvalidArgument(nil, "EMAIL-599BI", "Errors.User.Email.Invalid"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -3519,7 +3519,7 @@ func TestAddHumanCommand(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: Want{
|
want: Want{
|
||||||
ValidationErr: errors.ThrowInvalidArgument(nil, "USER-UCej2", "Errors.Invalid.Argument"),
|
ValidationErr: errors.ThrowInvalidArgument(nil, "USER-UCej2", "Errors.User.Profile.FirstNameEmpty"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -3534,7 +3534,7 @@ func TestAddHumanCommand(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: Want{
|
want: Want{
|
||||||
ValidationErr: errors.ThrowInvalidArgument(nil, "USER-DiAq8", "Errors.Invalid.Argument"),
|
ValidationErr: errors.ThrowInvalidArgument(nil, "USER-4hB7d", "Errors.User.Profile.LastNameEmpty"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -155,7 +155,7 @@ func (c *Commands) addHumanWebAuthN(ctx context.Context, userID, resourceowner s
|
|||||||
}
|
}
|
||||||
accountName := domain.GenerateLoginName(user.GetUsername(), org.PrimaryDomain, orgPolicy.UserLoginMustBeDomain)
|
accountName := domain.GenerateLoginName(user.GetUsername(), org.PrimaryDomain, orgPolicy.UserLoginMustBeDomain)
|
||||||
if accountName == "" {
|
if accountName == "" {
|
||||||
accountName = user.EmailAddress
|
accountName = string(user.EmailAddress)
|
||||||
}
|
}
|
||||||
webAuthN, err := c.webauthnConfig.BeginRegistration(ctx, user, accountName, authenticatorPlatform, userVerification, isLoginUI, tokens...)
|
webAuthN, err := c.webauthnConfig.BeginRegistration(ctx, user, accountName, authenticatorPlatform, userVerification, isLoginUI, tokens...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -65,10 +65,10 @@ type ExternalUser struct {
|
|||||||
FirstName string
|
FirstName string
|
||||||
LastName string
|
LastName string
|
||||||
NickName string
|
NickName string
|
||||||
Email string
|
Email EmailAddress
|
||||||
IsEmailVerified bool
|
IsEmailVerified bool
|
||||||
PreferredLanguage language.Tag
|
PreferredLanguage language.Tag
|
||||||
Phone string
|
Phone PhoneNumber
|
||||||
IsPhoneVerified bool
|
IsPhoneVerified bool
|
||||||
Metadatas []*Metadata
|
Metadatas []*Metadata
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
caos_errors "github.com/zitadel/zitadel/internal/errors"
|
caos_errors "github.com/zitadel/zitadel/internal/errors"
|
||||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||||
)
|
)
|
||||||
@ -60,8 +61,22 @@ func (f Gender) Specified() bool {
|
|||||||
return f > GenderUnspecified && f < genderCount
|
return f > GenderUnspecified && f < genderCount
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Human) IsValid() bool {
|
func (u *Human) Normalize() error {
|
||||||
return u.Username != "" && u.Profile != nil && u.Profile.IsValid() && u.Email != nil && u.Email.IsValid() && u.Phone == nil || (u.Phone != nil && u.Phone.PhoneNumber != "" && u.Phone.IsValid())
|
if u.Username == "" {
|
||||||
|
return errors.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.User.Username.Empty")
|
||||||
|
}
|
||||||
|
if err := u.Profile.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := u.Email.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if u.Phone != nil && u.Phone.PhoneNumber != "" {
|
||||||
|
if err := u.Phone.Normalize(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Human) CheckDomainPolicy(policy *DomainPolicy) error {
|
func (u *Human) CheckDomainPolicy(policy *DomainPolicy) error {
|
||||||
@ -69,7 +84,7 @@ func (u *Human) CheckDomainPolicy(policy *DomainPolicy) error {
|
|||||||
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-zSH7j", "Errors.Users.DomainPolicyNil")
|
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-zSH7j", "Errors.Users.DomainPolicyNil")
|
||||||
}
|
}
|
||||||
if !policy.UserLoginMustBeDomain && u.Profile != nil && u.Username == "" && u.Email != nil {
|
if !policy.UserLoginMustBeDomain && u.Profile != nil && u.Username == "" && u.Email != nil {
|
||||||
u.Username = u.EmailAddress
|
u.Username = string(u.EmailAddress)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,38 @@ package domain
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
EmailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type EmailAddress string
|
||||||
|
|
||||||
|
func (e EmailAddress) Validate() error {
|
||||||
|
if e == "" {
|
||||||
|
return errors.ThrowInvalidArgument(nil, "EMAIL-spblu", "Errors.User.Email.Empty")
|
||||||
|
}
|
||||||
|
if !emailRegex.MatchString(string(e)) {
|
||||||
|
return errors.ThrowInvalidArgument(nil, "EMAIL-599BI", "Errors.User.Email.Invalid")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e EmailAddress) Normalize() EmailAddress {
|
||||||
|
return EmailAddress(strings.TrimSpace(string(e)))
|
||||||
|
}
|
||||||
|
|
||||||
type Email struct {
|
type Email struct {
|
||||||
es_models.ObjectRoot
|
es_models.ObjectRoot
|
||||||
|
|
||||||
EmailAddress string
|
EmailAddress EmailAddress
|
||||||
IsEmailVerified bool
|
IsEmailVerified bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,8 +44,11 @@ type EmailCode struct {
|
|||||||
Expiry time.Duration
|
Expiry time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Email) IsValid() bool {
|
func (e *Email) Validate() error {
|
||||||
return e.EmailAddress != "" && EmailRegex.MatchString(e.EmailAddress)
|
if e == nil {
|
||||||
|
return errors.ThrowInvalidArgument(nil, "EMAIL-spblu", "Errors.User.Email.Empty")
|
||||||
|
}
|
||||||
|
return e.EmailAddress.Validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEmailCode(emailGenerator crypto.Generator) (*EmailCode, error) {
|
func NewEmailCode(emailGenerator crypto.Generator) (*EmailCode, error) {
|
||||||
|
@ -65,7 +65,7 @@ func TestEmailValid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result := tt.args.email.IsValid()
|
result := tt.args.email.Validate() == nil
|
||||||
if result != tt.result {
|
if result != tt.result {
|
||||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, result)
|
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, result)
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,31 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ttacon/libphonenumber"
|
"github.com/ttacon/libphonenumber"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const defaultRegion = "CH"
|
||||||
defaultRegion = "CH"
|
|
||||||
)
|
type PhoneNumber string
|
||||||
|
|
||||||
|
func (p PhoneNumber) Normalize() (PhoneNumber, error) {
|
||||||
|
if p == "" {
|
||||||
|
return p, caos_errs.ThrowInvalidArgument(nil, "PHONE-Zt0NV", "Errors.User.Phone.Empty")
|
||||||
|
}
|
||||||
|
phoneNr, err := libphonenumber.Parse(string(p), defaultRegion)
|
||||||
|
if err != nil {
|
||||||
|
return p, caos_errs.ThrowInvalidArgument(err, "PHONE-so0wa", "Errors.User.Phone.Invalid")
|
||||||
|
}
|
||||||
|
return PhoneNumber(libphonenumber.Format(phoneNr, libphonenumber.E164)), nil
|
||||||
|
}
|
||||||
|
|
||||||
type Phone struct {
|
type Phone struct {
|
||||||
es_models.ObjectRoot
|
es_models.ObjectRoot
|
||||||
|
|
||||||
PhoneNumber string
|
PhoneNumber PhoneNumber
|
||||||
IsPhoneVerified bool
|
IsPhoneVerified bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,17 +39,16 @@ type PhoneCode struct {
|
|||||||
Expiry time.Duration
|
Expiry time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Phone) IsValid() bool {
|
func (p *Phone) Normalize() error {
|
||||||
err := p.formatPhone()
|
if p == nil {
|
||||||
return p.PhoneNumber != "" && err == nil
|
return caos_errs.ThrowInvalidArgument(nil, "PHONE-YlbwO", "Errors.User.Phone.Empty")
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Phone) formatPhone() error {
|
|
||||||
phoneNr, err := libphonenumber.Parse(p.PhoneNumber, defaultRegion)
|
|
||||||
if err != nil {
|
|
||||||
return caos_errs.ThrowInvalidArgument(nil, "EVENT-so0wa", "Errors.User.Phone.Invalid")
|
|
||||||
}
|
}
|
||||||
p.PhoneNumber = libphonenumber.Format(phoneNr, libphonenumber.E164)
|
normalizedNumber, err := p.PhoneNumber.Normalize()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Issue for avoiding mutating state: https://github.com/zitadel/zitadel/issues/5412
|
||||||
|
p.PhoneNumber = normalizedNumber
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,10 +94,9 @@ func TestFormatPhoneNumber(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
err := tt.args.phone.formatPhone()
|
normalized, err := tt.args.phone.PhoneNumber.Normalize()
|
||||||
|
if tt.errFunc == nil && tt.result.PhoneNumber != normalized {
|
||||||
if tt.errFunc == nil && tt.result.PhoneNumber != tt.args.phone.PhoneNumber {
|
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.PhoneNumber, normalized)
|
||||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.args.phone.PhoneNumber, tt.result.PhoneNumber)
|
|
||||||
}
|
}
|
||||||
if tt.errFunc != nil && !tt.errFunc(err) {
|
if tt.errFunc != nil && !tt.errFunc(err) {
|
||||||
t.Errorf("got wrong err: %v ", err)
|
t.Errorf("got wrong err: %v ", err)
|
||||||
|
@ -3,6 +3,7 @@ package domain
|
|||||||
import (
|
import (
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,8 +20,17 @@ type Profile struct {
|
|||||||
LoginNames []string
|
LoginNames []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Profile) IsValid() bool {
|
func (p *Profile) Validate() error {
|
||||||
return p.FirstName != "" && p.LastName != ""
|
if p == nil {
|
||||||
|
return errors.ThrowInvalidArgument(nil, "PROFILE-GPY3p", "Errors.User.Profile.Empty")
|
||||||
|
}
|
||||||
|
if p.FirstName == "" {
|
||||||
|
return errors.ThrowInvalidArgument(nil, "PROFILE-RF5z2", "Errors.User.Profile.FirstNameEmpty")
|
||||||
|
}
|
||||||
|
if p.LastName == "" {
|
||||||
|
return errors.ThrowInvalidArgument(nil, "PROFILE-DSUkN", "Errors.User.Profile.LastNameEmpty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AvatarURL(prefix, resourceOwner, key string) string {
|
func AvatarURL(prefix, resourceOwner, key string) string {
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Provider is the minimal implementation for a 3rd party authentication provider
|
// Provider is the minimal implementation for a 3rd party authentication provider
|
||||||
@ -24,9 +26,9 @@ type User interface {
|
|||||||
GetDisplayName() string
|
GetDisplayName() string
|
||||||
GetNickname() string
|
GetNickname() string
|
||||||
GetPreferredUsername() string
|
GetPreferredUsername() string
|
||||||
GetEmail() string
|
GetEmail() domain.EmailAddress
|
||||||
IsEmailVerified() bool
|
IsEmailVerified() bool
|
||||||
GetPhone() string
|
GetPhone() domain.PhoneNumber
|
||||||
IsPhoneVerified() bool
|
IsPhoneVerified() bool
|
||||||
GetPreferredLanguage() language.Tag
|
GetPreferredLanguage() language.Tag
|
||||||
GetAvatarURL() string
|
GetAvatarURL() string
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/idp"
|
"github.com/zitadel/zitadel/internal/idp"
|
||||||
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
|
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
|
||||||
)
|
)
|
||||||
@ -120,13 +121,13 @@ func newConfig(tenant TenantType, clientID, secret, callbackURL string, scopes [
|
|||||||
// AzureAD does not return an `email_verified` claim.
|
// AzureAD does not return an `email_verified` claim.
|
||||||
// The verification can be automatically activated on the provider ([WithEmailVerified])
|
// The verification can be automatically activated on the provider ([WithEmailVerified])
|
||||||
type User struct {
|
type User struct {
|
||||||
Sub string `json:"sub"`
|
Sub string `json:"sub"`
|
||||||
FamilyName string `json:"family_name"`
|
FamilyName string `json:"family_name"`
|
||||||
GivenName string `json:"given_name"`
|
GivenName string `json:"given_name"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
PreferredUsername string `json:"preferred_username"`
|
PreferredUsername string `json:"preferred_username"`
|
||||||
Email string `json:"email"`
|
Email domain.EmailAddress `json:"email"`
|
||||||
Picture string `json:"picture"`
|
Picture string `json:"picture"`
|
||||||
isEmailVerified bool
|
isEmailVerified bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +163,7 @@ func (u *User) GetPreferredUsername() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetEmail is an implementation of the [idp.User] interface.
|
// GetEmail is an implementation of the [idp.User] interface.
|
||||||
func (u *User) GetEmail() string {
|
func (u *User) GetEmail() domain.EmailAddress {
|
||||||
return u.Email
|
return u.Email
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +177,7 @@ func (u *User) IsEmailVerified() bool {
|
|||||||
|
|
||||||
// GetPhone is an implementation of the [idp.User] interface.
|
// GetPhone is an implementation of the [idp.User] interface.
|
||||||
// It returns an empty string because AzureAD does not provide the user's phone.
|
// It returns an empty string because AzureAD does not provide the user's phone.
|
||||||
func (u *User) GetPhone() string {
|
func (u *User) GetPhone() domain.PhoneNumber {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/idp"
|
"github.com/zitadel/zitadel/internal/idp"
|
||||||
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
|
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
|
||||||
)
|
)
|
||||||
@ -259,9 +260,9 @@ func TestSession_FetchUser(t *testing.T) {
|
|||||||
a.Equal(tt.want.displayName, user.GetDisplayName())
|
a.Equal(tt.want.displayName, user.GetDisplayName())
|
||||||
a.Equal(tt.want.nickName, user.GetNickname())
|
a.Equal(tt.want.nickName, user.GetNickname())
|
||||||
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
||||||
a.Equal(tt.want.email, user.GetEmail())
|
a.Equal(domain.EmailAddress(tt.want.email), user.GetEmail())
|
||||||
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
||||||
a.Equal(tt.want.phone, user.GetPhone())
|
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
@ -68,44 +70,44 @@ func newConfig(clientID, secret, callbackURL, authURL, tokenURL string, scopes [
|
|||||||
// User is a representation of the authenticated GitHub user and implements the [idp.User] interface
|
// User is a representation of the authenticated GitHub user and implements the [idp.User] interface
|
||||||
// https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user
|
// https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user
|
||||||
type User struct {
|
type User struct {
|
||||||
Login string `json:"login"`
|
Login string `json:"login"`
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
NodeId string `json:"node_id"`
|
NodeId string `json:"node_id"`
|
||||||
AvatarUrl string `json:"avatar_url"`
|
AvatarUrl string `json:"avatar_url"`
|
||||||
GravatarId string `json:"gravatar_id"`
|
GravatarId string `json:"gravatar_id"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
HtmlUrl string `json:"html_url"`
|
HtmlUrl string `json:"html_url"`
|
||||||
FollowersUrl string `json:"followers_url"`
|
FollowersUrl string `json:"followers_url"`
|
||||||
FollowingUrl string `json:"following_url"`
|
FollowingUrl string `json:"following_url"`
|
||||||
GistsUrl string `json:"gists_url"`
|
GistsUrl string `json:"gists_url"`
|
||||||
StarredUrl string `json:"starred_url"`
|
StarredUrl string `json:"starred_url"`
|
||||||
SubscriptionsUrl string `json:"subscriptions_url"`
|
SubscriptionsUrl string `json:"subscriptions_url"`
|
||||||
OrganizationsUrl string `json:"organizations_url"`
|
OrganizationsUrl string `json:"organizations_url"`
|
||||||
ReposUrl string `json:"repos_url"`
|
ReposUrl string `json:"repos_url"`
|
||||||
EventsUrl string `json:"events_url"`
|
EventsUrl string `json:"events_url"`
|
||||||
ReceivedEventsUrl string `json:"received_events_url"`
|
ReceivedEventsUrl string `json:"received_events_url"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
SiteAdmin bool `json:"site_admin"`
|
SiteAdmin bool `json:"site_admin"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Company string `json:"company"`
|
Company string `json:"company"`
|
||||||
Blog string `json:"blog"`
|
Blog string `json:"blog"`
|
||||||
Location string `json:"location"`
|
Location string `json:"location"`
|
||||||
Email string `json:"email"`
|
Email domain.EmailAddress `json:"email"`
|
||||||
Hireable bool `json:"hireable"`
|
Hireable bool `json:"hireable"`
|
||||||
Bio string `json:"bio"`
|
Bio string `json:"bio"`
|
||||||
TwitterUsername string `json:"twitter_username"`
|
TwitterUsername string `json:"twitter_username"`
|
||||||
PublicRepos int `json:"public_repos"`
|
PublicRepos int `json:"public_repos"`
|
||||||
PublicGists int `json:"public_gists"`
|
PublicGists int `json:"public_gists"`
|
||||||
Followers int `json:"followers"`
|
Followers int `json:"followers"`
|
||||||
Following int `json:"following"`
|
Following int `json:"following"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
PrivateGists int `json:"private_gists"`
|
PrivateGists int `json:"private_gists"`
|
||||||
TotalPrivateRepos int `json:"total_private_repos"`
|
TotalPrivateRepos int `json:"total_private_repos"`
|
||||||
OwnedPrivateRepos int `json:"owned_private_repos"`
|
OwnedPrivateRepos int `json:"owned_private_repos"`
|
||||||
DiskUsage int `json:"disk_usage"`
|
DiskUsage int `json:"disk_usage"`
|
||||||
Collaborators int `json:"collaborators"`
|
Collaborators int `json:"collaborators"`
|
||||||
TwoFactorAuthentication bool `json:"two_factor_authentication"`
|
TwoFactorAuthentication bool `json:"two_factor_authentication"`
|
||||||
Plan struct {
|
Plan struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Space int `json:"space"`
|
Space int `json:"space"`
|
||||||
@ -150,7 +152,7 @@ func (u *User) GetPreferredUsername() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetEmail is an implementation of the [idp.User] interface.
|
// GetEmail is an implementation of the [idp.User] interface.
|
||||||
func (u *User) GetEmail() string {
|
func (u *User) GetEmail() domain.EmailAddress {
|
||||||
return u.Email
|
return u.Email
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +164,7 @@ func (u *User) IsEmailVerified() bool {
|
|||||||
|
|
||||||
// GetPhone is an implementation of the [idp.User] interface.
|
// GetPhone is an implementation of the [idp.User] interface.
|
||||||
// It returns an empty string because GitHub does not provide the user's phone.
|
// It returns an empty string because GitHub does not provide the user's phone.
|
||||||
func (u *User) GetPhone() string {
|
func (u *User) GetPhone() domain.PhoneNumber {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/idp"
|
"github.com/zitadel/zitadel/internal/idp"
|
||||||
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
|
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
|
||||||
)
|
)
|
||||||
@ -188,9 +189,9 @@ func TestSession_FetchUser(t *testing.T) {
|
|||||||
a.Equal(tt.want.displayName, user.GetDisplayName())
|
a.Equal(tt.want.displayName, user.GetDisplayName())
|
||||||
a.Equal(tt.want.nickName, user.GetNickname())
|
a.Equal(tt.want.nickName, user.GetNickname())
|
||||||
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
||||||
a.Equal(tt.want.email, user.GetEmail())
|
a.Equal(domain.EmailAddress(tt.want.email), user.GetEmail())
|
||||||
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
||||||
a.Equal(tt.want.phone, user.GetPhone())
|
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/idp/providers/oidc"
|
"github.com/zitadel/zitadel/internal/idp/providers/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -187,9 +188,9 @@ func TestProvider_FetchUser(t *testing.T) {
|
|||||||
a.Equal(tt.want.displayName, user.GetDisplayName())
|
a.Equal(tt.want.displayName, user.GetDisplayName())
|
||||||
a.Equal(tt.want.nickName, user.GetNickname())
|
a.Equal(tt.want.nickName, user.GetNickname())
|
||||||
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
||||||
a.Equal(tt.want.email, user.GetEmail())
|
a.Equal(domain.EmailAddress(tt.want.email), user.GetEmail())
|
||||||
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
||||||
a.Equal(tt.want.phone, user.GetPhone())
|
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||||
|
@ -43,5 +43,5 @@ type User struct {
|
|||||||
// GetPreferredUsername implements the [idp.User] interface.
|
// GetPreferredUsername implements the [idp.User] interface.
|
||||||
// It returns the email, because Google does not return a username.
|
// It returns the email, because Google does not return a username.
|
||||||
func (u *User) GetPreferredUsername() string {
|
func (u *User) GetPreferredUsername() string {
|
||||||
return u.GetEmail()
|
return string(u.GetEmail())
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/idp/providers/oidc"
|
"github.com/zitadel/zitadel/internal/idp/providers/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -188,9 +189,9 @@ func TestSession_FetchUser(t *testing.T) {
|
|||||||
a.Equal(tt.want.displayName, user.GetDisplayName())
|
a.Equal(tt.want.displayName, user.GetDisplayName())
|
||||||
a.Equal(tt.want.nickName, user.GetNickname())
|
a.Equal(tt.want.nickName, user.GetNickname())
|
||||||
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
||||||
a.Equal(tt.want.email, user.GetEmail())
|
a.Equal(domain.EmailAddress(tt.want.email), user.GetEmail())
|
||||||
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
||||||
a.Equal(tt.want.phone, user.GetPhone())
|
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/idp"
|
"github.com/zitadel/zitadel/internal/idp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -106,8 +107,8 @@ func (u *User) GetNickname() string {
|
|||||||
return u.IDTokenClaims.GetNickname()
|
return u.IDTokenClaims.GetNickname()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) GetPhone() string {
|
func (u *User) GetPhone() domain.PhoneNumber {
|
||||||
return u.IDTokenClaims.GetPhoneNumber()
|
return domain.PhoneNumber(u.IDTokenClaims.GetPhoneNumber())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) IsPhoneVerified() bool {
|
func (u *User) IsPhoneVerified() bool {
|
||||||
@ -121,3 +122,7 @@ func (u *User) GetPreferredLanguage() language.Tag {
|
|||||||
func (u *User) GetAvatarURL() string {
|
func (u *User) GetAvatarURL() string {
|
||||||
return u.IDTokenClaims.GetPicture()
|
return u.IDTokenClaims.GetPicture()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) GetEmail() domain.EmailAddress {
|
||||||
|
return domain.EmailAddress(u.IDTokenClaims.GetEmail())
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"gopkg.in/square/go-jose.v2"
|
"gopkg.in/square/go-jose.v2"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSession_FetchUser(t *testing.T) {
|
func TestSession_FetchUser(t *testing.T) {
|
||||||
@ -193,9 +194,9 @@ func TestSession_FetchUser(t *testing.T) {
|
|||||||
a.Equal(tt.want.displayName, user.GetDisplayName())
|
a.Equal(tt.want.displayName, user.GetDisplayName())
|
||||||
a.Equal(tt.want.nickName, user.GetNickname())
|
a.Equal(tt.want.nickName, user.GetNickname())
|
||||||
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
||||||
a.Equal(tt.want.email, user.GetEmail())
|
a.Equal(domain.EmailAddress(tt.want.email), user.GetEmail())
|
||||||
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
||||||
a.Equal(tt.want.phone, user.GetPhone())
|
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/go-ldap/ldap/v3"
|
"github.com/go-ldap/ldap/v3"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/idp"
|
"github.com/zitadel/zitadel/internal/idp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -87,9 +88,9 @@ func (s *Session) FetchUser(_ context.Context) (idp.User, error) {
|
|||||||
user.GetAttributeValue(s.Provider.displayNameAttribute),
|
user.GetAttributeValue(s.Provider.displayNameAttribute),
|
||||||
user.GetAttributeValue(s.Provider.nickNameAttribute),
|
user.GetAttributeValue(s.Provider.nickNameAttribute),
|
||||||
user.GetAttributeValue(s.Provider.preferredUsernameAttribute),
|
user.GetAttributeValue(s.Provider.preferredUsernameAttribute),
|
||||||
user.GetAttributeValue(s.Provider.emailAttribute),
|
domain.EmailAddress(user.GetAttributeValue(s.Provider.emailAttribute)),
|
||||||
emailVerified,
|
emailVerified,
|
||||||
user.GetAttributeValue(s.Provider.phoneAttribute),
|
domain.PhoneNumber(user.GetAttributeValue(s.Provider.phoneAttribute)),
|
||||||
phoneVerified,
|
phoneVerified,
|
||||||
language.Make(user.GetAttributeValue(s.Provider.preferredLanguageAttribute)),
|
language.Make(user.GetAttributeValue(s.Provider.preferredLanguageAttribute)),
|
||||||
user.GetAttributeValue(s.Provider.avatarURLAttribute),
|
user.GetAttributeValue(s.Provider.avatarURLAttribute),
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package ldap
|
package ldap
|
||||||
|
|
||||||
import "golang.org/x/text/language"
|
import (
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
id string
|
id string
|
||||||
@ -9,9 +13,9 @@ type User struct {
|
|||||||
displayName string
|
displayName string
|
||||||
nickName string
|
nickName string
|
||||||
preferredUsername string
|
preferredUsername string
|
||||||
email string
|
email domain.EmailAddress
|
||||||
emailVerified bool
|
emailVerified bool
|
||||||
phone string
|
phone domain.PhoneNumber
|
||||||
phoneVerified bool
|
phoneVerified bool
|
||||||
preferredLanguage language.Tag
|
preferredLanguage language.Tag
|
||||||
avatarURL string
|
avatarURL string
|
||||||
@ -25,9 +29,9 @@ func NewUser(
|
|||||||
displayName string,
|
displayName string,
|
||||||
nickName string,
|
nickName string,
|
||||||
preferredUsername string,
|
preferredUsername string,
|
||||||
email string,
|
email domain.EmailAddress,
|
||||||
emailVerified bool,
|
emailVerified bool,
|
||||||
phone string,
|
phone domain.PhoneNumber,
|
||||||
phoneVerified bool,
|
phoneVerified bool,
|
||||||
preferredLanguage language.Tag,
|
preferredLanguage language.Tag,
|
||||||
avatarURL string,
|
avatarURL string,
|
||||||
@ -68,13 +72,13 @@ func (u *User) GetNickname() string {
|
|||||||
func (u *User) GetPreferredUsername() string {
|
func (u *User) GetPreferredUsername() string {
|
||||||
return u.preferredUsername
|
return u.preferredUsername
|
||||||
}
|
}
|
||||||
func (u *User) GetEmail() string {
|
func (u *User) GetEmail() domain.EmailAddress {
|
||||||
return u.email
|
return u.email
|
||||||
}
|
}
|
||||||
func (u *User) IsEmailVerified() bool {
|
func (u *User) IsEmailVerified() bool {
|
||||||
return u.emailVerified
|
return u.emailVerified
|
||||||
}
|
}
|
||||||
func (u *User) GetPhone() string {
|
func (u *User) GetPhone() domain.PhoneNumber {
|
||||||
return u.phone
|
return u.phone
|
||||||
}
|
}
|
||||||
func (u *User) IsPhoneVerified() bool {
|
func (u *User) IsPhoneVerified() bool {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/idp"
|
"github.com/zitadel/zitadel/internal/idp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ func (u *UserMapper) GetPreferredUsername() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetEmail is an implementation of the [idp.User] interface.
|
// GetEmail is an implementation of the [idp.User] interface.
|
||||||
func (u *UserMapper) GetEmail() string {
|
func (u *UserMapper) GetEmail() domain.EmailAddress {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +85,7 @@ func (u *UserMapper) IsEmailVerified() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPhone is an implementation of the [idp.User] interface.
|
// GetPhone is an implementation of the [idp.User] interface.
|
||||||
func (u *UserMapper) GetPhone() string {
|
func (u *UserMapper) GetPhone() domain.PhoneNumber {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/idp"
|
"github.com/zitadel/zitadel/internal/idp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -260,9 +261,9 @@ func TestProvider_FetchUser(t *testing.T) {
|
|||||||
a.Equal(tt.want.displayName, user.GetDisplayName())
|
a.Equal(tt.want.displayName, user.GetDisplayName())
|
||||||
a.Equal(tt.want.nickName, user.GetNickname())
|
a.Equal(tt.want.nickName, user.GetNickname())
|
||||||
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
||||||
a.Equal(tt.want.email, user.GetEmail())
|
a.Equal(domain.EmailAddress(tt.want.email), user.GetEmail())
|
||||||
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
||||||
a.Equal(tt.want.phone, user.GetPhone())
|
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/idp"
|
"github.com/zitadel/zitadel/internal/idp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -82,8 +83,8 @@ func (u *User) GetDisplayName() string {
|
|||||||
return u.GetName()
|
return u.GetName()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) GetPhone() string {
|
func (u *User) GetPhone() domain.PhoneNumber {
|
||||||
return u.GetPhoneNumber()
|
return domain.PhoneNumber(u.GetPhoneNumber())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) IsPhoneVerified() bool {
|
func (u *User) IsPhoneVerified() bool {
|
||||||
@ -97,3 +98,7 @@ func (u *User) GetPreferredLanguage() language.Tag {
|
|||||||
func (u *User) GetAvatarURL() string {
|
func (u *User) GetAvatarURL() string {
|
||||||
return u.GetPicture()
|
return u.GetPicture()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) GetEmail() domain.EmailAddress {
|
||||||
|
return domain.EmailAddress(u.UserInfo.GetEmail())
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"gopkg.in/square/go-jose.v2"
|
"gopkg.in/square/go-jose.v2"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/idp"
|
"github.com/zitadel/zitadel/internal/idp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -281,9 +282,9 @@ func TestSession_FetchUser(t *testing.T) {
|
|||||||
a.Equal(tt.want.displayName, user.GetDisplayName())
|
a.Equal(tt.want.displayName, user.GetDisplayName())
|
||||||
a.Equal(tt.want.nickName, user.GetNickname())
|
a.Equal(tt.want.nickName, user.GetNickname())
|
||||||
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
a.Equal(tt.want.preferredUsername, user.GetPreferredUsername())
|
||||||
a.Equal(tt.want.email, user.GetEmail())
|
a.Equal(domain.EmailAddress(tt.want.email), user.GetEmail())
|
||||||
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
a.Equal(tt.want.isEmailVerified, user.IsEmailVerified())
|
||||||
a.Equal(tt.want.phone, user.GetPhone())
|
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||||
|
@ -342,7 +342,7 @@ func (p *userProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat
|
|||||||
handler.NewCol(HumanPreferredLanguageCol, &sql.NullString{String: e.PreferredLanguage.String(), Valid: !e.PreferredLanguage.IsRoot()}),
|
handler.NewCol(HumanPreferredLanguageCol, &sql.NullString{String: e.PreferredLanguage.String(), Valid: !e.PreferredLanguage.IsRoot()}),
|
||||||
handler.NewCol(HumanGenderCol, &sql.NullInt16{Int16: int16(e.Gender), Valid: e.Gender.Specified()}),
|
handler.NewCol(HumanGenderCol, &sql.NullInt16{Int16: int16(e.Gender), Valid: e.Gender.Specified()}),
|
||||||
handler.NewCol(HumanEmailCol, e.EmailAddress),
|
handler.NewCol(HumanEmailCol, e.EmailAddress),
|
||||||
handler.NewCol(HumanPhoneCol, &sql.NullString{String: e.PhoneNumber, Valid: e.PhoneNumber != ""}),
|
handler.NewCol(HumanPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}),
|
||||||
},
|
},
|
||||||
crdb.WithTableSuffix(UserHumanSuffix),
|
crdb.WithTableSuffix(UserHumanSuffix),
|
||||||
),
|
),
|
||||||
@ -351,7 +351,7 @@ func (p *userProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat
|
|||||||
handler.NewCol(NotifyUserIDCol, e.Aggregate().ID),
|
handler.NewCol(NotifyUserIDCol, e.Aggregate().ID),
|
||||||
handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID),
|
handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID),
|
||||||
handler.NewCol(NotifyLastEmailCol, e.EmailAddress),
|
handler.NewCol(NotifyLastEmailCol, e.EmailAddress),
|
||||||
handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: e.PhoneNumber, Valid: e.PhoneNumber != ""}),
|
handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}),
|
||||||
handler.NewCol(NotifyPasswordSetCol, e.Secret != nil),
|
handler.NewCol(NotifyPasswordSetCol, e.Secret != nil),
|
||||||
},
|
},
|
||||||
crdb.WithTableSuffix(UserNotifySuffix),
|
crdb.WithTableSuffix(UserNotifySuffix),
|
||||||
@ -390,7 +390,7 @@ func (p *userProjection) reduceHumanRegistered(event eventstore.Event) (*handler
|
|||||||
handler.NewCol(HumanPreferredLanguageCol, &sql.NullString{String: e.PreferredLanguage.String(), Valid: !e.PreferredLanguage.IsRoot()}),
|
handler.NewCol(HumanPreferredLanguageCol, &sql.NullString{String: e.PreferredLanguage.String(), Valid: !e.PreferredLanguage.IsRoot()}),
|
||||||
handler.NewCol(HumanGenderCol, &sql.NullInt16{Int16: int16(e.Gender), Valid: e.Gender.Specified()}),
|
handler.NewCol(HumanGenderCol, &sql.NullInt16{Int16: int16(e.Gender), Valid: e.Gender.Specified()}),
|
||||||
handler.NewCol(HumanEmailCol, e.EmailAddress),
|
handler.NewCol(HumanEmailCol, e.EmailAddress),
|
||||||
handler.NewCol(HumanPhoneCol, &sql.NullString{String: e.PhoneNumber, Valid: e.PhoneNumber != ""}),
|
handler.NewCol(HumanPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}),
|
||||||
},
|
},
|
||||||
crdb.WithTableSuffix(UserHumanSuffix),
|
crdb.WithTableSuffix(UserHumanSuffix),
|
||||||
),
|
),
|
||||||
@ -399,7 +399,7 @@ func (p *userProjection) reduceHumanRegistered(event eventstore.Event) (*handler
|
|||||||
handler.NewCol(NotifyUserIDCol, e.Aggregate().ID),
|
handler.NewCol(NotifyUserIDCol, e.Aggregate().ID),
|
||||||
handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID),
|
handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID),
|
||||||
handler.NewCol(NotifyLastEmailCol, e.EmailAddress),
|
handler.NewCol(NotifyLastEmailCol, e.EmailAddress),
|
||||||
handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: e.PhoneNumber, Valid: e.PhoneNumber != ""}),
|
handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}),
|
||||||
handler.NewCol(NotifyPasswordSetCol, e.Secret != nil),
|
handler.NewCol(NotifyPasswordSetCol, e.Secret != nil),
|
||||||
},
|
},
|
||||||
crdb.WithTableSuffix(UserNotifySuffix),
|
crdb.WithTableSuffix(UserNotifySuffix),
|
||||||
@ -660,7 +660,7 @@ func (p *userProjection) reduceHumanPhoneChanged(event eventstore.Event) (*handl
|
|||||||
),
|
),
|
||||||
crdb.AddUpdateStatement(
|
crdb.AddUpdateStatement(
|
||||||
[]handler.Column{
|
[]handler.Column{
|
||||||
handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: e.PhoneNumber, Valid: e.PhoneNumber != ""}),
|
handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}),
|
||||||
},
|
},
|
||||||
[]handler.Condition{
|
[]handler.Condition{
|
||||||
handler.NewCond(NotifyUserIDCol, e.Aggregate().ID),
|
handler.NewCond(NotifyUserIDCol, e.Aggregate().ID),
|
||||||
@ -786,7 +786,7 @@ func (p *userProjection) reduceHumanEmailChanged(event eventstore.Event) (*handl
|
|||||||
),
|
),
|
||||||
crdb.AddUpdateStatement(
|
crdb.AddUpdateStatement(
|
||||||
[]handler.Column{
|
[]handler.Column{
|
||||||
handler.NewCol(NotifyLastEmailCol, &sql.NullString{String: e.EmailAddress, Valid: e.EmailAddress != ""}),
|
handler.NewCol(NotifyLastEmailCol, &sql.NullString{String: string(e.EmailAddress), Valid: e.EmailAddress != ""}),
|
||||||
},
|
},
|
||||||
[]handler.Condition{
|
[]handler.Condition{
|
||||||
handler.NewCond(NotifyUserIDCol, e.Aggregate().ID),
|
handler.NewCond(NotifyUserIDCol, e.Aggregate().ID),
|
||||||
|
@ -75,7 +75,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
&sql.NullString{String: "display-name", Valid: true},
|
&sql.NullString{String: "display-name", Valid: true},
|
||||||
&sql.NullString{String: "ch-DE", Valid: true},
|
&sql.NullString{String: "ch-DE", Valid: true},
|
||||||
&sql.NullInt16{Int16: int16(domain.GenderFemale), Valid: true},
|
&sql.NullInt16{Int16: int16(domain.GenderFemale), Valid: true},
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -84,7 +84,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
@ -144,7 +144,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
&sql.NullString{String: "display-name", Valid: true},
|
&sql.NullString{String: "display-name", Valid: true},
|
||||||
&sql.NullString{String: "ch-DE", Valid: true},
|
&sql.NullString{String: "ch-DE", Valid: true},
|
||||||
&sql.NullInt16{Int16: int16(domain.GenderFemale), Valid: true},
|
&sql.NullInt16{Int16: int16(domain.GenderFemale), Valid: true},
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -153,7 +153,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
@ -208,7 +208,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
&sql.NullString{},
|
&sql.NullString{},
|
||||||
&sql.NullString{String: "und", Valid: false},
|
&sql.NullString{String: "und", Valid: false},
|
||||||
&sql.NullInt16{},
|
&sql.NullInt16{},
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{},
|
&sql.NullString{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -217,7 +217,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{String: "", Valid: false},
|
&sql.NullString{String: "", Valid: false},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
@ -277,7 +277,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
&sql.NullString{String: "display-name", Valid: true},
|
&sql.NullString{String: "display-name", Valid: true},
|
||||||
&sql.NullString{String: "ch-DE", Valid: true},
|
&sql.NullString{String: "ch-DE", Valid: true},
|
||||||
&sql.NullInt16{Int16: int16(domain.GenderFemale), Valid: true},
|
&sql.NullInt16{Int16: int16(domain.GenderFemale), Valid: true},
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -286,7 +286,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
@ -346,7 +346,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
&sql.NullString{String: "display-name", Valid: true},
|
&sql.NullString{String: "display-name", Valid: true},
|
||||||
&sql.NullString{String: "ch-DE", Valid: true},
|
&sql.NullString{String: "ch-DE", Valid: true},
|
||||||
&sql.NullInt16{Int16: int16(domain.GenderFemale), Valid: true},
|
&sql.NullInt16{Int16: int16(domain.GenderFemale), Valid: true},
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -355,7 +355,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
@ -410,7 +410,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
&sql.NullString{},
|
&sql.NullString{},
|
||||||
&sql.NullString{String: "und", Valid: false},
|
&sql.NullString{String: "und", Valid: false},
|
||||||
&sql.NullInt16{},
|
&sql.NullInt16{},
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{},
|
&sql.NullString{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -419,7 +419,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
&sql.NullString{String: "", Valid: false},
|
&sql.NullString{String: "", Valid: false},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
@ -879,7 +879,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.users8_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
expectedStmt: "UPDATE projections.users8_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"+41 00 000 00 00",
|
domain.PhoneNumber("+41 00 000 00 00"),
|
||||||
false,
|
false,
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
@ -927,7 +927,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.users8_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
expectedStmt: "UPDATE projections.users8_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"+41 00 000 00 00",
|
domain.PhoneNumber("+41 00 000 00 00"),
|
||||||
false,
|
false,
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
@ -1157,7 +1157,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.users8_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
expectedStmt: "UPDATE projections.users8_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
false,
|
false,
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
@ -1205,7 +1205,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
|||||||
{
|
{
|
||||||
expectedStmt: "UPDATE projections.users8_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
expectedStmt: "UPDATE projections.users8_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)",
|
||||||
expectedArgs: []interface{}{
|
expectedArgs: []interface{}{
|
||||||
"email@zitadel.com",
|
domain.EmailAddress("email@zitadel.com"),
|
||||||
false,
|
false,
|
||||||
"agg-id",
|
"agg-id",
|
||||||
"instance-id",
|
"instance-id",
|
||||||
|
@ -47,9 +47,9 @@ type Human struct {
|
|||||||
AvatarKey string
|
AvatarKey string
|
||||||
PreferredLanguage language.Tag
|
PreferredLanguage language.Tag
|
||||||
Gender domain.Gender
|
Gender domain.Gender
|
||||||
Email string
|
Email domain.EmailAddress
|
||||||
IsEmailVerified bool
|
IsEmailVerified bool
|
||||||
Phone string
|
Phone domain.PhoneNumber
|
||||||
IsPhoneVerified bool
|
IsPhoneVerified bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ type Email struct {
|
|||||||
ChangeDate time.Time
|
ChangeDate time.Time
|
||||||
ResourceOwner string
|
ResourceOwner string
|
||||||
Sequence uint64
|
Sequence uint64
|
||||||
Email string
|
Email domain.EmailAddress
|
||||||
IsVerified bool
|
IsVerified bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -847,9 +847,9 @@ func prepareUserQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder
|
|||||||
AvatarKey: avatarKey.String,
|
AvatarKey: avatarKey.String,
|
||||||
PreferredLanguage: language.Make(preferredLanguage.String),
|
PreferredLanguage: language.Make(preferredLanguage.String),
|
||||||
Gender: domain.Gender(gender.Int32),
|
Gender: domain.Gender(gender.Int32),
|
||||||
Email: email.String,
|
Email: domain.EmailAddress(email.String),
|
||||||
IsEmailVerified: isEmailVerified.Bool,
|
IsEmailVerified: isEmailVerified.Bool,
|
||||||
Phone: phone.String,
|
Phone: domain.PhoneNumber(phone.String),
|
||||||
IsPhoneVerified: isPhoneVerified.Bool,
|
IsPhoneVerified: isPhoneVerified.Bool,
|
||||||
}
|
}
|
||||||
} else if machineID.Valid {
|
} else if machineID.Valid {
|
||||||
@ -970,7 +970,7 @@ func prepareEmailQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilde
|
|||||||
return nil, errors.ThrowPreconditionFailed(nil, "QUERY-pt7HY", "Errors.User.NotHuman")
|
return nil, errors.ThrowPreconditionFailed(nil, "QUERY-pt7HY", "Errors.User.NotHuman")
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Email = email.String
|
e.Email = domain.EmailAddress(email.String)
|
||||||
e.IsVerified = isEmailVerified.Bool
|
e.IsVerified = isEmailVerified.Bool
|
||||||
|
|
||||||
return e, nil
|
return e, nil
|
||||||
@ -1318,9 +1318,9 @@ func prepareUsersQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilde
|
|||||||
AvatarKey: avatarKey.String,
|
AvatarKey: avatarKey.String,
|
||||||
PreferredLanguage: language.Make(preferredLanguage.String),
|
PreferredLanguage: language.Make(preferredLanguage.String),
|
||||||
Gender: domain.Gender(gender.Int32),
|
Gender: domain.Gender(gender.Int32),
|
||||||
Email: email.String,
|
Email: domain.EmailAddress(email.String),
|
||||||
IsEmailVerified: isEmailVerified.Bool,
|
IsEmailVerified: isEmailVerified.Bool,
|
||||||
Phone: phone.String,
|
Phone: domain.PhoneNumber(phone.String),
|
||||||
IsPhoneVerified: isPhoneVerified.Bool,
|
IsPhoneVerified: isPhoneVerified.Bool,
|
||||||
}
|
}
|
||||||
} else if machineID.Valid {
|
} else if machineID.Valid {
|
||||||
|
@ -38,9 +38,9 @@ type HumanAddedEvent struct {
|
|||||||
PreferredLanguage language.Tag `json:"preferredLanguage,omitempty"`
|
PreferredLanguage language.Tag `json:"preferredLanguage,omitempty"`
|
||||||
Gender domain.Gender `json:"gender,omitempty"`
|
Gender domain.Gender `json:"gender,omitempty"`
|
||||||
|
|
||||||
EmailAddress string `json:"email,omitempty"`
|
EmailAddress domain.EmailAddress `json:"email,omitempty"`
|
||||||
|
|
||||||
PhoneNumber string `json:"phone,omitempty"`
|
PhoneNumber domain.PhoneNumber `json:"phone,omitempty"`
|
||||||
|
|
||||||
Country string `json:"country,omitempty"`
|
Country string `json:"country,omitempty"`
|
||||||
Locality string `json:"locality,omitempty"`
|
Locality string `json:"locality,omitempty"`
|
||||||
@ -75,7 +75,7 @@ func (e *HumanAddedEvent) AddAddressData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *HumanAddedEvent) AddPhoneData(
|
func (e *HumanAddedEvent) AddPhoneData(
|
||||||
phoneNumber string,
|
phoneNumber domain.PhoneNumber,
|
||||||
) {
|
) {
|
||||||
e.PhoneNumber = phoneNumber
|
e.PhoneNumber = phoneNumber
|
||||||
}
|
}
|
||||||
@ -99,7 +99,7 @@ func NewHumanAddedEvent(
|
|||||||
displayName string,
|
displayName string,
|
||||||
preferredLanguage language.Tag,
|
preferredLanguage language.Tag,
|
||||||
gender domain.Gender,
|
gender domain.Gender,
|
||||||
emailAddress string,
|
emailAddress domain.EmailAddress,
|
||||||
userLoginMustBeDomain bool,
|
userLoginMustBeDomain bool,
|
||||||
) *HumanAddedEvent {
|
) *HumanAddedEvent {
|
||||||
return &HumanAddedEvent{
|
return &HumanAddedEvent{
|
||||||
@ -133,30 +133,24 @@ func HumanAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HumanRegisteredEvent struct {
|
type HumanRegisteredEvent struct {
|
||||||
eventstore.BaseEvent `json:"-"`
|
eventstore.BaseEvent `json:"-"`
|
||||||
|
|
||||||
UserName string `json:"userName"`
|
UserName string `json:"userName"`
|
||||||
userLoginMustBeDomain bool
|
userLoginMustBeDomain bool
|
||||||
|
FirstName string `json:"firstName,omitempty"`
|
||||||
FirstName string `json:"firstName,omitempty"`
|
LastName string `json:"lastName,omitempty"`
|
||||||
LastName string `json:"lastName,omitempty"`
|
NickName string `json:"nickName,omitempty"`
|
||||||
NickName string `json:"nickName,omitempty"`
|
DisplayName string `json:"displayName,omitempty"`
|
||||||
DisplayName string `json:"displayName,omitempty"`
|
PreferredLanguage language.Tag `json:"preferredLanguage,omitempty"`
|
||||||
PreferredLanguage language.Tag `json:"preferredLanguage,omitempty"`
|
Gender domain.Gender `json:"gender,omitempty"`
|
||||||
Gender domain.Gender `json:"gender,omitempty"`
|
EmailAddress domain.EmailAddress `json:"email,omitempty"`
|
||||||
|
PhoneNumber domain.PhoneNumber `json:"phone,omitempty"`
|
||||||
EmailAddress string `json:"email,omitempty"`
|
Country string `json:"country,omitempty"`
|
||||||
|
Locality string `json:"locality,omitempty"`
|
||||||
PhoneNumber string `json:"phone,omitempty"`
|
PostalCode string `json:"postalCode,omitempty"`
|
||||||
|
Region string `json:"region,omitempty"`
|
||||||
Country string `json:"country,omitempty"`
|
StreetAddress string `json:"streetAddress,omitempty"`
|
||||||
Locality string `json:"locality,omitempty"`
|
Secret *crypto.CryptoValue `json:"secret,omitempty"`
|
||||||
PostalCode string `json:"postalCode,omitempty"`
|
ChangeRequired bool `json:"changeRequired,omitempty"`
|
||||||
Region string `json:"region,omitempty"`
|
|
||||||
StreetAddress string `json:"streetAddress,omitempty"`
|
|
||||||
|
|
||||||
Secret *crypto.CryptoValue `json:"secret,omitempty"`
|
|
||||||
ChangeRequired bool `json:"changeRequired,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *HumanRegisteredEvent) Data() interface{} {
|
func (e *HumanRegisteredEvent) Data() interface{} {
|
||||||
@ -182,7 +176,7 @@ func (e *HumanRegisteredEvent) AddAddressData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *HumanRegisteredEvent) AddPhoneData(
|
func (e *HumanRegisteredEvent) AddPhoneData(
|
||||||
phoneNumber string,
|
phoneNumber domain.PhoneNumber,
|
||||||
) {
|
) {
|
||||||
e.PhoneNumber = phoneNumber
|
e.PhoneNumber = phoneNumber
|
||||||
}
|
}
|
||||||
@ -206,7 +200,7 @@ func NewHumanRegisteredEvent(
|
|||||||
displayName string,
|
displayName string,
|
||||||
preferredLanguage language.Tag,
|
preferredLanguage language.Tag,
|
||||||
gender domain.Gender,
|
gender domain.Gender,
|
||||||
emailAddress string,
|
emailAddress domain.EmailAddress,
|
||||||
userLoginMustBeDomain bool,
|
userLoginMustBeDomain bool,
|
||||||
) *HumanRegisteredEvent {
|
) *HumanRegisteredEvent {
|
||||||
return &HumanRegisteredEvent{
|
return &HumanRegisteredEvent{
|
||||||
|
@ -5,10 +5,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/errors"
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ const (
|
|||||||
type HumanEmailChangedEvent struct {
|
type HumanEmailChangedEvent struct {
|
||||||
eventstore.BaseEvent `json:"-"`
|
eventstore.BaseEvent `json:"-"`
|
||||||
|
|
||||||
EmailAddress string `json:"email,omitempty"`
|
EmailAddress domain.EmailAddress `json:"email,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *HumanEmailChangedEvent) Data() interface{} {
|
func (e *HumanEmailChangedEvent) Data() interface{} {
|
||||||
@ -35,7 +35,7 @@ func (e *HumanEmailChangedEvent) UniqueConstraints() []*eventstore.EventUniqueCo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHumanEmailChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, emailAddress string) *HumanEmailChangedEvent {
|
func NewHumanEmailChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, emailAddress domain.EmailAddress) *HumanEmailChangedEvent {
|
||||||
return &HumanEmailChangedEvent{
|
return &HumanEmailChangedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/errors"
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore"
|
"github.com/zitadel/zitadel/internal/eventstore"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||||
@ -24,7 +25,7 @@ const (
|
|||||||
type HumanPhoneChangedEvent struct {
|
type HumanPhoneChangedEvent struct {
|
||||||
eventstore.BaseEvent `json:"-"`
|
eventstore.BaseEvent `json:"-"`
|
||||||
|
|
||||||
PhoneNumber string `json:"phone,omitempty"`
|
PhoneNumber domain.PhoneNumber `json:"phone,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *HumanPhoneChangedEvent) Data() interface{} {
|
func (e *HumanPhoneChangedEvent) Data() interface{} {
|
||||||
@ -35,7 +36,7 @@ func (e *HumanPhoneChangedEvent) UniqueConstraints() []*eventstore.EventUniqueCo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHumanPhoneChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, phone string) *HumanPhoneChangedEvent {
|
func NewHumanPhoneChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, phone domain.PhoneNumber) *HumanPhoneChangedEvent {
|
||||||
return &HumanPhoneChangedEvent{
|
return &HumanPhoneChangedEvent{
|
||||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -69,17 +69,24 @@ Errors:
|
|||||||
UsernameNotChanged: Benutzername wurde nicht verändert
|
UsernameNotChanged: Benutzername wurde nicht verändert
|
||||||
Profile:
|
Profile:
|
||||||
NotFound: Profil nicht gefunden
|
NotFound: Profil nicht gefunden
|
||||||
NotChanged: Profile nicht verändert
|
NotChanged: Profil nicht verändert
|
||||||
Invalid: Profildaten sind ungültig
|
Empty: Profil ist leer
|
||||||
|
FirstNameEmpty: Vorname im Profil ist leer
|
||||||
|
LastNameEmpty: Nachname im Profil ist leer
|
||||||
|
IDMissing: Profil ID fehlt
|
||||||
Email:
|
Email:
|
||||||
NotFound: Email nicht gefunden
|
NotFound: Email nicht gefunden
|
||||||
Invalid: Email ist ungültig
|
Invalid: Email ist ungültig
|
||||||
AlreadyVerified: Email ist bereits verifiziert
|
AlreadyVerified: Email ist bereits verifiziert
|
||||||
NotChanged: Email wurde nicht geändert
|
NotChanged: Email wurde nicht geändert
|
||||||
|
Empty: Email ist leer
|
||||||
|
IDMissing: Email ID fehlt
|
||||||
Phone:
|
Phone:
|
||||||
NotFound: Telefonnummer nicht gefunden
|
NotFound: Telefonnummer nicht gefunden
|
||||||
Invalid: Telefonnummer ist ungültig
|
Invalid: Telefonnummer ist ungültig
|
||||||
AlreadyVerified: Telefonnummer bereits verifiziert
|
AlreadyVerified: Telefonnummer bereits verifiziert
|
||||||
|
Empty: Telefonnummer ist leer
|
||||||
|
NotChanged: Telefonnummer wurde nicht geändert
|
||||||
Address:
|
Address:
|
||||||
NotFound: Adresse nicht gefunden
|
NotFound: Adresse nicht gefunden
|
||||||
NotChanged: Adresse wurde nicht geändert
|
NotChanged: Adresse wurde nicht geändert
|
||||||
@ -100,6 +107,7 @@ Errors:
|
|||||||
Username:
|
Username:
|
||||||
AlreadyExists: Benutzername ist bereits vergeben
|
AlreadyExists: Benutzername ist bereits vergeben
|
||||||
Reserved: Benutzername ist bereits vergeben
|
Reserved: Benutzername ist bereits vergeben
|
||||||
|
Empty: Benutzername ist leer
|
||||||
Code:
|
Code:
|
||||||
Empty: Code ist leer
|
Empty: Code ist leer
|
||||||
NotFound: Code konnte nicht gefunden werden
|
NotFound: Code konnte nicht gefunden werden
|
||||||
|
@ -70,16 +70,23 @@ Errors:
|
|||||||
Profile:
|
Profile:
|
||||||
NotFound: Profile not found
|
NotFound: Profile not found
|
||||||
NotChanged: Profile not changed
|
NotChanged: Profile not changed
|
||||||
Invalid: Profile data invalid
|
Empty: Profile is empty
|
||||||
|
FirstNameEmpty: First name in profile is empty
|
||||||
|
LastNameEmpty: Last name in profile is empty
|
||||||
|
IDMissing: Profile ID is missing
|
||||||
Email:
|
Email:
|
||||||
NotFound: Email not found
|
NotFound: Email not found
|
||||||
Invalid: Email is invalid
|
Invalid: Email is invalid
|
||||||
AlreadyVerified: Email is already verified
|
AlreadyVerified: Email is already verified
|
||||||
NotChanged: Email not changed
|
NotChanged: Email not changed
|
||||||
|
Empty: Email is empty
|
||||||
|
IDMissing: Email ID is missing
|
||||||
Phone:
|
Phone:
|
||||||
NotFound: Phone not found
|
NotFound: Phone not found
|
||||||
Invalid: Phone is invalid
|
Invalid: Phone is invalid
|
||||||
AlreadyVerified: Phone already verified
|
AlreadyVerified: Phone already verified
|
||||||
|
Empty: Phone is empty
|
||||||
|
NotChanged: Phone not changed
|
||||||
Address:
|
Address:
|
||||||
NotFound: Address not found
|
NotFound: Address not found
|
||||||
NotChanged: Address not changed
|
NotChanged: Address not changed
|
||||||
@ -100,6 +107,7 @@ Errors:
|
|||||||
Username:
|
Username:
|
||||||
AlreadyExists: Username already taken
|
AlreadyExists: Username already taken
|
||||||
Reserved: Username is already taken
|
Reserved: Username is already taken
|
||||||
|
Empty: Username is empty
|
||||||
Code:
|
Code:
|
||||||
Empty: Code is empty
|
Empty: Code is empty
|
||||||
NotFound: Code not found
|
NotFound: Code not found
|
||||||
|
@ -70,16 +70,23 @@ Errors:
|
|||||||
Profile:
|
Profile:
|
||||||
NotFound: Profil non trouvé
|
NotFound: Profil non trouvé
|
||||||
NotChanged: Le profil n'a pas changé
|
NotChanged: Le profil n'a pas changé
|
||||||
Invalid: Données de profil non valides
|
Empty: Profil est vide
|
||||||
|
FirstNameEmpty: Le prénom dans le profil est vide
|
||||||
|
LastNameEmpty: Le nom de famille dans le profil est vide
|
||||||
|
IDMissing: Profil ID manquant
|
||||||
Email:
|
Email:
|
||||||
NotFound: Email non trouvé
|
NotFound: Email non trouvé
|
||||||
Invalid: L'email n'est pas valide
|
Invalid: L'email n'est pas valide
|
||||||
AlreadyVerified: L'adresse électronique est déjà vérifiée
|
AlreadyVerified: L'adresse électronique est déjà vérifiée
|
||||||
NotChanged: L'adresse électronique n'a pas changé
|
NotChanged: L'adresse électronique n'a pas changé
|
||||||
|
Empty: Email est vide
|
||||||
|
IDMissing: Email ID manquant
|
||||||
Phone:
|
Phone:
|
||||||
Notfound: Téléphone non trouvé
|
Notfound: Téléphone non trouvé
|
||||||
Invalid: Le téléphone n'est pas valide
|
Invalid: Le téléphone n'est pas valide
|
||||||
AlreadyVerified: Téléphone déjà vérifié
|
AlreadyVerified: Téléphone déjà vérifié
|
||||||
|
Empty: Téléphone est vide
|
||||||
|
NotChanged: Téléphone n'a pas changé
|
||||||
Address:
|
Address:
|
||||||
NotFound: Adresse non trouvée
|
NotFound: Adresse non trouvée
|
||||||
NotChanged: L'adresse n'a pas changé
|
NotChanged: L'adresse n'a pas changé
|
||||||
@ -100,6 +107,7 @@ Errors:
|
|||||||
Username:
|
Username:
|
||||||
AlreadyExists: Nom d'utilisateur déjà pris
|
AlreadyExists: Nom d'utilisateur déjà pris
|
||||||
Reserved: Le nom d'utilisateur est déjà pris
|
Reserved: Le nom d'utilisateur est déjà pris
|
||||||
|
Empty: Le nom d'utilisateur est vide
|
||||||
Code:
|
Code:
|
||||||
Empty: Le code est vide
|
Empty: Le code est vide
|
||||||
NotFound: Code non trouvé
|
NotFound: Code non trouvé
|
||||||
|
@ -70,16 +70,23 @@ Errors:
|
|||||||
Profile:
|
Profile:
|
||||||
NotFound: Profilo non trovato
|
NotFound: Profilo non trovato
|
||||||
NotChanged: Profilo non cambiato
|
NotChanged: Profilo non cambiato
|
||||||
Invalid: Dati non sono validi
|
Empty: Profilo è vuoto
|
||||||
|
FirstNameEmpty: Il nome nel profilo è vuoto
|
||||||
|
LastNameEmpty: Il cognome nel profilo è vuoto
|
||||||
|
IDMissing: Profilo ID mancante
|
||||||
Email:
|
Email:
|
||||||
NotFound: Email non trovata
|
NotFound: Email non trovata
|
||||||
Invalid: L'e-mail non è valida
|
Invalid: L'e-mail non è valida
|
||||||
AlreadyVerified: L'e-mail è già verificata
|
AlreadyVerified: L'e-mail è già verificata
|
||||||
NotChanged: Email non cambiata
|
NotChanged: Email non cambiata
|
||||||
|
Empty: Email è vuota
|
||||||
|
IDMissing: Email ID mancante
|
||||||
Phone:
|
Phone:
|
||||||
NotFound: Telefono non trovato
|
NotFound: Telefono non trovato
|
||||||
Invalid: Il telefono non è valido
|
Invalid: Il telefono non è valido
|
||||||
AlreadyVerified: Telefono già verificato
|
AlreadyVerified: Telefono già verificato
|
||||||
|
Empty: Il telefono è vuoto
|
||||||
|
NotChanged: Telefono non cambiato
|
||||||
Address:
|
Address:
|
||||||
NotFound: Indirizzo non trovato
|
NotFound: Indirizzo non trovato
|
||||||
NotChanged: Indirizzo non cambiato
|
NotChanged: Indirizzo non cambiato
|
||||||
@ -100,6 +107,7 @@ Errors:
|
|||||||
Username:
|
Username:
|
||||||
AlreadyExists: Nome utente già preso
|
AlreadyExists: Nome utente già preso
|
||||||
Reserved: Il nome utente è già preso
|
Reserved: Il nome utente è già preso
|
||||||
|
Empty: Il nome utente è vuoto
|
||||||
Code:
|
Code:
|
||||||
Empty: Il codice è vuoto
|
Empty: Il codice è vuoto
|
||||||
NotFound: Codice non trovato
|
NotFound: Codice non trovato
|
||||||
|
@ -70,16 +70,23 @@ Errors:
|
|||||||
Profile:
|
Profile:
|
||||||
NotFound: Profil nie znaleziony
|
NotFound: Profil nie znaleziony
|
||||||
NotChanged: Profil nie zmieniony
|
NotChanged: Profil nie zmieniony
|
||||||
Invalid: Nieprawidłowe dane profilu
|
Empty: Profil jest pusty
|
||||||
|
FirstNameEmpty: Imię w profilu jest puste
|
||||||
|
LastNameEmpty: Nazwisko w profilu jest puste
|
||||||
|
IDMissing: Profil ID brakuje
|
||||||
Email:
|
Email:
|
||||||
NotFound: Adres e-mail nie znaleziony
|
NotFound: Adres e-mail nie znaleziony
|
||||||
Invalid: Adres e-mail jest nieprawidłowy
|
Invalid: Adres e-mail jest nieprawidłowy
|
||||||
AlreadyVerified: Adres e-mail jest już zweryfikowany
|
AlreadyVerified: Adres e-mail jest już zweryfikowany
|
||||||
NotChanged: Adres e-mail nie zmieniony
|
NotChanged: Adres e-mail nie zmieniony
|
||||||
|
Empty: Adres e-mail jest pusty
|
||||||
|
IDMissing: Adres e-mail ID brakuje
|
||||||
Phone:
|
Phone:
|
||||||
NotFound: Numer telefonu nie znaleziony
|
NotFound: Numer telefonu nie znaleziony
|
||||||
Invalid: Numer telefonu jest nieprawidłowy
|
Invalid: Numer telefonu jest nieprawidłowy
|
||||||
AlreadyVerified: Numer telefonu już zweryfikowany
|
AlreadyVerified: Numer telefonu już zweryfikowany
|
||||||
|
Empty: Numer telefonu jest pusty
|
||||||
|
NotChanged: Numer telefonu nie zmieniony
|
||||||
Address:
|
Address:
|
||||||
NotFound: Adres nie znaleziony
|
NotFound: Adres nie znaleziony
|
||||||
NotChanged: Adres nie zmieniony
|
NotChanged: Adres nie zmieniony
|
||||||
@ -100,6 +107,7 @@ Errors:
|
|||||||
Username:
|
Username:
|
||||||
AlreadyExists: Nazwa użytkownika jest już zajęta
|
AlreadyExists: Nazwa użytkownika jest już zajęta
|
||||||
Reserved: Nazwa użytkownika jest już zajęta
|
Reserved: Nazwa użytkownika jest już zajęta
|
||||||
|
Empty: Nazwa użytkownika jest pusty
|
||||||
Code:
|
Code:
|
||||||
Empty: Kod jest pusty
|
Empty: Kod jest pusty
|
||||||
NotFound: Kod nie znaleziony
|
NotFound: Kod nie znaleziony
|
||||||
|
@ -70,16 +70,23 @@ Errors:
|
|||||||
Profile:
|
Profile:
|
||||||
NotFound: 未找到个人资料
|
NotFound: 未找到个人资料
|
||||||
NotChanged: 个人资料未更改
|
NotChanged: 个人资料未更改
|
||||||
Invalid: 个人资料数据无效
|
Empty: 简介是空的
|
||||||
|
FirstNameEmpty: 简介中的名字是空的
|
||||||
|
LastNameEmpty: 简介中的姓氏是空的
|
||||||
|
IDMissing: 简介ID丢失
|
||||||
Email:
|
Email:
|
||||||
NotFound: 电子邮件没有找到
|
NotFound: 电子邮件没有找到
|
||||||
Invalid: 电子邮件无效
|
Invalid: 电子邮件无效
|
||||||
AlreadyVerified: 电子邮件已经过验证
|
AlreadyVerified: 电子邮件已经过验证
|
||||||
NotChanged: 电子邮件未更改
|
NotChanged: 电子邮件未更改
|
||||||
|
Empty: 电子邮件是空的
|
||||||
|
IDMissing: 电子邮件ID丢失
|
||||||
Phone:
|
Phone:
|
||||||
NotFound: 手机号码未找到
|
NotFound: 手机号码未找到
|
||||||
Invalid: 手机号码无效
|
Invalid: 手机号码无效
|
||||||
AlreadyVerified: 手机号码已经验证
|
AlreadyVerified: 手机号码已经验证
|
||||||
|
Empty: 电话号码是空的
|
||||||
|
NotChanged: 电话号码没有改变
|
||||||
Address:
|
Address:
|
||||||
NotFound: 找不到地址
|
NotFound: 找不到地址
|
||||||
NotChanged: 地址没有改变
|
NotChanged: 地址没有改变
|
||||||
@ -100,6 +107,7 @@ Errors:
|
|||||||
Username:
|
Username:
|
||||||
AlreadyExists: 用户名已被使用
|
AlreadyExists: 用户名已被使用
|
||||||
Reserved: 用户名已被使用
|
Reserved: 用户名已被使用
|
||||||
|
Empty: 用户名是空的
|
||||||
Code:
|
Code:
|
||||||
Empty: 验证码为空
|
Empty: 验证码为空
|
||||||
NotFound: 验证码不存在
|
NotFound: 验证码不存在
|
||||||
|
@ -21,10 +21,6 @@ type EmailCode struct {
|
|||||||
Expiry time.Duration
|
Expiry time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Email) IsValid() bool {
|
|
||||||
return e.EmailAddress != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Email) GenerateEmailCodeIfNeeded(emailGenerator crypto.Generator) (*EmailCode, error) {
|
func (e *Email) GenerateEmailCodeIfNeeded(emailGenerator crypto.Generator) (*EmailCode, error) {
|
||||||
var emailCode *EmailCode
|
var emailCode *EmailCode
|
||||||
if e.IsEmailVerified {
|
if e.IsEmailVerified {
|
||||||
|
@ -3,9 +3,7 @@ package model
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ttacon/libphonenumber"
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"github.com/zitadel/zitadel/internal/crypto"
|
||||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
|
||||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,20 +25,6 @@ type PhoneCode struct {
|
|||||||
Expiry time.Duration
|
Expiry time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Phone) IsValid() bool {
|
|
||||||
err := p.formatPhone()
|
|
||||||
return p.PhoneNumber != "" && err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Phone) formatPhone() error {
|
|
||||||
phoneNr, err := libphonenumber.Parse(p.PhoneNumber, defaultRegion)
|
|
||||||
if err != nil {
|
|
||||||
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-so0wa", "Phonenumber is invalid")
|
|
||||||
}
|
|
||||||
p.PhoneNumber = libphonenumber.Format(phoneNr, libphonenumber.E164)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Phone) GeneratePhoneCodeIfNeeded(phoneGenerator crypto.Generator) (*PhoneCode, error) {
|
func (p *Phone) GeneratePhoneCodeIfNeeded(phoneGenerator crypto.Generator) (*PhoneCode, error) {
|
||||||
var phoneCode *PhoneCode
|
var phoneCode *PhoneCode
|
||||||
if p.IsPhoneVerified {
|
if p.IsPhoneVerified {
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFormatPhoneNumber(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
phone *Phone
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
result *Phone
|
|
||||||
errFunc func(err error) bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "invalid phone number",
|
|
||||||
args: args{
|
|
||||||
phone: &Phone{
|
|
||||||
PhoneNumber: "PhoneNumber",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
errFunc: caos_errs.IsPreconditionFailed,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "format phone 071...",
|
|
||||||
args: args{
|
|
||||||
phone: &Phone{
|
|
||||||
PhoneNumber: "0711234567",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
result: &Phone{
|
|
||||||
PhoneNumber: "+41711234567",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "format phone 0041...",
|
|
||||||
args: args{
|
|
||||||
phone: &Phone{
|
|
||||||
PhoneNumber: "0041711234567",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
result: &Phone{
|
|
||||||
PhoneNumber: "+41711234567",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "format phone 071 xxx xx xx",
|
|
||||||
args: args{
|
|
||||||
phone: &Phone{
|
|
||||||
PhoneNumber: "071 123 45 67",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
result: &Phone{
|
|
||||||
PhoneNumber: "+41711234567",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "format phone +4171 xxx xx xx",
|
|
||||||
args: args{
|
|
||||||
phone: &Phone{
|
|
||||||
PhoneNumber: "+4171 123 45 67",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
result: &Phone{
|
|
||||||
PhoneNumber: "+41711234567",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "format phone 004171 xxx xx xx",
|
|
||||||
args: args{
|
|
||||||
phone: &Phone{
|
|
||||||
PhoneNumber: "004171 123 45 67",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
result: &Phone{
|
|
||||||
PhoneNumber: "+41711234567",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "format non swiss phone 004371 xxx xx xx",
|
|
||||||
args: args{
|
|
||||||
phone: &Phone{
|
|
||||||
PhoneNumber: "004371 123 45 67",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
result: &Phone{
|
|
||||||
PhoneNumber: "+43711234567",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
err := tt.args.phone.formatPhone()
|
|
||||||
|
|
||||||
if tt.errFunc == nil && tt.result.PhoneNumber != tt.args.phone.PhoneNumber {
|
|
||||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.args.phone.PhoneNumber, tt.result.PhoneNumber)
|
|
||||||
}
|
|
||||||
if tt.errFunc != nil && !tt.errFunc(err) {
|
|
||||||
t.Errorf("got wrong err: %v ", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user