mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 23:17:23 +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, "@") {
|
||||
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 == "" {
|
||||
mig.instanceSetup.Org.Human.Email.Address = mig.instanceSetup.Org.Human.Username
|
||||
if !strings.Contains(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)
|
||||
if !strings.Contains(string(mig.instanceSetup.Org.Human.Email.Address), "@") {
|
||||
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:
|
||||
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:
|
||||
Enabled: false
|
||||
|
@ -3,7 +3,8 @@ ExternalSecure: false
|
||||
|
||||
Database:
|
||||
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:
|
||||
Enabled: false
|
||||
|
@ -156,9 +156,9 @@ type human struct {
|
||||
AvatarKey string
|
||||
PreferredLanguage string
|
||||
Gender domain.Gender
|
||||
Email string
|
||||
Email domain.EmailAddress
|
||||
IsEmailVerified bool
|
||||
Phone string
|
||||
Phone domain.PhoneNumber
|
||||
IsPhoneVerified bool
|
||||
}
|
||||
|
||||
|
@ -564,13 +564,13 @@ func (s *Server) getUsers(ctx context.Context, org string, withPasswords bool, w
|
||||
}
|
||||
if user.Human.Email != "" {
|
||||
dataUser.User.Email = &management_pb.ImportHumanUserRequest_Email{
|
||||
Email: user.Human.Email,
|
||||
Email: string(user.Human.Email),
|
||||
IsEmailVerified: user.Human.IsEmailVerified,
|
||||
}
|
||||
}
|
||||
if user.Human.Phone != "" {
|
||||
dataUser.User.Phone = &management_pb.ImportHumanUserRequest_Phone{
|
||||
Phone: user.Human.Phone,
|
||||
Phone: string(user.Human.Phone),
|
||||
IsPhoneVerified: user.Human.IsPhoneVerified,
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
user_grpc "github.com/zitadel/zitadel/internal/api/grpc/user"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
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 {
|
||||
return command.Email{
|
||||
Address: email.Email,
|
||||
Address: domain.EmailAddress(email.Email),
|
||||
Verified: email.IsEmailVerified,
|
||||
}
|
||||
}
|
||||
@ -39,7 +40,7 @@ func setUpOrgHumanPhoneToDomain(phone *admin_grpc.SetUpOrgRequest_Human_Phone) c
|
||||
return command.Phone{}
|
||||
}
|
||||
return command.Phone{
|
||||
Number: phone.Phone,
|
||||
Number: domain.PhoneNumber(phone.Phone),
|
||||
Verified: phone.IsPhoneVerified,
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,6 @@ import (
|
||||
func UpdateMyEmailToDomain(ctx context.Context, email *auth.SetMyEmailRequest) *domain.Email {
|
||||
return &domain.Email{
|
||||
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 {
|
||||
return &domain.Phone{
|
||||
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,
|
||||
DisplayName: req.Profile.DisplayName,
|
||||
Email: command.Email{
|
||||
Address: req.Email.Email,
|
||||
Address: domain.EmailAddress(req.Email.Email),
|
||||
Verified: req.Email.IsEmailVerified,
|
||||
},
|
||||
PreferredLanguage: lang,
|
||||
@ -221,7 +221,7 @@ func AddHumanUserRequestToAddHuman(req *mgmt_pb.AddHumanUserRequest) *command.Ad
|
||||
}
|
||||
if req.Phone != nil {
|
||||
human.Phone = command.Phone{
|
||||
Number: req.Phone.Phone,
|
||||
Number: domain.PhoneNumber(req.Phone.Phone),
|
||||
Verified: req.Phone.IsPhoneVerified,
|
||||
}
|
||||
}
|
||||
@ -446,7 +446,7 @@ func (s *Server) ResendHumanInitialization(ctx context.Context, req *mgmt_pb.Res
|
||||
if err != nil {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -91,37 +91,6 @@ func ListUserMetadataToDomain(req *mgmt_pb.ListUserMetadataRequest) (*query.User
|
||||
}, 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) {
|
||||
human = &domain.Human{
|
||||
Username: req.UserName,
|
||||
@ -137,12 +106,12 @@ func ImportHumanUserRequestToDomain(req *mgmt_pb.ImportHumanUserRequest) (human
|
||||
Gender: user_grpc.GenderToDomain(req.Profile.Gender),
|
||||
}
|
||||
human.Email = &domain.Email{
|
||||
EmailAddress: req.Email.Email,
|
||||
EmailAddress: domain.EmailAddress(req.Email.Email),
|
||||
IsEmailVerified: req.Email.IsEmailVerified,
|
||||
}
|
||||
if req.Phone != nil {
|
||||
human.Phone = &domain.Phone{
|
||||
PhoneNumber: req.Phone.Phone,
|
||||
PhoneNumber: domain.PhoneNumber(req.Phone.Phone),
|
||||
IsPhoneVerified: req.Phone.IsPhoneVerified,
|
||||
}
|
||||
}
|
||||
@ -199,7 +168,7 @@ func UpdateHumanEmailRequestToDomain(ctx context.Context, req *mgmt_pb.UpdateHum
|
||||
AggregateID: req.UserId,
|
||||
ResourceOwner: authz.GetCtxData(ctx).OrgID,
|
||||
},
|
||||
EmailAddress: req.Email,
|
||||
EmailAddress: domain.EmailAddress(req.Email),
|
||||
IsEmailVerified: req.IsEmailVerified,
|
||||
}
|
||||
}
|
||||
@ -207,7 +176,7 @@ func UpdateHumanEmailRequestToDomain(ctx context.Context, req *mgmt_pb.UpdateHum
|
||||
func UpdateHumanPhoneRequestToDomain(req *mgmt_pb.UpdateHumanPhoneRequest) *domain.Phone {
|
||||
return &domain.Phone{
|
||||
ObjectRoot: models.ObjectRoot{AggregateID: req.UserId},
|
||||
PhoneNumber: req.Phone,
|
||||
PhoneNumber: domain.PhoneNumber(req.Phone),
|
||||
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 {
|
||||
user := defaultHuman
|
||||
if req.Email != nil {
|
||||
user.Email.Address = req.Email.Email
|
||||
user.Email.Address = domain.EmailAddress(req.Email.Email)
|
||||
user.Email.Verified = req.Email.IsEmailVerified
|
||||
}
|
||||
if req.Profile != nil {
|
||||
@ -164,7 +164,7 @@ func AddInstancePbToSetupInstance(req *system_pb.AddInstanceRequest, defaultInst
|
||||
instance.Org.Human = new(command.AddHuman)
|
||||
}
|
||||
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
|
||||
}
|
||||
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),
|
||||
},
|
||||
Email: &user_pb.Email{
|
||||
Email: view.Email,
|
||||
Email: string(view.Email),
|
||||
IsEmailVerified: view.IsEmailVerified,
|
||||
},
|
||||
Phone: &user_pb.Phone{
|
||||
Phone: view.Phone,
|
||||
Phone: string(view.Phone),
|
||||
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 {
|
||||
return &user_pb.Email{
|
||||
Email: email.Email,
|
||||
Email: string(email.Email),
|
||||
IsEmailVerified: email.IsVerified,
|
||||
}
|
||||
}
|
||||
@ -105,7 +105,7 @@ func PhoneToPb(phone *query.Phone) *user_pb.Phone {
|
||||
|
||||
func ModelEmailToPb(email *query.Email) *user_pb.Email {
|
||||
return &user_pb.Email{
|
||||
Email: email.Email,
|
||||
Email: string(email.Email),
|
||||
IsEmailVerified: email.IsVerified,
|
||||
}
|
||||
}
|
||||
|
@ -268,7 +268,7 @@ func (o *OPStorage) setUserinfo(ctx context.Context, userInfo oidc.UserInfoSette
|
||||
if user.Human == nil {
|
||||
continue
|
||||
}
|
||||
userInfo.SetEmail(user.Human.Email, user.Human.IsEmailVerified)
|
||||
userInfo.SetEmail(string(user.Human.Email), user.Human.IsEmailVerified)
|
||||
case oidc.ScopeProfile:
|
||||
userInfo.SetPreferredUsername(user.PreferredLoginName)
|
||||
userInfo.SetUpdatedAt(user.ChangeDate)
|
||||
@ -287,7 +287,7 @@ func (o *OPStorage) setUserinfo(ctx context.Context, userInfo oidc.UserInfoSette
|
||||
if user.Human == nil {
|
||||
continue
|
||||
}
|
||||
userInfo.SetPhone(user.Human.Phone, user.Human.IsPhoneVerified)
|
||||
userInfo.SetPhone(string(user.Human.Phone), user.Human.IsPhoneVerified)
|
||||
case oidc.ScopeAddress:
|
||||
//TODO: handle address for human users as soon as implemented
|
||||
case ScopeUserMetaData:
|
||||
|
@ -154,7 +154,7 @@ func setUserinfo(user *query.User, userinfo models.AttributeSetter, attributes [
|
||||
if user.Human == nil {
|
||||
return
|
||||
}
|
||||
userinfo.SetEmail(user.Human.Email)
|
||||
userinfo.SetEmail(string(user.Human.Email))
|
||||
userinfo.SetSurname(user.Human.LastName)
|
||||
userinfo.SetGivenName(user.Human.FirstName)
|
||||
userinfo.SetFullName(user.Human.DisplayName)
|
||||
@ -164,7 +164,7 @@ func setUserinfo(user *query.User, userinfo models.AttributeSetter, attributes [
|
||||
switch attribute {
|
||||
case provider.AttributeEmail:
|
||||
if user.Human != nil {
|
||||
userinfo.SetEmail(user.Human.Email)
|
||||
userinfo.SetEmail(string(user.Human.Email))
|
||||
}
|
||||
case provider.AttributeSurname:
|
||||
if user.Human != nil {
|
||||
|
@ -55,13 +55,13 @@ func (l *Login) runPostExternalAuthenticationActions(
|
||||
actions.SetFields("setPreferredUsername", func(username string) {
|
||||
user.PreferredUsername = username
|
||||
}),
|
||||
actions.SetFields("setEmail", func(email string) {
|
||||
actions.SetFields("setEmail", func(email domain.EmailAddress) {
|
||||
user.Email = email
|
||||
}),
|
||||
actions.SetFields("setEmailVerified", func(verified bool) {
|
||||
user.IsEmailVerified = verified
|
||||
}),
|
||||
actions.SetFields("setPhone", func(phone string) {
|
||||
actions.SetFields("setPhone", func(phone domain.PhoneNumber) {
|
||||
user.Phone = phone
|
||||
}),
|
||||
actions.SetFields("setPhoneVerified", func(verified bool) {
|
||||
@ -222,7 +222,7 @@ func (l *Login) runPreCreationActions(
|
||||
actions.SetFields("setUsername", func(username string) {
|
||||
user.Username = username
|
||||
}),
|
||||
actions.SetFields("setEmail", func(email string) {
|
||||
actions.SetFields("setEmail", func(email domain.EmailAddress) {
|
||||
if user.Email == nil {
|
||||
user.Email = &domain.Email{}
|
||||
}
|
||||
@ -234,11 +234,11 @@ func (l *Login) runPreCreationActions(
|
||||
}
|
||||
user.Email.IsEmailVerified = verified
|
||||
}),
|
||||
actions.SetFields("setPhone", func(email string) {
|
||||
actions.SetFields("setPhone", func(phone domain.PhoneNumber) {
|
||||
if user.Phone == nil {
|
||||
user.Phone = &domain.Phone{}
|
||||
}
|
||||
user.Phone.PhoneNumber = email
|
||||
user.Phone.PhoneNumber = phone
|
||||
}),
|
||||
actions.SetFields("setPhoneVerified", func(verified bool) {
|
||||
if user.Phone == nil {
|
||||
|
@ -60,28 +60,28 @@ type externalNotFoundOptionData struct {
|
||||
ShowUsername bool
|
||||
ShowUsernameSuffix bool
|
||||
OrgRegister bool
|
||||
ExternalEmail string
|
||||
ExternalEmail domain.EmailAddress
|
||||
ExternalEmailVerified bool
|
||||
ExternalPhone string
|
||||
ExternalPhone domain.PhoneNumber
|
||||
ExternalPhoneVerified bool
|
||||
}
|
||||
|
||||
type externalRegisterFormData struct {
|
||||
ExternalIDPConfigID string `schema:"external-idp-config-id"`
|
||||
ExternalIDPExtUserID string `schema:"external-idp-ext-user-id"`
|
||||
ExternalIDPDisplayName string `schema:"external-idp-display-name"`
|
||||
ExternalEmail string `schema:"external-email"`
|
||||
ExternalEmailVerified bool `schema:"external-email-verified"`
|
||||
Email string `schema:"email"`
|
||||
Username string `schema:"username"`
|
||||
Firstname string `schema:"firstname"`
|
||||
Lastname string `schema:"lastname"`
|
||||
Nickname string `schema:"nickname"`
|
||||
ExternalPhone string `schema:"external-phone"`
|
||||
ExternalPhoneVerified bool `schema:"external-phone-verified"`
|
||||
Phone string `schema:"phone"`
|
||||
Language string `schema:"language"`
|
||||
TermsConfirm bool `schema:"terms-confirm"`
|
||||
ExternalIDPConfigID string `schema:"external-idp-config-id"`
|
||||
ExternalIDPExtUserID string `schema:"external-idp-ext-user-id"`
|
||||
ExternalIDPDisplayName string `schema:"external-idp-display-name"`
|
||||
ExternalEmail domain.EmailAddress `schema:"external-email"`
|
||||
ExternalEmailVerified bool `schema:"external-email-verified"`
|
||||
Email domain.EmailAddress `schema:"email"`
|
||||
Username string `schema:"username"`
|
||||
Firstname string `schema:"firstname"`
|
||||
Lastname string `schema:"lastname"`
|
||||
Nickname string `schema:"nickname"`
|
||||
ExternalPhone domain.PhoneNumber `schema:"external-phone"`
|
||||
ExternalPhoneVerified bool `schema:"external-phone-verified"`
|
||||
Phone domain.PhoneNumber `schema:"phone"`
|
||||
Language string `schema:"language"`
|
||||
TermsConfirm bool `schema:"terms-confirm"`
|
||||
}
|
||||
|
||||
// handleExternalLoginStep is called as nextStep
|
||||
@ -815,7 +815,7 @@ func mapExternalNotFoundOptionFormDataToLoginUser(formData *externalNotFoundOpti
|
||||
IDPConfigID: formData.ExternalIDPConfigID,
|
||||
ExternalUserID: formData.ExternalIDPExtUserID,
|
||||
PreferredUsername: formData.Username,
|
||||
DisplayName: formData.Email,
|
||||
DisplayName: string(formData.Email),
|
||||
FirstName: formData.Firstname,
|
||||
LastName: formData.Lastname,
|
||||
NickName: formData.Nickname,
|
||||
|
@ -16,14 +16,14 @@ const (
|
||||
)
|
||||
|
||||
type registerFormData struct {
|
||||
Email string `schema:"email"`
|
||||
Username string `schema:"username"`
|
||||
Firstname string `schema:"firstname"`
|
||||
Lastname string `schema:"lastname"`
|
||||
Language string `schema:"language"`
|
||||
Password string `schema:"register-password"`
|
||||
Password2 string `schema:"register-password-confirmation"`
|
||||
TermsConfirm bool `schema:"terms-confirm"`
|
||||
Email domain.EmailAddress `schema:"email"`
|
||||
Username string `schema:"username"`
|
||||
Firstname string `schema:"firstname"`
|
||||
Lastname string `schema:"lastname"`
|
||||
Language string `schema:"language"`
|
||||
Password string `schema:"register-password"`
|
||||
Password2 string `schema:"register-password-confirmation"`
|
||||
TermsConfirm bool `schema:"terms-confirm"`
|
||||
}
|
||||
|
||||
type registerData struct {
|
||||
|
@ -94,7 +94,7 @@ func (l *Login) passLoginHintToRegistration(r *http.Request, authReq *domain.Aut
|
||||
if authReq == nil {
|
||||
return data
|
||||
}
|
||||
data.Email = authReq.LoginHint
|
||||
data.Email = domain.EmailAddress(authReq.LoginHint)
|
||||
domainPolicy, err := l.getOrgDomainPolicy(r, authReq.RequestedOrgID)
|
||||
if err != nil {
|
||||
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 {
|
||||
RegisterOrgName string `schema:"orgname"`
|
||||
Email string `schema:"email"`
|
||||
Username string `schema:"username"`
|
||||
Firstname string `schema:"firstname"`
|
||||
Lastname string `schema:"lastname"`
|
||||
Password string `schema:"register-password"`
|
||||
Password2 string `schema:"register-password-confirmation"`
|
||||
TermsConfirm bool `schema:"terms-confirm"`
|
||||
RegisterOrgName string `schema:"orgname"`
|
||||
Email domain.EmailAddress `schema:"email"`
|
||||
Username string `schema:"username"`
|
||||
Firstname string `schema:"firstname"`
|
||||
Lastname string `schema:"lastname"`
|
||||
Password string `schema:"register-password"`
|
||||
Password2 string `schema:"register-password-confirmation"`
|
||||
TermsConfirm bool `schema:"terms-confirm"`
|
||||
}
|
||||
|
||||
type registerOrgData struct {
|
||||
@ -121,7 +121,7 @@ func (l *Login) renderRegisterOrg(w http.ResponseWriter, r *http.Request, authRe
|
||||
|
||||
func (d registerOrgFormData) toUserDomain() *domain.Human {
|
||||
if d.Username == "" {
|
||||
d.Username = d.Email
|
||||
d.Username = string(d.Email)
|
||||
}
|
||||
return &domain.Human{
|
||||
Username: d.Username,
|
||||
@ -140,7 +140,7 @@ func (d registerOrgFormData) toUserDomain() *domain.Human {
|
||||
|
||||
func (d registerOrgFormData) toCommandOrg() *command.OrgSetup {
|
||||
if d.Username == "" {
|
||||
d.Username = d.Email
|
||||
d.Username = string(d.Email)
|
||||
}
|
||||
return &command.OrgSetup{
|
||||
Name: d.RegisterOrgName,
|
||||
|
@ -328,6 +328,33 @@ Errors:
|
||||
Invalid: Userdaten sind ungültig
|
||||
DomainNotAllowedAsUsername: Domäne ist bereits reserviert und kann nicht verwendet 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:
|
||||
ConfirmationWrong: Passwort Bestätigung stimmt nicht überein
|
||||
Empty: Passwort ist leer
|
||||
|
@ -328,6 +328,33 @@ Errors:
|
||||
Invalid: Invalid userdata
|
||||
DomainNotAllowedAsUsername: Domain is already reserved and cannot be used
|
||||
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:
|
||||
ConfirmationWrong: Passwordconfirmation is wrong
|
||||
Empty: Password is empty
|
||||
|
@ -328,6 +328,33 @@ Errors:
|
||||
Invalid: Données utilisateur non valides
|
||||
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
|
||||
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:
|
||||
ConfirmationWrong: La confirmation du mot de passe est erronée
|
||||
Empty: Le mot de passe est vide
|
||||
|
@ -328,6 +328,33 @@ Errors:
|
||||
Invalid: I dati del utente non sono validi
|
||||
DomainNotAllowedAsUsername: Il dominio è già riservato e non può essere utilizzato
|
||||
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:
|
||||
ConfirmationWrong: La conferma della password è sbagliata
|
||||
Empty: La password è vuota
|
||||
|
@ -328,6 +328,33 @@ Errors:
|
||||
Invalid: Nieprawidłowe dane użytkownika
|
||||
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
|
||||
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:
|
||||
ConfirmationWrong: Potwierdzenie hasła jest niepoprawne
|
||||
Empty: Hasło jest puste
|
||||
|
@ -328,6 +328,33 @@ Errors:
|
||||
Invalid: 无效的用户数据
|
||||
DomainNotAllowedAsUsername: 域名已存在,但无法使用
|
||||
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:
|
||||
ConfirmationWrong: 密码不一致
|
||||
Empty: 密码为空
|
||||
|
@ -10,12 +10,12 @@ import (
|
||||
)
|
||||
|
||||
type Email struct {
|
||||
Address string
|
||||
Address domain.EmailAddress
|
||||
Verified bool
|
||||
}
|
||||
|
||||
func (e *Email) Valid() bool {
|
||||
return e.Address != "" && domain.EmailRegex.MatchString(e.Address)
|
||||
func (e *Email) Validate() error {
|
||||
return e.Address.Validate()
|
||||
}
|
||||
|
||||
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"
|
||||
"time"
|
||||
|
||||
"github.com/ttacon/libphonenumber"
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
type Phone struct {
|
||||
Number string
|
||||
Number domain.PhoneNumber
|
||||
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) {
|
||||
return newCryptoCodeWithExpiry(ctx, filter, domain.SecretGeneratorTypeVerifyPhoneCode, alg)
|
||||
}
|
||||
|
@ -3,12 +3,13 @@ package command
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
func TestFormatPhoneNumber(t *testing.T) {
|
||||
type args struct {
|
||||
number string
|
||||
number domain.PhoneNumber
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -44,10 +45,9 @@ func TestFormatPhoneNumber(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
formatted, err := FormatPhoneNumber(tt.args.number)
|
||||
|
||||
if tt.errFunc == nil && tt.result.Number != formatted {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.args.number, formatted)
|
||||
normalized, err := tt.args.number.Normalize()
|
||||
if tt.errFunc == nil && tt.result.Number != normalized {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.Number, normalized)
|
||||
}
|
||||
if tt.errFunc != nil && !tt.errFunc(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 {
|
||||
eventstore.Command
|
||||
AddPhoneData(phoneNumber string)
|
||||
AddPhoneData(phoneNumber domain.PhoneNumber)
|
||||
AddPasswordData(secret *crypto.CryptoValue, changeRequired bool)
|
||||
}
|
||||
|
||||
func AddHumanCommand(a *user.Aggregate, human *AddHuman, passwordAlg crypto.HashAlgorithm, codeAlg crypto.EncryptionAlgorithm) preparation.Validation {
|
||||
return func() (_ preparation.CreateCommands, err error) {
|
||||
if !human.Email.Valid() {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "USER-Ec7dM", "Errors.Invalid.Argument")
|
||||
if err := human.Email.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if human.Username = strings.TrimSpace(human.Username); human.Username == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "V2-zzad3", "Errors.Invalid.Argument")
|
||||
}
|
||||
|
||||
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 == "" {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "USER-DiAq8", "Errors.Invalid.Argument")
|
||||
return nil, errors.ThrowInvalidArgument(nil, "USER-4hB7d", "Errors.User.Profile.LastNameEmpty")
|
||||
}
|
||||
human.ensureDisplayName()
|
||||
|
||||
if human.Phone.Number, err = FormatPhoneNumber(human.Phone.Number); err != nil {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "USER-tD6ax", "Errors.Invalid.Argument")
|
||||
if human.Phone.Number != "" {
|
||||
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) {
|
||||
@ -387,19 +389,12 @@ func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domai
|
||||
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) {
|
||||
if orgID == "" || !human.IsValid() {
|
||||
return nil, nil, nil, "", errors.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.User.Invalid")
|
||||
if orgID == "" {
|
||||
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)
|
||||
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")
|
||||
}
|
||||
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 == "") {
|
||||
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-9dk45", "Errors.User.Invalid")
|
||||
if orgID == "" {
|
||||
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 != "" {
|
||||
human.Password.ChangeRequired = false
|
||||
@ -441,7 +442,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
|
||||
return nil, nil, err
|
||||
}
|
||||
human.Username = strings.TrimSpace(human.Username)
|
||||
human.EmailAddress = strings.TrimSpace(human.EmailAddress)
|
||||
human.EmailAddress = human.EmailAddress.Normalize()
|
||||
if !domainPolicy.UserLoginMustBeDomain {
|
||||
index := strings.LastIndex(human.Username, "@")
|
||||
if index > 1 {
|
||||
|
@ -13,8 +13,11 @@ import (
|
||||
)
|
||||
|
||||
func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email, emailCodeGenerator crypto.Generator) (*domain.Email, error) {
|
||||
if !email.IsValid() || email.AggregateID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M9sf", "Errors.Email.Invalid")
|
||||
if email.AggregateID == "" {
|
||||
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)
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
type HumanEmailWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
Email string
|
||||
Email domain.EmailAddress
|
||||
IsEmailVerified bool
|
||||
|
||||
Code *crypto.CryptoValue
|
||||
@ -95,7 +95,7 @@ func (wm *HumanEmailWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
func (wm *HumanEmailWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
email string,
|
||||
email domain.EmailAddress,
|
||||
) (*user.HumanEmailChangedEvent, bool) {
|
||||
if wm.Email == email {
|
||||
return nil, false
|
||||
|
@ -11,8 +11,8 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
)
|
||||
|
||||
//ResendInitialMail resend inital 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) {
|
||||
// ResendInitialMail resend initial mail and changes email if provided
|
||||
func (c *Commands) ResendInitialMail(ctx context.Context, userID string, email domain.EmailAddress, resourceOwner string, initCodeGenerator crypto.Generator) (objectDetails *domain.ObjectDetails, err error) {
|
||||
if userID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
type HumanInitCodeWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
Email string
|
||||
Email domain.EmailAddress
|
||||
IsEmailVerified bool
|
||||
|
||||
Code *crypto.CryptoValue
|
||||
@ -92,7 +92,7 @@ func (wm *HumanInitCodeWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
func (wm *HumanInitCodeWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
email string,
|
||||
email domain.EmailAddress,
|
||||
) (*user.HumanEmailChangedEvent, bool) {
|
||||
changedEvent := user.NewHumanEmailChangedEvent(ctx, aggregate, email)
|
||||
return changedEvent, wm.Email != email
|
||||
|
@ -289,7 +289,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
||||
r := &Commands{
|
||||
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 {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
@ -22,10 +22,10 @@ type HumanWriteModel struct {
|
||||
Gender domain.Gender
|
||||
Avatar string
|
||||
|
||||
Email string
|
||||
Email domain.EmailAddress
|
||||
IsEmailVerified bool
|
||||
|
||||
Phone string
|
||||
Phone domain.PhoneNumber
|
||||
IsPhoneVerified bool
|
||||
|
||||
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)
|
||||
if accountName == "" {
|
||||
accountName = human.EmailAddress
|
||||
accountName = string(human.EmailAddress)
|
||||
}
|
||||
key, secret, err := domain.NewOTPKey(c.multifactors.OTP.Issuer, accountName, c.multifactors.OTP.CryptoMFA)
|
||||
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) {
|
||||
if !phone.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-6M0ds", "Errors.Phone.Invalid")
|
||||
if err := phone.Normalize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
existingPhone, err := c.phoneWriteModelByID(ctx, phone.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
type HumanPhoneWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
Phone string
|
||||
Phone domain.PhoneNumber
|
||||
IsPhoneVerified bool
|
||||
|
||||
Code *crypto.CryptoValue
|
||||
@ -107,7 +107,7 @@ func (wm *HumanPhoneWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
func (wm *HumanPhoneWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
phone string,
|
||||
phone domain.PhoneNumber,
|
||||
) (*user.HumanPhoneChangedEvent, bool) {
|
||||
changedEvent := user.NewHumanPhoneChangedEvent(ctx, aggregate, phone)
|
||||
return changedEvent, phone != wm.Phone
|
||||
|
@ -9,10 +9,12 @@ import (
|
||||
)
|
||||
|
||||
func (c *Commands) ChangeHumanProfile(ctx context.Context, profile *domain.Profile) (*domain.Profile, error) {
|
||||
if !profile.IsValid() && profile.AggregateID != "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8io0d", "Errors.User.Profile.Invalid")
|
||||
if profile.AggregateID == "" {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -3445,7 +3445,7 @@ func newAddHumanEvent(password string, changeRequired bool, phone string) *user.
|
||||
changeRequired)
|
||||
}
|
||||
if phone != "" {
|
||||
event.AddPhoneData(phone)
|
||||
event.AddPhoneData(domain.PhoneNumber(phone))
|
||||
}
|
||||
return event
|
||||
}
|
||||
@ -3473,7 +3473,7 @@ func newRegisterHumanEvent(username, password string, changeRequired bool, phone
|
||||
changeRequired)
|
||||
}
|
||||
if phone != "" {
|
||||
event.AddPhoneData(phone)
|
||||
event.AddPhoneData(domain.PhoneNumber(phone))
|
||||
}
|
||||
return event
|
||||
}
|
||||
@ -3503,7 +3503,7 @@ func TestAddHumanCommand(t *testing.T) {
|
||||
},
|
||||
},
|
||||
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{
|
||||
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{
|
||||
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)
|
||||
if accountName == "" {
|
||||
accountName = user.EmailAddress
|
||||
accountName = string(user.EmailAddress)
|
||||
}
|
||||
webAuthN, err := c.webauthnConfig.BeginRegistration(ctx, user, accountName, authenticatorPlatform, userVerification, isLoginUI, tokens...)
|
||||
if err != nil {
|
||||
|
@ -65,10 +65,10 @@ type ExternalUser struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
Email string
|
||||
Email EmailAddress
|
||||
IsEmailVerified bool
|
||||
PreferredLanguage language.Tag
|
||||
Phone string
|
||||
Phone PhoneNumber
|
||||
IsPhoneVerified bool
|
||||
Metadatas []*Metadata
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
caos_errors "github.com/zitadel/zitadel/internal/errors"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
@ -60,8 +61,22 @@ func (f Gender) Specified() bool {
|
||||
return f > GenderUnspecified && f < genderCount
|
||||
}
|
||||
|
||||
func (u *Human) IsValid() bool {
|
||||
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())
|
||||
func (u *Human) Normalize() error {
|
||||
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 {
|
||||
@ -69,7 +84,7 @@ func (u *Human) CheckDomainPolicy(policy *DomainPolicy) error {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "DOMAIN-zSH7j", "Errors.Users.DomainPolicyNil")
|
||||
}
|
||||
if !policy.UserLoginMustBeDomain && u.Profile != nil && u.Username == "" && u.Email != nil {
|
||||
u.Username = u.EmailAddress
|
||||
u.Username = string(u.EmailAddress)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -2,20 +2,38 @@ package domain
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
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 {
|
||||
es_models.ObjectRoot
|
||||
|
||||
EmailAddress string
|
||||
EmailAddress EmailAddress
|
||||
IsEmailVerified bool
|
||||
}
|
||||
|
||||
@ -26,8 +44,11 @@ type EmailCode struct {
|
||||
Expiry time.Duration
|
||||
}
|
||||
|
||||
func (e *Email) IsValid() bool {
|
||||
return e.EmailAddress != "" && EmailRegex.MatchString(e.EmailAddress)
|
||||
func (e *Email) Validate() error {
|
||||
if e == nil {
|
||||
return errors.ThrowInvalidArgument(nil, "EMAIL-spblu", "Errors.User.Email.Empty")
|
||||
}
|
||||
return e.EmailAddress.Validate()
|
||||
}
|
||||
|
||||
func NewEmailCode(emailGenerator crypto.Generator) (*EmailCode, error) {
|
||||
|
@ -65,7 +65,7 @@ func TestEmailValid(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := tt.args.email.IsValid()
|
||||
result := tt.args.email.Validate() == nil
|
||||
if result != tt.result {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, result)
|
||||
}
|
||||
|
@ -4,19 +4,31 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ttacon/libphonenumber"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultRegion = "CH"
|
||||
)
|
||||
const 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 {
|
||||
es_models.ObjectRoot
|
||||
|
||||
PhoneNumber string
|
||||
PhoneNumber PhoneNumber
|
||||
IsPhoneVerified bool
|
||||
}
|
||||
|
||||
@ -27,17 +39,16 @@ type PhoneCode struct {
|
||||
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.ThrowInvalidArgument(nil, "EVENT-so0wa", "Errors.User.Phone.Invalid")
|
||||
func (p *Phone) Normalize() error {
|
||||
if p == nil {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "PHONE-YlbwO", "Errors.User.Phone.Empty")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -94,10 +94,9 @@ func TestFormatPhoneNumber(t *testing.T) {
|
||||
}
|
||||
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)
|
||||
normalized, err := tt.args.phone.PhoneNumber.Normalize()
|
||||
if tt.errFunc == nil && tt.result.PhoneNumber != normalized {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.PhoneNumber, normalized)
|
||||
}
|
||||
if tt.errFunc != nil && !tt.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
|
@ -3,6 +3,7 @@ package domain
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
@ -19,8 +20,17 @@ type Profile struct {
|
||||
LoginNames []string
|
||||
}
|
||||
|
||||
func (p *Profile) IsValid() bool {
|
||||
return p.FirstName != "" && p.LastName != ""
|
||||
func (p *Profile) Validate() error {
|
||||
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 {
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
// Provider is the minimal implementation for a 3rd party authentication provider
|
||||
@ -24,9 +26,9 @@ type User interface {
|
||||
GetDisplayName() string
|
||||
GetNickname() string
|
||||
GetPreferredUsername() string
|
||||
GetEmail() string
|
||||
GetEmail() domain.EmailAddress
|
||||
IsEmailVerified() bool
|
||||
GetPhone() string
|
||||
GetPhone() domain.PhoneNumber
|
||||
IsPhoneVerified() bool
|
||||
GetPreferredLanguage() language.Tag
|
||||
GetAvatarURL() string
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/idp"
|
||||
"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.
|
||||
// The verification can be automatically activated on the provider ([WithEmailVerified])
|
||||
type User struct {
|
||||
Sub string `json:"sub"`
|
||||
FamilyName string `json:"family_name"`
|
||||
GivenName string `json:"given_name"`
|
||||
Name string `json:"name"`
|
||||
PreferredUsername string `json:"preferred_username"`
|
||||
Email string `json:"email"`
|
||||
Picture string `json:"picture"`
|
||||
Sub string `json:"sub"`
|
||||
FamilyName string `json:"family_name"`
|
||||
GivenName string `json:"given_name"`
|
||||
Name string `json:"name"`
|
||||
PreferredUsername string `json:"preferred_username"`
|
||||
Email domain.EmailAddress `json:"email"`
|
||||
Picture string `json:"picture"`
|
||||
isEmailVerified bool
|
||||
}
|
||||
|
||||
@ -162,7 +163,7 @@ func (u *User) GetPreferredUsername() string {
|
||||
}
|
||||
|
||||
// GetEmail is an implementation of the [idp.User] interface.
|
||||
func (u *User) GetEmail() string {
|
||||
func (u *User) GetEmail() domain.EmailAddress {
|
||||
return u.Email
|
||||
}
|
||||
|
||||
@ -176,7 +177,7 @@ func (u *User) IsEmailVerified() bool {
|
||||
|
||||
// GetPhone is an implementation of the [idp.User] interface.
|
||||
// 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 ""
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/idp"
|
||||
"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.nickName, user.GetNickname())
|
||||
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.phone, user.GetPhone())
|
||||
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"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
|
||||
// https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user
|
||||
type User struct {
|
||||
Login string `json:"login"`
|
||||
ID int `json:"id"`
|
||||
NodeId string `json:"node_id"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
GravatarId string `json:"gravatar_id"`
|
||||
Url string `json:"url"`
|
||||
HtmlUrl string `json:"html_url"`
|
||||
FollowersUrl string `json:"followers_url"`
|
||||
FollowingUrl string `json:"following_url"`
|
||||
GistsUrl string `json:"gists_url"`
|
||||
StarredUrl string `json:"starred_url"`
|
||||
SubscriptionsUrl string `json:"subscriptions_url"`
|
||||
OrganizationsUrl string `json:"organizations_url"`
|
||||
ReposUrl string `json:"repos_url"`
|
||||
EventsUrl string `json:"events_url"`
|
||||
ReceivedEventsUrl string `json:"received_events_url"`
|
||||
Type string `json:"type"`
|
||||
SiteAdmin bool `json:"site_admin"`
|
||||
Name string `json:"name"`
|
||||
Company string `json:"company"`
|
||||
Blog string `json:"blog"`
|
||||
Location string `json:"location"`
|
||||
Email string `json:"email"`
|
||||
Hireable bool `json:"hireable"`
|
||||
Bio string `json:"bio"`
|
||||
TwitterUsername string `json:"twitter_username"`
|
||||
PublicRepos int `json:"public_repos"`
|
||||
PublicGists int `json:"public_gists"`
|
||||
Followers int `json:"followers"`
|
||||
Following int `json:"following"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
PrivateGists int `json:"private_gists"`
|
||||
TotalPrivateRepos int `json:"total_private_repos"`
|
||||
OwnedPrivateRepos int `json:"owned_private_repos"`
|
||||
DiskUsage int `json:"disk_usage"`
|
||||
Collaborators int `json:"collaborators"`
|
||||
TwoFactorAuthentication bool `json:"two_factor_authentication"`
|
||||
Login string `json:"login"`
|
||||
ID int `json:"id"`
|
||||
NodeId string `json:"node_id"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
GravatarId string `json:"gravatar_id"`
|
||||
Url string `json:"url"`
|
||||
HtmlUrl string `json:"html_url"`
|
||||
FollowersUrl string `json:"followers_url"`
|
||||
FollowingUrl string `json:"following_url"`
|
||||
GistsUrl string `json:"gists_url"`
|
||||
StarredUrl string `json:"starred_url"`
|
||||
SubscriptionsUrl string `json:"subscriptions_url"`
|
||||
OrganizationsUrl string `json:"organizations_url"`
|
||||
ReposUrl string `json:"repos_url"`
|
||||
EventsUrl string `json:"events_url"`
|
||||
ReceivedEventsUrl string `json:"received_events_url"`
|
||||
Type string `json:"type"`
|
||||
SiteAdmin bool `json:"site_admin"`
|
||||
Name string `json:"name"`
|
||||
Company string `json:"company"`
|
||||
Blog string `json:"blog"`
|
||||
Location string `json:"location"`
|
||||
Email domain.EmailAddress `json:"email"`
|
||||
Hireable bool `json:"hireable"`
|
||||
Bio string `json:"bio"`
|
||||
TwitterUsername string `json:"twitter_username"`
|
||||
PublicRepos int `json:"public_repos"`
|
||||
PublicGists int `json:"public_gists"`
|
||||
Followers int `json:"followers"`
|
||||
Following int `json:"following"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
PrivateGists int `json:"private_gists"`
|
||||
TotalPrivateRepos int `json:"total_private_repos"`
|
||||
OwnedPrivateRepos int `json:"owned_private_repos"`
|
||||
DiskUsage int `json:"disk_usage"`
|
||||
Collaborators int `json:"collaborators"`
|
||||
TwoFactorAuthentication bool `json:"two_factor_authentication"`
|
||||
Plan struct {
|
||||
Name string `json:"name"`
|
||||
Space int `json:"space"`
|
||||
@ -150,7 +152,7 @@ func (u *User) GetPreferredUsername() string {
|
||||
}
|
||||
|
||||
// GetEmail is an implementation of the [idp.User] interface.
|
||||
func (u *User) GetEmail() string {
|
||||
func (u *User) GetEmail() domain.EmailAddress {
|
||||
return u.Email
|
||||
}
|
||||
|
||||
@ -162,7 +164,7 @@ func (u *User) IsEmailVerified() bool {
|
||||
|
||||
// GetPhone is an implementation of the [idp.User] interface.
|
||||
// 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 ""
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/idp"
|
||||
"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.nickName, user.GetNickname())
|
||||
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.phone, user.GetPhone())
|
||||
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"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.nickName, user.GetNickname())
|
||||
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.phone, user.GetPhone())
|
||||
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||
|
@ -43,5 +43,5 @@ type User struct {
|
||||
// GetPreferredUsername implements the [idp.User] interface.
|
||||
// It returns the email, because Google does not return a username.
|
||||
func (u *User) GetPreferredUsername() string {
|
||||
return u.GetEmail()
|
||||
return string(u.GetEmail())
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"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.nickName, user.GetNickname())
|
||||
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.phone, user.GetPhone())
|
||||
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/idp"
|
||||
)
|
||||
|
||||
@ -106,8 +107,8 @@ func (u *User) GetNickname() string {
|
||||
return u.IDTokenClaims.GetNickname()
|
||||
}
|
||||
|
||||
func (u *User) GetPhone() string {
|
||||
return u.IDTokenClaims.GetPhoneNumber()
|
||||
func (u *User) GetPhone() domain.PhoneNumber {
|
||||
return domain.PhoneNumber(u.IDTokenClaims.GetPhoneNumber())
|
||||
}
|
||||
|
||||
func (u *User) IsPhoneVerified() bool {
|
||||
@ -121,3 +122,7 @@ func (u *User) GetPreferredLanguage() language.Tag {
|
||||
func (u *User) GetAvatarURL() string {
|
||||
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"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
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.nickName, user.GetNickname())
|
||||
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.phone, user.GetPhone())
|
||||
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"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.nickNameAttribute),
|
||||
user.GetAttributeValue(s.Provider.preferredUsernameAttribute),
|
||||
user.GetAttributeValue(s.Provider.emailAttribute),
|
||||
domain.EmailAddress(user.GetAttributeValue(s.Provider.emailAttribute)),
|
||||
emailVerified,
|
||||
user.GetAttributeValue(s.Provider.phoneAttribute),
|
||||
domain.PhoneNumber(user.GetAttributeValue(s.Provider.phoneAttribute)),
|
||||
phoneVerified,
|
||||
language.Make(user.GetAttributeValue(s.Provider.preferredLanguageAttribute)),
|
||||
user.GetAttributeValue(s.Provider.avatarURLAttribute),
|
||||
|
@ -1,6 +1,10 @@
|
||||
package ldap
|
||||
|
||||
import "golang.org/x/text/language"
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
id string
|
||||
@ -9,9 +13,9 @@ type User struct {
|
||||
displayName string
|
||||
nickName string
|
||||
preferredUsername string
|
||||
email string
|
||||
email domain.EmailAddress
|
||||
emailVerified bool
|
||||
phone string
|
||||
phone domain.PhoneNumber
|
||||
phoneVerified bool
|
||||
preferredLanguage language.Tag
|
||||
avatarURL string
|
||||
@ -25,9 +29,9 @@ func NewUser(
|
||||
displayName string,
|
||||
nickName string,
|
||||
preferredUsername string,
|
||||
email string,
|
||||
email domain.EmailAddress,
|
||||
emailVerified bool,
|
||||
phone string,
|
||||
phone domain.PhoneNumber,
|
||||
phoneVerified bool,
|
||||
preferredLanguage language.Tag,
|
||||
avatarURL string,
|
||||
@ -68,13 +72,13 @@ func (u *User) GetNickname() string {
|
||||
func (u *User) GetPreferredUsername() string {
|
||||
return u.preferredUsername
|
||||
}
|
||||
func (u *User) GetEmail() string {
|
||||
func (u *User) GetEmail() domain.EmailAddress {
|
||||
return u.email
|
||||
}
|
||||
func (u *User) IsEmailVerified() bool {
|
||||
return u.emailVerified
|
||||
}
|
||||
func (u *User) GetPhone() string {
|
||||
func (u *User) GetPhone() domain.PhoneNumber {
|
||||
return u.phone
|
||||
}
|
||||
func (u *User) IsPhoneVerified() bool {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/idp"
|
||||
)
|
||||
|
||||
@ -74,7 +75,7 @@ func (u *UserMapper) GetPreferredUsername() string {
|
||||
}
|
||||
|
||||
// GetEmail is an implementation of the [idp.User] interface.
|
||||
func (u *UserMapper) GetEmail() string {
|
||||
func (u *UserMapper) GetEmail() domain.EmailAddress {
|
||||
return ""
|
||||
}
|
||||
|
||||
@ -84,7 +85,7 @@ func (u *UserMapper) IsEmailVerified() bool {
|
||||
}
|
||||
|
||||
// GetPhone is an implementation of the [idp.User] interface.
|
||||
func (u *UserMapper) GetPhone() string {
|
||||
func (u *UserMapper) GetPhone() domain.PhoneNumber {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"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.nickName, user.GetNickname())
|
||||
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.phone, user.GetPhone())
|
||||
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||
a.Equal(tt.want.avatarURL, user.GetAvatarURL())
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/idp"
|
||||
)
|
||||
|
||||
@ -82,8 +83,8 @@ func (u *User) GetDisplayName() string {
|
||||
return u.GetName()
|
||||
}
|
||||
|
||||
func (u *User) GetPhone() string {
|
||||
return u.GetPhoneNumber()
|
||||
func (u *User) GetPhone() domain.PhoneNumber {
|
||||
return domain.PhoneNumber(u.GetPhoneNumber())
|
||||
}
|
||||
|
||||
func (u *User) IsPhoneVerified() bool {
|
||||
@ -97,3 +98,7 @@ func (u *User) GetPreferredLanguage() language.Tag {
|
||||
func (u *User) GetAvatarURL() string {
|
||||
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"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"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.nickName, user.GetNickname())
|
||||
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.phone, user.GetPhone())
|
||||
a.Equal(domain.PhoneNumber(tt.want.phone), user.GetPhone())
|
||||
a.Equal(tt.want.isPhoneVerified, user.IsPhoneVerified())
|
||||
a.Equal(tt.want.preferredLanguage, user.GetPreferredLanguage())
|
||||
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(HumanGenderCol, &sql.NullInt16{Int16: int16(e.Gender), Valid: e.Gender.Specified()}),
|
||||
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),
|
||||
),
|
||||
@ -351,7 +351,7 @@ func (p *userProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat
|
||||
handler.NewCol(NotifyUserIDCol, e.Aggregate().ID),
|
||||
handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID),
|
||||
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),
|
||||
},
|
||||
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(HumanGenderCol, &sql.NullInt16{Int16: int16(e.Gender), Valid: e.Gender.Specified()}),
|
||||
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),
|
||||
),
|
||||
@ -399,7 +399,7 @@ func (p *userProjection) reduceHumanRegistered(event eventstore.Event) (*handler
|
||||
handler.NewCol(NotifyUserIDCol, e.Aggregate().ID),
|
||||
handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID),
|
||||
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),
|
||||
},
|
||||
crdb.WithTableSuffix(UserNotifySuffix),
|
||||
@ -660,7 +660,7 @@ func (p *userProjection) reduceHumanPhoneChanged(event eventstore.Event) (*handl
|
||||
),
|
||||
crdb.AddUpdateStatement(
|
||||
[]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.NewCond(NotifyUserIDCol, e.Aggregate().ID),
|
||||
@ -786,7 +786,7 @@ func (p *userProjection) reduceHumanEmailChanged(event eventstore.Event) (*handl
|
||||
),
|
||||
crdb.AddUpdateStatement(
|
||||
[]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.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: "ch-DE", 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},
|
||||
},
|
||||
},
|
||||
@ -84,7 +84,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
"email@zitadel.com",
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||
false,
|
||||
},
|
||||
@ -144,7 +144,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
&sql.NullString{String: "display-name", Valid: true},
|
||||
&sql.NullString{String: "ch-DE", 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},
|
||||
},
|
||||
},
|
||||
@ -153,7 +153,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
"email@zitadel.com",
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||
false,
|
||||
},
|
||||
@ -208,7 +208,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
&sql.NullString{},
|
||||
&sql.NullString{String: "und", Valid: false},
|
||||
&sql.NullInt16{},
|
||||
"email@zitadel.com",
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
&sql.NullString{},
|
||||
},
|
||||
},
|
||||
@ -217,7 +217,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
"email@zitadel.com",
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
&sql.NullString{String: "", Valid: false},
|
||||
false,
|
||||
},
|
||||
@ -277,7 +277,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
&sql.NullString{String: "display-name", Valid: true},
|
||||
&sql.NullString{String: "ch-DE", 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},
|
||||
},
|
||||
},
|
||||
@ -286,7 +286,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
"email@zitadel.com",
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||
false,
|
||||
},
|
||||
@ -346,7 +346,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
&sql.NullString{String: "display-name", Valid: true},
|
||||
&sql.NullString{String: "ch-DE", 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},
|
||||
},
|
||||
},
|
||||
@ -355,7 +355,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
"email@zitadel.com",
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
&sql.NullString{String: "+41 00 000 00 00", Valid: true},
|
||||
false,
|
||||
},
|
||||
@ -410,7 +410,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
&sql.NullString{},
|
||||
&sql.NullString{String: "und", Valid: false},
|
||||
&sql.NullInt16{},
|
||||
"email@zitadel.com",
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
&sql.NullString{},
|
||||
},
|
||||
},
|
||||
@ -419,7 +419,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
"email@zitadel.com",
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
&sql.NullString{String: "", Valid: 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)",
|
||||
expectedArgs: []interface{}{
|
||||
"+41 00 000 00 00",
|
||||
domain.PhoneNumber("+41 00 000 00 00"),
|
||||
false,
|
||||
"agg-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)",
|
||||
expectedArgs: []interface{}{
|
||||
"+41 00 000 00 00",
|
||||
domain.PhoneNumber("+41 00 000 00 00"),
|
||||
false,
|
||||
"agg-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)",
|
||||
expectedArgs: []interface{}{
|
||||
"email@zitadel.com",
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
false,
|
||||
"agg-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)",
|
||||
expectedArgs: []interface{}{
|
||||
"email@zitadel.com",
|
||||
domain.EmailAddress("email@zitadel.com"),
|
||||
false,
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
|
@ -47,9 +47,9 @@ type Human struct {
|
||||
AvatarKey string
|
||||
PreferredLanguage language.Tag
|
||||
Gender domain.Gender
|
||||
Email string
|
||||
Email domain.EmailAddress
|
||||
IsEmailVerified bool
|
||||
Phone string
|
||||
Phone domain.PhoneNumber
|
||||
IsPhoneVerified bool
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ type Email struct {
|
||||
ChangeDate time.Time
|
||||
ResourceOwner string
|
||||
Sequence uint64
|
||||
Email string
|
||||
Email domain.EmailAddress
|
||||
IsVerified bool
|
||||
}
|
||||
|
||||
@ -847,9 +847,9 @@ func prepareUserQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder
|
||||
AvatarKey: avatarKey.String,
|
||||
PreferredLanguage: language.Make(preferredLanguage.String),
|
||||
Gender: domain.Gender(gender.Int32),
|
||||
Email: email.String,
|
||||
Email: domain.EmailAddress(email.String),
|
||||
IsEmailVerified: isEmailVerified.Bool,
|
||||
Phone: phone.String,
|
||||
Phone: domain.PhoneNumber(phone.String),
|
||||
IsPhoneVerified: isPhoneVerified.Bool,
|
||||
}
|
||||
} 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")
|
||||
}
|
||||
|
||||
e.Email = email.String
|
||||
e.Email = domain.EmailAddress(email.String)
|
||||
e.IsVerified = isEmailVerified.Bool
|
||||
|
||||
return e, nil
|
||||
@ -1318,9 +1318,9 @@ func prepareUsersQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilde
|
||||
AvatarKey: avatarKey.String,
|
||||
PreferredLanguage: language.Make(preferredLanguage.String),
|
||||
Gender: domain.Gender(gender.Int32),
|
||||
Email: email.String,
|
||||
Email: domain.EmailAddress(email.String),
|
||||
IsEmailVerified: isEmailVerified.Bool,
|
||||
Phone: phone.String,
|
||||
Phone: domain.PhoneNumber(phone.String),
|
||||
IsPhoneVerified: isPhoneVerified.Bool,
|
||||
}
|
||||
} else if machineID.Valid {
|
||||
|
@ -38,9 +38,9 @@ type HumanAddedEvent struct {
|
||||
PreferredLanguage language.Tag `json:"preferredLanguage,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"`
|
||||
Locality string `json:"locality,omitempty"`
|
||||
@ -75,7 +75,7 @@ func (e *HumanAddedEvent) AddAddressData(
|
||||
}
|
||||
|
||||
func (e *HumanAddedEvent) AddPhoneData(
|
||||
phoneNumber string,
|
||||
phoneNumber domain.PhoneNumber,
|
||||
) {
|
||||
e.PhoneNumber = phoneNumber
|
||||
}
|
||||
@ -99,7 +99,7 @@ func NewHumanAddedEvent(
|
||||
displayName string,
|
||||
preferredLanguage language.Tag,
|
||||
gender domain.Gender,
|
||||
emailAddress string,
|
||||
emailAddress domain.EmailAddress,
|
||||
userLoginMustBeDomain bool,
|
||||
) *HumanAddedEvent {
|
||||
return &HumanAddedEvent{
|
||||
@ -133,30 +133,24 @@ func HumanAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
}
|
||||
|
||||
type HumanRegisteredEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
UserName string `json:"userName"`
|
||||
userLoginMustBeDomain bool
|
||||
|
||||
FirstName string `json:"firstName,omitempty"`
|
||||
LastName string `json:"lastName,omitempty"`
|
||||
NickName string `json:"nickName,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty"`
|
||||
PreferredLanguage language.Tag `json:"preferredLanguage,omitempty"`
|
||||
Gender domain.Gender `json:"gender,omitempty"`
|
||||
|
||||
EmailAddress string `json:"email,omitempty"`
|
||||
|
||||
PhoneNumber string `json:"phone,omitempty"`
|
||||
|
||||
Country string `json:"country,omitempty"`
|
||||
Locality string `json:"locality,omitempty"`
|
||||
PostalCode string `json:"postalCode,omitempty"`
|
||||
Region string `json:"region,omitempty"`
|
||||
StreetAddress string `json:"streetAddress,omitempty"`
|
||||
|
||||
Secret *crypto.CryptoValue `json:"secret,omitempty"`
|
||||
ChangeRequired bool `json:"changeRequired,omitempty"`
|
||||
FirstName string `json:"firstName,omitempty"`
|
||||
LastName string `json:"lastName,omitempty"`
|
||||
NickName string `json:"nickName,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty"`
|
||||
PreferredLanguage language.Tag `json:"preferredLanguage,omitempty"`
|
||||
Gender domain.Gender `json:"gender,omitempty"`
|
||||
EmailAddress domain.EmailAddress `json:"email,omitempty"`
|
||||
PhoneNumber domain.PhoneNumber `json:"phone,omitempty"`
|
||||
Country string `json:"country,omitempty"`
|
||||
Locality string `json:"locality,omitempty"`
|
||||
PostalCode string `json:"postalCode,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{} {
|
||||
@ -182,7 +176,7 @@ func (e *HumanRegisteredEvent) AddAddressData(
|
||||
}
|
||||
|
||||
func (e *HumanRegisteredEvent) AddPhoneData(
|
||||
phoneNumber string,
|
||||
phoneNumber domain.PhoneNumber,
|
||||
) {
|
||||
e.PhoneNumber = phoneNumber
|
||||
}
|
||||
@ -206,7 +200,7 @@ func NewHumanRegisteredEvent(
|
||||
displayName string,
|
||||
preferredLanguage language.Tag,
|
||||
gender domain.Gender,
|
||||
emailAddress string,
|
||||
emailAddress domain.EmailAddress,
|
||||
userLoginMustBeDomain bool,
|
||||
) *HumanRegisteredEvent {
|
||||
return &HumanRegisteredEvent{
|
||||
|
@ -5,10 +5,10 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
)
|
||||
|
||||
@ -24,7 +24,7 @@ const (
|
||||
type HumanEmailChangedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
EmailAddress string `json:"email,omitempty"`
|
||||
EmailAddress domain.EmailAddress `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
func (e *HumanEmailChangedEvent) Data() interface{} {
|
||||
@ -35,7 +35,7 @@ func (e *HumanEmailChangedEvent) UniqueConstraints() []*eventstore.EventUniqueCo
|
||||
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{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
@ -24,7 +25,7 @@ const (
|
||||
type HumanPhoneChangedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
PhoneNumber string `json:"phone,omitempty"`
|
||||
PhoneNumber domain.PhoneNumber `json:"phone,omitempty"`
|
||||
}
|
||||
|
||||
func (e *HumanPhoneChangedEvent) Data() interface{} {
|
||||
@ -35,7 +36,7 @@ func (e *HumanPhoneChangedEvent) UniqueConstraints() []*eventstore.EventUniqueCo
|
||||
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{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
|
@ -69,17 +69,24 @@ Errors:
|
||||
UsernameNotChanged: Benutzername wurde nicht verändert
|
||||
Profile:
|
||||
NotFound: Profil nicht gefunden
|
||||
NotChanged: Profile nicht verändert
|
||||
Invalid: Profildaten sind ungültig
|
||||
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
|
||||
@ -100,6 +107,7 @@ Errors:
|
||||
Username:
|
||||
AlreadyExists: Benutzername ist bereits vergeben
|
||||
Reserved: Benutzername ist bereits vergeben
|
||||
Empty: Benutzername ist leer
|
||||
Code:
|
||||
Empty: Code ist leer
|
||||
NotFound: Code konnte nicht gefunden werden
|
||||
|
@ -70,16 +70,23 @@ Errors:
|
||||
Profile:
|
||||
NotFound: Profile not found
|
||||
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:
|
||||
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
|
||||
@ -100,6 +107,7 @@ Errors:
|
||||
Username:
|
||||
AlreadyExists: Username already taken
|
||||
Reserved: Username is already taken
|
||||
Empty: Username is empty
|
||||
Code:
|
||||
Empty: Code is empty
|
||||
NotFound: Code not found
|
||||
|
@ -70,16 +70,23 @@ Errors:
|
||||
Profile:
|
||||
NotFound: Profil non trouvé
|
||||
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:
|
||||
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é
|
||||
@ -100,6 +107,7 @@ Errors:
|
||||
Username:
|
||||
AlreadyExists: Nom d'utilisateur déjà pris
|
||||
Reserved: Le nom d'utilisateur est déjà pris
|
||||
Empty: Le nom d'utilisateur est vide
|
||||
Code:
|
||||
Empty: Le code est vide
|
||||
NotFound: Code non trouvé
|
||||
|
@ -70,16 +70,23 @@ Errors:
|
||||
Profile:
|
||||
NotFound: Profilo non trovato
|
||||
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:
|
||||
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
|
||||
@ -100,6 +107,7 @@ Errors:
|
||||
Username:
|
||||
AlreadyExists: Nome utente già preso
|
||||
Reserved: Il nome utente è già preso
|
||||
Empty: Il nome utente è vuoto
|
||||
Code:
|
||||
Empty: Il codice è vuoto
|
||||
NotFound: Codice non trovato
|
||||
|
@ -70,16 +70,23 @@ Errors:
|
||||
Profile:
|
||||
NotFound: Profil nie znaleziony
|
||||
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:
|
||||
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
|
||||
@ -100,6 +107,7 @@ Errors:
|
||||
Username:
|
||||
AlreadyExists: Nazwa użytkownika jest już zajęta
|
||||
Reserved: Nazwa użytkownika jest już zajęta
|
||||
Empty: Nazwa użytkownika jest pusty
|
||||
Code:
|
||||
Empty: Kod jest pusty
|
||||
NotFound: Kod nie znaleziony
|
||||
|
@ -70,16 +70,23 @@ Errors:
|
||||
Profile:
|
||||
NotFound: 未找到个人资料
|
||||
NotChanged: 个人资料未更改
|
||||
Invalid: 个人资料数据无效
|
||||
Empty: 简介是空的
|
||||
FirstNameEmpty: 简介中的名字是空的
|
||||
LastNameEmpty: 简介中的姓氏是空的
|
||||
IDMissing: 简介ID丢失
|
||||
Email:
|
||||
NotFound: 电子邮件没有找到
|
||||
Invalid: 电子邮件无效
|
||||
AlreadyVerified: 电子邮件已经过验证
|
||||
NotChanged: 电子邮件未更改
|
||||
Empty: 电子邮件是空的
|
||||
IDMissing: 电子邮件ID丢失
|
||||
Phone:
|
||||
NotFound: 手机号码未找到
|
||||
Invalid: 手机号码无效
|
||||
AlreadyVerified: 手机号码已经验证
|
||||
Empty: 电话号码是空的
|
||||
NotChanged: 电话号码没有改变
|
||||
Address:
|
||||
NotFound: 找不到地址
|
||||
NotChanged: 地址没有改变
|
||||
@ -100,6 +107,7 @@ Errors:
|
||||
Username:
|
||||
AlreadyExists: 用户名已被使用
|
||||
Reserved: 用户名已被使用
|
||||
Empty: 用户名是空的
|
||||
Code:
|
||||
Empty: 验证码为空
|
||||
NotFound: 验证码不存在
|
||||
|
@ -21,10 +21,6 @@ type EmailCode struct {
|
||||
Expiry time.Duration
|
||||
}
|
||||
|
||||
func (e *Email) IsValid() bool {
|
||||
return e.EmailAddress != ""
|
||||
}
|
||||
|
||||
func (e *Email) GenerateEmailCodeIfNeeded(emailGenerator crypto.Generator) (*EmailCode, error) {
|
||||
var emailCode *EmailCode
|
||||
if e.IsEmailVerified {
|
||||
|
@ -3,9 +3,7 @@ package model
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ttacon/libphonenumber"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
@ -27,20 +25,6 @@ type PhoneCode struct {
|
||||
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) {
|
||||
var phoneCode *PhoneCode
|
||||
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