feat: fixes (#228)

* feat: user login names

* fix: user login names

* fix: generate login name
This commit is contained in:
Fabi 2020-06-17 07:25:04 +02:00 committed by GitHub
parent f2cdae9ea3
commit 6fa62ccd0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 7131 additions and 4888 deletions

View File

@ -2,11 +2,13 @@ package eventstore
import ( import (
"context" "context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/sdk" "github.com/caos/zitadel/internal/eventstore/sdk"
org_model "github.com/caos/zitadel/internal/org/model" org_model "github.com/caos/zitadel/internal/org/model"
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing"
usr_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model"
"github.com/caos/zitadel/internal/api/auth" "github.com/caos/zitadel/internal/api/auth"
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view" "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
@ -63,7 +65,11 @@ func (repo *UserRepo) Register(ctx context.Context, registerUser *model.User, or
} }
func (repo *UserRepo) MyProfile(ctx context.Context) (*model.Profile, error) { func (repo *UserRepo) MyProfile(ctx context.Context) (*model.Profile, error) {
return repo.UserEvents.ProfileByID(ctx, auth.GetCtxData(ctx).UserID) user, err := repo.UserByID(ctx, auth.GetCtxData(ctx).UserID)
if err != nil {
return nil, err
}
return user.GetProfile(), nil
} }
func (repo *UserRepo) ChangeMyProfile(ctx context.Context, profile *model.Profile) (*model.Profile, error) { func (repo *UserRepo) ChangeMyProfile(ctx context.Context, profile *model.Profile) (*model.Profile, error) {
@ -74,7 +80,11 @@ func (repo *UserRepo) ChangeMyProfile(ctx context.Context, profile *model.Profil
} }
func (repo *UserRepo) MyEmail(ctx context.Context) (*model.Email, error) { func (repo *UserRepo) MyEmail(ctx context.Context) (*model.Email, error) {
return repo.UserEvents.EmailByID(ctx, auth.GetCtxData(ctx).UserID) user, err := repo.UserByID(ctx, auth.GetCtxData(ctx).UserID)
if err != nil {
return nil, err
}
return user.GetEmail(), nil
} }
func (repo *UserRepo) ChangeMyEmail(ctx context.Context, email *model.Email) (*model.Email, error) { func (repo *UserRepo) ChangeMyEmail(ctx context.Context, email *model.Email) (*model.Email, error) {
@ -101,7 +111,11 @@ func (repo *UserRepo) ResendMyEmailVerificationMail(ctx context.Context) error {
} }
func (repo *UserRepo) MyPhone(ctx context.Context) (*model.Phone, error) { func (repo *UserRepo) MyPhone(ctx context.Context) (*model.Phone, error) {
return repo.UserEvents.PhoneByID(ctx, auth.GetCtxData(ctx).UserID) user, err := repo.UserByID(ctx, auth.GetCtxData(ctx).UserID)
if err != nil {
return nil, err
}
return user.GetPhone(), nil
} }
func (repo *UserRepo) ChangeMyPhone(ctx context.Context, phone *model.Phone) (*model.Phone, error) { func (repo *UserRepo) ChangeMyPhone(ctx context.Context, phone *model.Phone) (*model.Phone, error) {
@ -120,7 +134,11 @@ func (repo *UserRepo) ResendMyPhoneVerificationCode(ctx context.Context) error {
} }
func (repo *UserRepo) MyAddress(ctx context.Context) (*model.Address, error) { func (repo *UserRepo) MyAddress(ctx context.Context) (*model.Address, error) {
return repo.UserEvents.AddressByID(ctx, auth.GetCtxData(ctx).UserID) user, err := repo.UserByID(ctx, auth.GetCtxData(ctx).UserID)
if err != nil {
return nil, err
}
return user.GetAddress(), nil
} }
func (repo *UserRepo) ChangeMyAddress(ctx context.Context, address *model.Address) (*model.Address, error) { func (repo *UserRepo) ChangeMyAddress(ctx context.Context, address *model.Address) (*model.Address, error) {
@ -205,8 +223,23 @@ func (repo *UserRepo) SignOut(ctx context.Context, agentID, userID string) error
return repo.UserEvents.SignOut(ctx, agentID, userID) return repo.UserEvents.SignOut(ctx, agentID, userID)
} }
func (repo *UserRepo) UserByID(ctx context.Context, userID string) (*model.User, error) { func (repo *UserRepo) UserByID(ctx context.Context, id string) (*model.UserView, error) {
return repo.UserEvents.UserByID(ctx, userID) user, err := repo.View.UserByID(id)
if err != nil {
return nil, err
}
events, err := repo.UserEvents.UserEventsByID(ctx, id, user.Sequence)
if err != nil {
logging.Log("EVENT-PSoc3").WithError(err).Debug("error retrieving new events")
return usr_view_model.UserToModel(user), nil
}
userCopy := *user
for _, event := range events {
if err := userCopy.AppendEvent(event); err != nil {
return usr_view_model.UserToModel(user), nil
}
}
return usr_view_model.UserToModel(&userCopy), nil
} }
func checkIDs(ctx context.Context, obj es_models.ObjectRoot) error { func checkIDs(ctx context.Context, obj es_models.ObjectRoot) error {

View File

@ -101,23 +101,11 @@ func (u *User) fillLoginNames(user *view_model.UserView) (err error) {
if err != nil { if err != nil {
return err return err
} }
user.LoginNames = getLoginNames(policy, user.UserName, org.Domains) user.SetLoginNames(policy, org.Domains)
user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain)
return nil return nil
} }
func getLoginNames(policy *org_model.OrgIamPolicy, userName string, domains []*org_model.OrgDomain) []string {
loginNames := make([]string, 0)
if !policy.UserLoginMustBeDomain {
return []string{userName}
}
for _, d := range domains {
if d.Verified {
loginNames = append(loginNames, userName+"@"+d.Domain)
}
}
return loginNames
}
func (u *User) ProcessOrg(event *models.Event) (err error) { func (u *User) ProcessOrg(event *models.Event) (err error) {
switch event.Type { switch event.Type {
case org_es_model.OrgDomainVerified, case org_es_model.OrgDomainVerified,
@ -126,6 +114,8 @@ func (u *User) ProcessOrg(event *models.Event) (err error) {
org_es_model.OrgIamPolicyChanged, org_es_model.OrgIamPolicyChanged,
org_es_model.OrgIamPolicyRemoved: org_es_model.OrgIamPolicyRemoved:
return u.fillLoginNamesOnOrgUsers(event) return u.fillLoginNamesOnOrgUsers(event)
case org_es_model.OrgDomainPrimarySet:
return u.fillPreferredLoginNamesOnOrgUsers(event)
default: default:
return u.view.ProcessedUserSequence(event.Sequence) return u.view.ProcessedUserSequence(event.Sequence)
} }
@ -134,6 +124,7 @@ func (u *User) ProcessOrg(event *models.Event) (err error) {
} }
return nil return nil
} }
func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error { func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error {
org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner)) org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner))
if err != nil { if err != nil {
@ -148,7 +139,7 @@ func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error {
return err return err
} }
for _, user := range users { for _, user := range users {
user.LoginNames = getLoginNames(policy, user.UserName, org.Domains) user.SetLoginNames(policy, org.Domains)
err := u.view.PutUser(user, event.Sequence) err := u.view.PutUser(user, event.Sequence)
if err != nil { if err != nil {
return err return err
@ -157,6 +148,32 @@ func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error {
return nil return nil
} }
func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error {
org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner))
if err != nil {
return err
}
policy, err := u.orgEvents.GetOrgIamPolicy(context.Background(), event.ResourceOwner)
if err != nil {
return err
}
if !policy.UserLoginMustBeDomain {
return nil
}
users, err := u.view.UsersByOrgID(event.AggregateID)
if err != nil {
return err
}
for _, user := range users {
user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain)
err := u.view.PutUser(user, 0)
if err != nil {
return err
}
}
return nil
}
func (p *User) OnError(event *models.Event, err error) error { func (p *User) OnError(event *models.Event, err error) error {
logging.LogWithFields("SPOOL-is8wa", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler") logging.LogWithFields("SPOOL-is8wa", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler")
return spooler.HandleError(event, err, p.view.GetLatestUserFailedEvent, p.view.ProcessedUserFailedEvent, p.view.ProcessedUserSequence, p.errorCountUntilSkip) return spooler.HandleError(event, err, p.view.GetLatestUserFailedEvent, p.view.ProcessedUserFailedEvent, p.view.ProcessedUserSequence, p.errorCountUntilSkip)

View File

@ -25,7 +25,7 @@ type UserRepository interface {
SignOut(ctx context.Context, agentID, userID string) error SignOut(ctx context.Context, agentID, userID string) error
UserByID(ctx context.Context, userID string) (*model.User, error) UserByID(ctx context.Context, userID string) (*model.UserView, error)
} }
type myUserRepo interface { type myUserRepo interface {

View File

@ -2,6 +2,7 @@ package eventstore
import ( import (
"context" "context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/api/auth" "github.com/caos/zitadel/internal/api/auth"
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
@ -20,8 +21,23 @@ type UserRepo struct {
View *view.View View *view.View
} }
func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_model.User, err error) { func (repo *UserRepo) UserByID(ctx context.Context, id string) (*usr_model.UserView, error) {
return repo.UserEvents.UserByID(ctx, id) user, err := repo.View.UserByID(id)
if err != nil {
return nil, err
}
events, err := repo.UserEvents.UserEventsByID(ctx, id, user.Sequence)
if err != nil {
logging.Log("EVENT-PSoc3").WithError(err).Debug("error retrieving new events")
return model.UserToModel(user), nil
}
userCopy := *user
for _, event := range events {
if err := userCopy.AppendEvent(event); err != nil {
return model.UserToModel(user), nil
}
}
return model.UserToModel(&userCopy), nil
} }
func (repo *UserRepo) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) { func (repo *UserRepo) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) {
@ -119,7 +135,11 @@ func (repo *UserRepo) RequestSetPassword(ctx context.Context, id string, notifyT
} }
func (repo *UserRepo) ProfileByID(ctx context.Context, userID string) (*usr_model.Profile, error) { func (repo *UserRepo) ProfileByID(ctx context.Context, userID string) (*usr_model.Profile, error) {
return repo.UserEvents.ProfileByID(ctx, userID) user, err := repo.UserByID(ctx, userID)
if err != nil {
return nil, err
}
return user.GetProfile(), nil
} }
func (repo *UserRepo) ChangeProfile(ctx context.Context, profile *usr_model.Profile) (*usr_model.Profile, error) { func (repo *UserRepo) ChangeProfile(ctx context.Context, profile *usr_model.Profile) (*usr_model.Profile, error) {
@ -127,7 +147,11 @@ func (repo *UserRepo) ChangeProfile(ctx context.Context, profile *usr_model.Prof
} }
func (repo *UserRepo) EmailByID(ctx context.Context, userID string) (*usr_model.Email, error) { func (repo *UserRepo) EmailByID(ctx context.Context, userID string) (*usr_model.Email, error) {
return repo.UserEvents.EmailByID(ctx, userID) user, err := repo.UserByID(ctx, userID)
if err != nil {
return nil, err
}
return user.GetEmail(), nil
} }
func (repo *UserRepo) ChangeEmail(ctx context.Context, email *usr_model.Email) (*usr_model.Email, error) { func (repo *UserRepo) ChangeEmail(ctx context.Context, email *usr_model.Email) (*usr_model.Email, error) {
@ -139,7 +163,11 @@ func (repo *UserRepo) CreateEmailVerificationCode(ctx context.Context, userID st
} }
func (repo *UserRepo) PhoneByID(ctx context.Context, userID string) (*usr_model.Phone, error) { func (repo *UserRepo) PhoneByID(ctx context.Context, userID string) (*usr_model.Phone, error) {
return repo.UserEvents.PhoneByID(ctx, userID) user, err := repo.UserByID(ctx, userID)
if err != nil {
return nil, err
}
return user.GetPhone(), nil
} }
func (repo *UserRepo) ChangePhone(ctx context.Context, email *usr_model.Phone) (*usr_model.Phone, error) { func (repo *UserRepo) ChangePhone(ctx context.Context, email *usr_model.Phone) (*usr_model.Phone, error) {
@ -151,7 +179,11 @@ func (repo *UserRepo) CreatePhoneVerificationCode(ctx context.Context, userID st
} }
func (repo *UserRepo) AddressByID(ctx context.Context, userID string) (*usr_model.Address, error) { func (repo *UserRepo) AddressByID(ctx context.Context, userID string) (*usr_model.Address, error) {
return repo.UserEvents.AddressByID(ctx, userID) user, err := repo.UserByID(ctx, userID)
if err != nil {
return nil, err
}
return user.GetAddress(), nil
} }
func (repo *UserRepo) ChangeAddress(ctx context.Context, address *usr_model.Address) (*usr_model.Address, error) { func (repo *UserRepo) ChangeAddress(ctx context.Context, address *usr_model.Address) (*usr_model.Address, error) {

View File

@ -39,7 +39,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, ev
&ProjectMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectMember"), errorCount}, userEvents: repos.UserEvents}, &ProjectMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectMember"), errorCount}, userEvents: repos.UserEvents},
&ProjectGrantMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectGrantMember"), errorCount}, userEvents: repos.UserEvents}, &ProjectGrantMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectGrantMember"), errorCount}, userEvents: repos.UserEvents},
&Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}, projectEvents: repos.ProjectEvents}, &Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}, projectEvents: repos.ProjectEvents},
&User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, eventstore: eventstore}, &User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, eventstore: eventstore, orgEvents: repos.OrgEvents},
&UserGrant{handler: handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount}, projectEvents: repos.ProjectEvents, userEvents: repos.UserEvents, orgEvents: repos.OrgEvents}, &UserGrant{handler: handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount}, projectEvents: repos.ProjectEvents, userEvents: repos.UserEvents, orgEvents: repos.OrgEvents},
&Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}}, &Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}},
&OrgMember{handler: handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount}, userEvents: repos.UserEvents}, &OrgMember{handler: handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount}, userEvents: repos.UserEvents},

View File

@ -1,6 +1,11 @@
package handler package handler
import ( import (
"context"
es_models "github.com/caos/zitadel/internal/eventstore/models"
org_model "github.com/caos/zitadel/internal/org/model"
org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
"time" "time"
@ -9,13 +14,13 @@ import (
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/eventstore/spooler"
"github.com/caos/zitadel/internal/user/repository/eventsourcing"
view_model "github.com/caos/zitadel/internal/user/repository/view/model" view_model "github.com/caos/zitadel/internal/user/repository/view/model"
) )
type User struct { type User struct {
handler handler
eventstore eventstore.Eventstore eventstore eventstore.Eventstore
orgEvents *org_events.OrgEventstore
} }
const ( const (
@ -33,15 +38,29 @@ func (p *User) EventQuery() (*models.SearchQuery, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return eventsourcing.UserQuery(sequence), nil return es_models.NewSearchQuery().
AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate).
LatestSequenceFilter(sequence), nil
} }
func (p *User) Process(event *models.Event) (err error) { func (u *User) Process(event *models.Event) (err error) {
switch event.AggregateType {
case es_model.UserAggregate:
return u.ProcessUser(event)
case org_es_model.OrgAggregate:
return u.ProcessOrg(event)
default:
return nil
}
}
func (p *User) ProcessUser(event *models.Event) (err error) {
user := new(view_model.UserView) user := new(view_model.UserView)
switch event.Type { switch event.Type {
case es_model.UserAdded, case es_model.UserAdded,
es_model.UserRegistered: es_model.UserRegistered:
user.AppendEvent(event) user.AppendEvent(event)
p.fillLoginNames(user)
case es_model.UserProfileChanged, case es_model.UserProfileChanged,
es_model.UserEmailChanged, es_model.UserEmailChanged,
es_model.UserEmailVerified, es_model.UserEmailVerified,
@ -68,7 +87,89 @@ func (p *User) Process(event *models.Event) (err error) {
if err != nil { if err != nil {
return err return err
} }
return p.view.PutUser(user) return p.view.PutUser(user, user.Sequence)
}
func (u *User) ProcessOrg(event *models.Event) (err error) {
switch event.Type {
case org_es_model.OrgDomainVerified,
org_es_model.OrgDomainRemoved,
org_es_model.OrgIamPolicyAdded,
org_es_model.OrgIamPolicyChanged,
org_es_model.OrgIamPolicyRemoved:
return u.fillLoginNamesOnOrgUsers(event)
case org_es_model.OrgDomainPrimarySet:
return u.fillPreferredLoginNamesOnOrgUsers(event)
default:
return u.view.ProcessedUserSequence(event.Sequence)
}
if err != nil {
return err
}
return nil
}
func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error {
org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner))
if err != nil {
return err
}
policy, err := u.orgEvents.GetOrgIamPolicy(context.Background(), event.ResourceOwner)
if err != nil {
return err
}
users, err := u.view.UsersByOrgID(event.AggregateID)
if err != nil {
return err
}
for _, user := range users {
user.SetLoginNames(policy, org.Domains)
err := u.view.PutUser(user, 0)
if err != nil {
return err
}
}
return nil
}
func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error {
org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner))
if err != nil {
return err
}
policy, err := u.orgEvents.GetOrgIamPolicy(context.Background(), event.ResourceOwner)
if err != nil {
return err
}
if !policy.UserLoginMustBeDomain {
return nil
}
users, err := u.view.UsersByOrgID(event.AggregateID)
if err != nil {
return err
}
for _, user := range users {
user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain)
err := u.view.PutUser(user, 0)
if err != nil {
return err
}
}
return nil
}
func (u *User) fillLoginNames(user *view_model.UserView) (err error) {
org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(user.ResourceOwner))
if err != nil {
return err
}
policy, err := u.orgEvents.GetOrgIamPolicy(context.Background(), user.ResourceOwner)
if err != nil {
return err
}
user.SetLoginNames(policy, org.Domains)
user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain)
return nil
} }
func (p *User) OnError(event *models.Event, err error) error { func (p *User) OnError(event *models.Event, err error) error {

View File

@ -23,6 +23,10 @@ func (v *View) GetGlobalUserByEmail(email string) (*model.UserView, error) {
return view.GetGlobalUserByEmail(v.Db, userTable, email) return view.GetGlobalUserByEmail(v.Db, userTable, email)
} }
func (v *View) UsersByOrgID(orgID string) ([]*model.UserView, error) {
return view.UsersByOrgID(v.Db, userTable, orgID)
}
func (v *View) IsUserUnique(userName, email string) (bool, error) { func (v *View) IsUserUnique(userName, email string) (bool, error) {
return view.IsUserUnique(v.Db, userTable, userName, email) return view.IsUserUnique(v.Db, userTable, userName, email)
} }
@ -31,12 +35,15 @@ func (v *View) UserMfas(userID string) ([]*usr_model.MultiFactor, error) {
return view.UserMfas(v.Db, userTable, userID) return view.UserMfas(v.Db, userTable, userID)
} }
func (v *View) PutUser(user *model.UserView) error { func (v *View) PutUser(user *model.UserView, sequence uint64) error {
err := view.PutUser(v.Db, userTable, user) err := view.PutUser(v.Db, userTable, user)
if err != nil { if err != nil {
return err return err
} }
return v.ProcessedUserSequence(user.Sequence) if sequence != 0 {
return v.ProcessedUserSequence(sequence)
}
return nil
} }
func (v *View) DeleteUser(userID string, eventSequence uint64) error { func (v *View) DeleteUser(userID string, eventSequence uint64) error {

View File

@ -7,7 +7,7 @@ import (
) )
type UserRepository interface { type UserRepository interface {
UserByID(ctx context.Context, id string) (*model.User, error) UserByID(ctx context.Context, id string) (*model.UserView, error)
CreateUser(ctx context.Context, user *model.User) (*model.User, error) CreateUser(ctx context.Context, user *model.User) (*model.User, error)
RegisterUser(ctx context.Context, user *model.User, resourceOwner string) (*model.User, error) RegisterUser(ctx context.Context, user *model.User, resourceOwner string) (*model.User, error)
DeactivateUser(ctx context.Context, id string) (*model.User, error) DeactivateUser(ctx context.Context, id string) (*model.User, error)

View File

@ -2,8 +2,8 @@ package model
import ( import (
es_models "github.com/caos/zitadel/internal/eventstore/models" es_models "github.com/caos/zitadel/internal/eventstore/models"
"strings"
"github.com/golang/protobuf/ptypes/timestamp" "github.com/golang/protobuf/ptypes/timestamp"
"strings"
) )
type Org struct { type Org struct {
@ -57,6 +57,15 @@ func (o *Org) ContainsDomain(domain *OrgDomain) bool {
return false return false
} }
func (o *Org) GetPrimaryDomain() *OrgDomain {
for _, d := range o.Domains {
if d.Primary {
return d
}
}
return nil
}
func (o *Org) ContainsMember(userID string) bool { func (o *Org) ContainsMember(userID string) bool {
for _, member := range o.Members { for _, member := range o.Members {
if member.UserID == userID { if member.UserID == userID {

View File

@ -8,13 +8,15 @@ import (
type Profile struct { type Profile struct {
es_models.ObjectRoot es_models.ObjectRoot
UserName string UserName string
FirstName string FirstName string
LastName string LastName string
NickName string NickName string
DisplayName string DisplayName string
PreferredLanguage language.Tag PreferredLanguage language.Tag
Gender Gender Gender Gender
PreferredLoginName string
LoginNames []string
} }
func (p *Profile) IsValid() bool { func (p *Profile) IsValid() bool {

View File

@ -1,6 +1,8 @@
package model package model
import ( import (
"github.com/caos/zitadel/internal/eventstore/models"
"golang.org/x/text/language"
"time" "time"
req_model "github.com/caos/zitadel/internal/auth_request/model" req_model "github.com/caos/zitadel/internal/auth_request/model"
@ -18,6 +20,7 @@ type UserView struct {
PasswordChanged time.Time PasswordChanged time.Time
LastLogin time.Time LastLogin time.Time
UserName string UserName string
PreferredLoginName string
LoginNames []string LoginNames []string
FirstName string FirstName string
LastName string LastName string
@ -121,3 +124,69 @@ func (u *UserView) MfaTypesAllowed(level req_model.MfaLevel) []req_model.MfaType
} }
return types return types
} }
func (u *UserView) GetProfile() *Profile {
return &Profile{
ObjectRoot: models.ObjectRoot{
AggregateID: u.ID,
Sequence: u.Sequence,
ResourceOwner: u.ResourceOwner,
CreationDate: u.CreationDate,
ChangeDate: u.ChangeDate,
},
UserName: u.UserName,
FirstName: u.FirstName,
LastName: u.LastName,
NickName: u.NickName,
DisplayName: u.DisplayName,
PreferredLanguage: language.Make(u.PreferredLanguage),
Gender: u.Gender,
PreferredLoginName: u.PreferredLoginName,
LoginNames: u.LoginNames,
}
}
func (u *UserView) GetPhone() *Phone {
return &Phone{
ObjectRoot: models.ObjectRoot{
AggregateID: u.ID,
Sequence: u.Sequence,
ResourceOwner: u.ResourceOwner,
CreationDate: u.CreationDate,
ChangeDate: u.ChangeDate,
},
PhoneNumber: u.Phone,
IsPhoneVerified: u.IsPhoneVerified,
}
}
func (u *UserView) GetEmail() *Email {
return &Email{
ObjectRoot: models.ObjectRoot{
AggregateID: u.ID,
Sequence: u.Sequence,
ResourceOwner: u.ResourceOwner,
CreationDate: u.CreationDate,
ChangeDate: u.ChangeDate,
},
EmailAddress: u.Email,
IsEmailVerified: u.IsEmailVerified,
}
}
func (u *UserView) GetAddress() *Address {
return &Address{
ObjectRoot: models.ObjectRoot{
AggregateID: u.ID,
Sequence: u.Sequence,
ResourceOwner: u.ResourceOwner,
CreationDate: u.CreationDate,
ChangeDate: u.ChangeDate,
},
Country: u.Country,
Locality: u.Locality,
PostalCode: u.PostalCode,
Region: u.Region,
StreetAddress: u.StreetAddress,
}
}

View File

@ -106,6 +106,9 @@ func (es *UserEventstore) UserEventsByID(ctx context.Context, id string, sequenc
func (es *UserEventstore) PrepareCreateUser(ctx context.Context, user *usr_model.User, pwPolicy *policy_model.PasswordComplexityPolicy, orgIamPolicy *org_model.OrgIamPolicy, resourceOwner string) (*model.User, []*es_models.Aggregate, error) { func (es *UserEventstore) PrepareCreateUser(ctx context.Context, user *usr_model.User, pwPolicy *policy_model.PasswordComplexityPolicy, orgIamPolicy *org_model.OrgIamPolicy, resourceOwner string) (*model.User, []*es_models.Aggregate, error) {
err := user.CheckOrgIamPolicy(orgIamPolicy) err := user.CheckOrgIamPolicy(orgIamPolicy)
if err != nil {
return nil, nil, err
}
if !user.IsValid() { if !user.IsValid() {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "User is invalid") return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "User is invalid")
} }

View File

@ -2,6 +2,7 @@ package model
import ( import (
"encoding/json" "encoding/json"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/lib/pq" "github.com/lib/pq"
"time" "time"
@ -39,6 +40,7 @@ type UserView struct {
LastLogin time.Time `json:"-" gorm:"column:last_login"` LastLogin time.Time `json:"-" gorm:"column:last_login"`
UserName string `json:"userName" gorm:"column:user_name"` UserName string `json:"userName" gorm:"column:user_name"`
LoginNames pq.StringArray `json:"-" gorm:"column:login_names"` LoginNames pq.StringArray `json:"-" gorm:"column:login_names"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
FirstName string `json:"firstName" gorm:"column:first_name"` FirstName string `json:"firstName" gorm:"column:first_name"`
LastName string `json:"lastName" gorm:"column:last_name"` LastName string `json:"lastName" gorm:"column:last_name"`
NickName string `json:"nickName" gorm:"column:nick_name"` NickName string `json:"nickName" gorm:"column:nick_name"`
@ -74,6 +76,7 @@ func UserFromModel(user *model.UserView) *UserView {
LastLogin: user.LastLogin, LastLogin: user.LastLogin,
UserName: user.UserName, UserName: user.UserName,
LoginNames: user.LoginNames, LoginNames: user.LoginNames,
PreferredLoginName: user.PreferredLoginName,
FirstName: user.FirstName, FirstName: user.FirstName,
LastName: user.LastName, LastName: user.LastName,
NickName: user.NickName, NickName: user.NickName,
@ -108,6 +111,8 @@ func UserToModel(user *UserView) *model.UserView {
PasswordChangeRequired: user.PasswordChangeRequired, PasswordChangeRequired: user.PasswordChangeRequired,
PasswordChanged: user.PasswordChanged, PasswordChanged: user.PasswordChanged,
LastLogin: user.LastLogin, LastLogin: user.LastLogin,
PreferredLoginName: user.PreferredLoginName,
LoginNames: user.LoginNames,
UserName: user.UserName, UserName: user.UserName,
FirstName: user.FirstName, FirstName: user.FirstName,
LastName: user.LastName, LastName: user.LastName,
@ -140,6 +145,24 @@ func UsersToModel(users []*UserView) []*model.UserView {
return result return result
} }
func (u *UserView) GenerateLoginName(domain string) string {
return u.UserName + "@" + domain
}
func (u *UserView) SetLoginNames(policy *org_model.OrgIamPolicy, domains []*org_model.OrgDomain) {
loginNames := make([]string, 0)
if !policy.UserLoginMustBeDomain {
u.LoginNames = []string{u.UserName}
return
}
for _, d := range domains {
if d.Verified {
loginNames = append(loginNames, u.GenerateLoginName(d.Domain))
}
}
u.LoginNames = loginNames
}
func (u *UserView) AppendEvent(event *models.Event) (err error) { func (u *UserView) AppendEvent(event *models.Event) (err error) {
u.ChangeDate = event.CreationDate u.ChangeDate = event.CreationDate
u.Sequence = event.Sequence u.Sequence = event.Sequence

View File

@ -0,0 +1,6 @@
BEGIN;
ALTER TABLE auth.users ADD COLUMN preferred_login_name TEXT;
ALTER TABLE management.users ADD COLUMN preferred_login_name TEXT;
COMMIT;

File diff suppressed because it is too large Load Diff

View File

@ -145,7 +145,7 @@
"200": { "200": {
"description": "A successful response.", "description": "A successful response.",
"schema": { "schema": {
"$ref": "#/definitions/v1UserAddress" "$ref": "#/definitions/v1UserAddressView"
} }
} }
}, },
@ -185,7 +185,7 @@
"200": { "200": {
"description": "A successful response.", "description": "A successful response.",
"schema": { "schema": {
"$ref": "#/definitions/v1UserEmail" "$ref": "#/definitions/v1UserEmailView"
} }
} }
}, },
@ -387,7 +387,7 @@
"200": { "200": {
"description": "A successful response.", "description": "A successful response.",
"schema": { "schema": {
"$ref": "#/definitions/v1UserPhone" "$ref": "#/definitions/v1UserPhoneView"
} }
} }
}, },
@ -480,7 +480,7 @@
"200": { "200": {
"description": "A successful response.", "description": "A successful response.",
"schema": { "schema": {
"$ref": "#/definitions/v1UserProfile" "$ref": "#/definitions/v1UserProfileView"
} }
} }
}, },
@ -872,6 +872,41 @@
} }
} }
}, },
"v1UserAddressView": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"country": {
"type": "string"
},
"locality": {
"type": "string"
},
"postal_code": {
"type": "string"
},
"region": {
"type": "string"
},
"street_address": {
"type": "string"
},
"sequence": {
"type": "string",
"format": "uint64"
},
"creation_date": {
"type": "string",
"format": "date-time"
},
"change_date": {
"type": "string",
"format": "date-time"
}
}
},
"v1UserEmail": { "v1UserEmail": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -899,6 +934,33 @@
} }
} }
}, },
"v1UserEmailView": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"email": {
"type": "string"
},
"isEmailVerified": {
"type": "boolean",
"format": "boolean"
},
"sequence": {
"type": "string",
"format": "uint64"
},
"creation_date": {
"type": "string",
"format": "date-time"
},
"change_date": {
"type": "string",
"format": "date-time"
}
}
},
"v1UserGrantSearchKey": { "v1UserGrantSearchKey": {
"type": "string", "type": "string",
"enum": [ "enum": [
@ -1021,6 +1083,33 @@
} }
} }
}, },
"v1UserPhoneView": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"phone": {
"type": "string"
},
"is_phone_verified": {
"type": "boolean",
"format": "boolean"
},
"sequence": {
"type": "string",
"format": "uint64"
},
"creation_date": {
"type": "string",
"format": "date-time"
},
"change_date": {
"type": "string",
"format": "date-time"
}
}
},
"v1UserProfile": { "v1UserProfile": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -1062,6 +1151,56 @@
} }
} }
}, },
"v1UserProfileView": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"user_name": {
"type": "string"
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"nick_name": {
"type": "string"
},
"display_name": {
"type": "string"
},
"preferred_language": {
"type": "string"
},
"gender": {
"$ref": "#/definitions/v1Gender"
},
"sequence": {
"type": "string",
"format": "uint64"
},
"creation_date": {
"type": "string",
"format": "date-time"
},
"change_date": {
"type": "string",
"format": "date-time"
},
"login_names": {
"type": "array",
"items": {
"type": "string"
}
},
"preferred_login_name": {
"type": "string"
}
}
},
"v1UserSessionState": { "v1UserSessionState": {
"type": "string", "type": "string",
"enum": [ "enum": [

View File

@ -8,36 +8,36 @@ import (
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
) )
func (s *Server) GetMyUserProfile(ctx context.Context, _ *empty.Empty) (*UserProfile, error) { func (s *Server) GetMyUserProfile(ctx context.Context, _ *empty.Empty) (*UserProfileView, error) {
profile, err := s.repo.MyProfile(ctx) profile, err := s.repo.MyProfile(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return profileFromModel(profile), nil return profileViewFromModel(profile), nil
} }
func (s *Server) GetMyUserEmail(ctx context.Context, _ *empty.Empty) (*UserEmail, error) { func (s *Server) GetMyUserEmail(ctx context.Context, _ *empty.Empty) (*UserEmailView, error) {
email, err := s.repo.MyEmail(ctx) email, err := s.repo.MyEmail(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return emailFromModel(email), nil return emailViewFromModel(email), nil
} }
func (s *Server) GetMyUserPhone(ctx context.Context, _ *empty.Empty) (*UserPhone, error) { func (s *Server) GetMyUserPhone(ctx context.Context, _ *empty.Empty) (*UserPhoneView, error) {
phone, err := s.repo.MyPhone(ctx) phone, err := s.repo.MyPhone(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return phoneFromModel(phone), nil return phoneViewFromModel(phone), nil
} }
func (s *Server) GetMyUserAddress(ctx context.Context, _ *empty.Empty) (*UserAddress, error) { func (s *Server) GetMyUserAddress(ctx context.Context, _ *empty.Empty) (*UserAddressView, error) {
address, err := s.repo.MyAddress(ctx) address, err := s.repo.MyAddress(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return addressFromModel(address), nil return addressViewFromModel(address), nil
} }
func (s *Server) GetMyMfas(ctx context.Context, _ *empty.Empty) (*MultiFactors, error) { func (s *Server) GetMyMfas(ctx context.Context, _ *empty.Empty) (*MultiFactors, error) {

View File

@ -33,6 +33,30 @@ func profileFromModel(profile *usr_model.Profile) *UserProfile {
} }
} }
func profileViewFromModel(profile *usr_model.Profile) *UserProfileView {
creationDate, err := ptypes.TimestampProto(profile.CreationDate)
logging.Log("GRPC-s9iKs").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(profile.ChangeDate)
logging.Log("GRPC-9sujE").OnError(err).Debug("unable to parse timestamp")
return &UserProfileView{
Id: profile.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: profile.Sequence,
UserName: profile.UserName,
FirstName: profile.FirstName,
LastName: profile.LastName,
DisplayName: profile.DisplayName,
NickName: profile.NickName,
PreferredLanguage: profile.PreferredLanguage.String(),
Gender: genderFromModel(profile.Gender),
LoginNames: profile.LoginNames,
PreferredLoginName: profile.PreferredLoginName,
}
}
func updateProfileToModel(ctx context.Context, u *UpdateUserProfileRequest) *usr_model.Profile { func updateProfileToModel(ctx context.Context, u *UpdateUserProfileRequest) *usr_model.Profile {
preferredLanguage, err := language.Parse(u.PreferredLanguage) preferredLanguage, err := language.Parse(u.PreferredLanguage)
logging.Log("GRPC-lk73L").OnError(err).Debug("language malformed") logging.Log("GRPC-lk73L").OnError(err).Debug("language malformed")
@ -65,6 +89,23 @@ func emailFromModel(email *usr_model.Email) *UserEmail {
} }
} }
func emailViewFromModel(email *usr_model.Email) *UserEmailView {
creationDate, err := ptypes.TimestampProto(email.CreationDate)
logging.Log("GRPC-LSp8s").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(email.ChangeDate)
logging.Log("GRPC-6szJe").OnError(err).Debug("unable to parse timestamp")
return &UserEmailView{
Id: email.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: email.Sequence,
Email: email.EmailAddress,
IsEmailVerified: email.IsEmailVerified,
}
}
func updateEmailToModel(ctx context.Context, e *UpdateUserEmailRequest) *usr_model.Email { func updateEmailToModel(ctx context.Context, e *UpdateUserEmailRequest) *usr_model.Email {
return &usr_model.Email{ return &usr_model.Email{
ObjectRoot: models.ObjectRoot{AggregateID: auth.GetCtxData(ctx).UserID}, ObjectRoot: models.ObjectRoot{AggregateID: auth.GetCtxData(ctx).UserID},
@ -89,6 +130,23 @@ func phoneFromModel(phone *usr_model.Phone) *UserPhone {
} }
} }
func phoneViewFromModel(phone *usr_model.Phone) *UserPhoneView {
creationDate, err := ptypes.TimestampProto(phone.CreationDate)
logging.Log("GRPC-s5zJS").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(phone.ChangeDate)
logging.Log("GRPC-s9kLe").OnError(err).Debug("unable to parse timestamp")
return &UserPhoneView{
Id: phone.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: phone.Sequence,
Phone: phone.PhoneNumber,
IsPhoneVerified: phone.IsPhoneVerified,
}
}
func updatePhoneToModel(ctx context.Context, e *UpdateUserPhoneRequest) *usr_model.Phone { func updatePhoneToModel(ctx context.Context, e *UpdateUserPhoneRequest) *usr_model.Phone {
return &usr_model.Phone{ return &usr_model.Phone{
ObjectRoot: models.ObjectRoot{AggregateID: auth.GetCtxData(ctx).UserID}, ObjectRoot: models.ObjectRoot{AggregateID: auth.GetCtxData(ctx).UserID},
@ -116,6 +174,26 @@ func addressFromModel(address *usr_model.Address) *UserAddress {
} }
} }
func addressViewFromModel(address *usr_model.Address) *UserAddressView {
creationDate, err := ptypes.TimestampProto(address.CreationDate)
logging.Log("GRPC-sk4fS").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(address.ChangeDate)
logging.Log("GRPC-9siEs").OnError(err).Debug("unable to parse timestamp")
return &UserAddressView{
Id: address.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: address.Sequence,
Country: address.Country,
StreetAddress: address.StreetAddress,
Region: address.Region,
PostalCode: address.PostalCode,
Locality: address.Locality,
}
}
func updateAddressToModel(ctx context.Context, address *UpdateUserAddressRequest) *usr_model.Address { func updateAddressToModel(ctx context.Context, address *UpdateUserAddressRequest) *usr_model.Address {
return &usr_model.Address{ return &usr_model.Address{
ObjectRoot: models.ObjectRoot{AggregateID: auth.GetCtxData(ctx).UserID}, ObjectRoot: models.ObjectRoot{AggregateID: auth.GetCtxData(ctx).UserID},

View File

@ -46,18 +46,12 @@ func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID string, sc
for _, scope := range scopes { for _, scope := range scopes {
switch scope { switch scope {
case scopeOpenID: case scopeOpenID:
userInfo.Subject = user.AggregateID userInfo.Subject = user.ID
case scopeEmail: case scopeEmail:
if user.Email == nil { userInfo.Email = user.Email
continue
}
userInfo.Email = user.EmailAddress
userInfo.EmailVerified = user.IsEmailVerified userInfo.EmailVerified = user.IsEmailVerified
case scopeProfile: case scopeProfile:
if user.Profile == nil { userInfo.Name = user.DisplayName
continue
}
userInfo.Name = user.FirstName + " " + user.LastName
userInfo.FamilyName = user.LastName userInfo.FamilyName = user.LastName
userInfo.GivenName = user.FirstName userInfo.GivenName = user.FirstName
userInfo.Nickname = user.NickName userInfo.Nickname = user.NickName
@ -65,15 +59,9 @@ func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID string, sc
userInfo.UpdatedAt = user.ChangeDate userInfo.UpdatedAt = user.ChangeDate
userInfo.Gender = oidc.Gender(getGender(user.Gender)) userInfo.Gender = oidc.Gender(getGender(user.Gender))
case scopePhone: case scopePhone:
if user.Phone == nil { userInfo.PhoneNumber = user.Phone
continue
}
userInfo.PhoneNumber = user.PhoneNumber
userInfo.PhoneNumberVerified = user.IsPhoneVerified userInfo.PhoneNumberVerified = user.IsPhoneVerified
case scopeAddress: case scopeAddress:
if user.Address == nil {
continue
}
userInfo.Address.StreetAddress = user.StreetAddress userInfo.Address.StreetAddress = user.StreetAddress
userInfo.Address.Locality = user.Locality userInfo.Address.Locality = user.Locality
userInfo.Address.Region = user.Region userInfo.Address.Region = user.Region

View File

@ -62,7 +62,7 @@ service AuthService {
} }
//User //User
rpc GetMyUserProfile(google.protobuf.Empty) returns (UserProfile) { rpc GetMyUserProfile(google.protobuf.Empty) returns (UserProfileView) {
option (google.api.http) = { option (google.api.http) = {
get: "/users/me/profile" get: "/users/me/profile"
}; };
@ -83,7 +83,7 @@ service AuthService {
}; };
} }
rpc GetMyUserEmail(google.protobuf.Empty) returns (UserEmail) { rpc GetMyUserEmail(google.protobuf.Empty) returns (UserEmailView) {
option (google.api.http) = { option (google.api.http) = {
get: "/users/me/email" get: "/users/me/email"
}; };
@ -126,7 +126,7 @@ service AuthService {
}; };
} }
rpc GetMyUserPhone(google.protobuf.Empty) returns (UserPhone) { rpc GetMyUserPhone(google.protobuf.Empty) returns (UserPhoneView) {
option (google.api.http) = { option (google.api.http) = {
get: "/users/me/phone" get: "/users/me/phone"
}; };
@ -169,7 +169,7 @@ service AuthService {
}; };
} }
rpc GetMyUserAddress(google.protobuf.Empty) returns (UserAddress) { rpc GetMyUserAddress(google.protobuf.Empty) returns (UserAddressView) {
option (google.api.http) = { option (google.api.http) = {
get: "/users/me/address" get: "/users/me/address"
}; };
@ -326,6 +326,8 @@ message User {
string street_address = 23; string street_address = 23;
bool password_change_required = 24; bool password_change_required = 24;
uint64 sequence = 25; uint64 sequence = 25;
repeated string login_names = 26;
string preferred_login_name = 27;
} }
enum UserState { enum UserState {
@ -359,6 +361,22 @@ message UserProfile {
google.protobuf.Timestamp change_date = 11; google.protobuf.Timestamp change_date = 11;
} }
message UserProfileView {
string id = 1;
string user_name = 2;
string first_name = 3;
string last_name = 4;
string nick_name = 5;
string display_name = 6;
string preferred_language = 7;
Gender gender = 8;
uint64 sequence = 9;
google.protobuf.Timestamp creation_date = 10;
google.protobuf.Timestamp change_date = 11;
repeated string login_names = 12;
string preferred_login_name = 13;
}
message UpdateUserProfileRequest { message UpdateUserProfileRequest {
string first_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; string first_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
string last_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; string last_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
@ -377,6 +395,15 @@ message UserEmail {
google.protobuf.Timestamp change_date = 6; google.protobuf.Timestamp change_date = 6;
} }
message UserEmailView {
string id = 1;
string email = 2;
bool isEmailVerified = 3;
uint64 sequence = 4;
google.protobuf.Timestamp creation_date = 5;
google.protobuf.Timestamp change_date = 6;
}
message VerifyMyUserEmailRequest { message VerifyMyUserEmailRequest {
string code = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; string code = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
} }
@ -399,6 +426,15 @@ message UserPhone {
google.protobuf.Timestamp change_date = 6; google.protobuf.Timestamp change_date = 6;
} }
message UserPhoneView {
string id = 1;
string phone = 2;
bool is_phone_verified = 3;
uint64 sequence = 4;
google.protobuf.Timestamp creation_date = 5;
google.protobuf.Timestamp change_date = 6;
}
message UpdateUserPhoneRequest { message UpdateUserPhoneRequest {
string phone = 1 [(validate.rules).string = {min_len: 1, max_len: 20}]; string phone = 1 [(validate.rules).string = {min_len: 1, max_len: 20}];
} }
@ -419,6 +455,18 @@ message UserAddress {
google.protobuf.Timestamp change_date = 9; google.protobuf.Timestamp change_date = 9;
} }
message UserAddressView {
string id = 1;
string country = 2;
string locality = 3;
string postal_code = 4;
string region = 5;
string street_address = 6;
uint64 sequence = 7;
google.protobuf.Timestamp creation_date = 8;
google.protobuf.Timestamp change_date = 9;
}
message UpdateUserAddressRequest { message UpdateUserAddressRequest {
string country = 1 [(validate.rules).string = {max_len: 200}]; string country = 1 [(validate.rules).string = {max_len: 200}];
string locality = 2 [(validate.rules).string = {max_len: 200}]; string locality = 2 [(validate.rules).string = {max_len: 200}];

View File

@ -3,7 +3,6 @@ package grpc
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/errors"
"github.com/golang/protobuf/ptypes/empty" "github.com/golang/protobuf/ptypes/empty"
) )
@ -38,9 +37,6 @@ func (s *Server) UpdateApplication(ctx context.Context, in *ApplicationUpdate) (
return appFromModel(app), nil return appFromModel(app), nil
} }
func (s *Server) DeactivateApplication(ctx context.Context, in *ApplicationID) (*Application, error) { func (s *Server) DeactivateApplication(ctx context.Context, in *ApplicationID) (*Application, error) {
if s.IsZitadel(ctx, in.ProjectId) {
return nil, errors.ThrowInvalidArgument(nil, "GRPC-LSped", "Zitadel Project Applications should not be deactivated")
}
app, err := s.project.DeactivateApplication(ctx, in.ProjectId, in.Id) app, err := s.project.DeactivateApplication(ctx, in.ProjectId, in.Id)
if err != nil { if err != nil {
return nil, err return nil, err
@ -56,17 +52,11 @@ func (s *Server) ReactivateApplication(ctx context.Context, in *ApplicationID) (
} }
func (s *Server) RemoveApplication(ctx context.Context, in *ApplicationID) (*empty.Empty, error) { func (s *Server) RemoveApplication(ctx context.Context, in *ApplicationID) (*empty.Empty, error) {
if s.IsZitadel(ctx, in.ProjectId) {
return nil, errors.ThrowInvalidArgument(nil, "GRPC-LSpee", "Zitadel Project Applications should not be removed")
}
err := s.project.RemoveApplication(ctx, in.ProjectId, in.Id) err := s.project.RemoveApplication(ctx, in.ProjectId, in.Id)
return &empty.Empty{}, err return &empty.Empty{}, err
} }
func (s *Server) UpdateApplicationOIDCConfig(ctx context.Context, in *OIDCConfigUpdate) (*OIDCConfig, error) { func (s *Server) UpdateApplicationOIDCConfig(ctx context.Context, in *OIDCConfigUpdate) (*OIDCConfig, error) {
if s.IsZitadel(ctx, in.ProjectId) {
return nil, errors.ThrowInvalidArgument(nil, "GRPC-LSpee", "Zitadel Project Applications OIdc Config should not be changed")
}
config, err := s.project.ChangeOIDCConfig(ctx, oidcConfigUpdateToModel(in)) config, err := s.project.ChangeOIDCConfig(ctx, oidcConfigUpdateToModel(in))
if err != nil { if err != nil {
return nil, err return nil, err
@ -75,9 +65,6 @@ func (s *Server) UpdateApplicationOIDCConfig(ctx context.Context, in *OIDCConfig
} }
func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, in *ApplicationID) (*ClientSecret, error) { func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, in *ApplicationID) (*ClientSecret, error) {
if s.IsZitadel(ctx, in.ProjectId) {
return nil, errors.ThrowInvalidArgument(nil, "GRPC-Lps4d", "Zitadel Project Applications OIdc Config should not be changed")
}
config, err := s.project.ChangeOIDConfigSecret(ctx, in.ProjectId, in.Id) config, err := s.project.ChangeOIDConfigSecret(ctx, in.ProjectId, in.Id)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -0,0 +1,14 @@
package grpc
import (
"context"
"github.com/golang/protobuf/ptypes/empty"
)
func (s *Server) GetIam(ctx context.Context, _ *empty.Empty) (*Iam, error) {
iam, err := s.iam.IamByID(ctx, s.systemDefaults.IamID)
if err != nil {
return nil, err
}
return iamFromModel(iam), nil
}

View File

@ -0,0 +1,14 @@
package grpc
import (
iam_model "github.com/caos/zitadel/internal/iam/model"
)
func iamFromModel(iam *iam_model.Iam) *Iam {
return &Iam{
IamProjectId: iam.IamProjectID,
GlobalOrgId: iam.GlobalOrgID,
SetUpDone: iam.SetUpDone,
SetUpStarted: iam.SetUpStarted,
}
}

View File

@ -15,6 +15,11 @@ import (
var ManagementService_AuthMethods = utils_auth.MethodMapping{ var ManagementService_AuthMethods = utils_auth.MethodMapping{
"/caos.zitadel.management.api.v1.ManagementService/GetIam": utils_auth.Option{
Permission: "authenticated",
CheckParam: "",
},
"/caos.zitadel.management.api.v1.ManagementService/GetUserByID": utils_auth.Option{ "/caos.zitadel.management.api.v1.ManagementService/GetUserByID": utils_auth.Option{
Permission: "user.read", Permission: "user.read",
CheckParam: "", CheckParam: "",

File diff suppressed because it is too large Load Diff

View File

@ -56,6 +56,15 @@ func request_ManagementService_Validate_0(ctx context.Context, marshaler runtime
} }
func request_ManagementService_GetIam_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq empty.Empty
var metadata runtime.ServerMetadata
msg, err := client.GetIam(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ManagementService_GetUserByID_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func request_ManagementService_GetUserByID_0(ctx context.Context, marshaler runtime.Marshaler, client ManagementServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UserID var protoReq UserID
var metadata runtime.ServerMetadata var metadata runtime.ServerMetadata
@ -3760,6 +3769,26 @@ func RegisterManagementServiceHandlerClient(ctx context.Context, mux *runtime.Se
}) })
mux.Handle("GET", pattern_ManagementService_GetIam_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_ManagementService_GetIam_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_ManagementService_GetIam_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_ManagementService_GetUserByID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle("GET", pattern_ManagementService_GetUserByID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
@ -5950,6 +5979,8 @@ var (
pattern_ManagementService_Validate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"validate"}, "")) pattern_ManagementService_Validate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"validate"}, ""))
pattern_ManagementService_GetIam_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"iam"}, ""))
pattern_ManagementService_GetUserByID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"users", "id"}, "")) pattern_ManagementService_GetUserByID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"users", "id"}, ""))
pattern_ManagementService_GetUserByEmailGlobal_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 2}, []string{"global", "users", "email"}, "")) pattern_ManagementService_GetUserByEmailGlobal_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 2}, []string{"global", "users", "email"}, ""))
@ -6176,6 +6207,8 @@ var (
forward_ManagementService_Validate_0 = runtime.ForwardResponseMessage forward_ManagementService_Validate_0 = runtime.ForwardResponseMessage
forward_ManagementService_GetIam_0 = runtime.ForwardResponseMessage
forward_ManagementService_GetUserByID_0 = runtime.ForwardResponseMessage forward_ManagementService_GetUserByID_0 = runtime.ForwardResponseMessage
forward_ManagementService_GetUserByEmailGlobal_0 = runtime.ForwardResponseMessage forward_ManagementService_GetUserByEmailGlobal_0 = runtime.ForwardResponseMessage

View File

@ -209,6 +209,23 @@
] ]
} }
}, },
"/iam": {
"get": {
"summary": "IAM",
"operationId": "GetIam",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1Iam"
}
}
},
"tags": [
"ManagementService"
]
}
},
"/orgs/me/domains": { "/orgs/me/domains": {
"post": { "post": {
"operationId": "AddMyOrgDomain", "operationId": "AddMyOrgDomain",
@ -2663,7 +2680,7 @@
"200": { "200": {
"description": "A successful response.", "description": "A successful response.",
"schema": { "schema": {
"$ref": "#/definitions/v1User" "$ref": "#/definitions/v1UserView"
} }
} }
}, },
@ -2904,7 +2921,7 @@
"200": { "200": {
"description": "A successful response.", "description": "A successful response.",
"schema": { "schema": {
"$ref": "#/definitions/v1UserAddress" "$ref": "#/definitions/v1UserAddressView"
} }
} }
}, },
@ -3003,7 +3020,7 @@
"200": { "200": {
"description": "A successful response.", "description": "A successful response.",
"schema": { "schema": {
"$ref": "#/definitions/v1UserEmail" "$ref": "#/definitions/v1UserEmailView"
} }
} }
}, },
@ -3115,7 +3132,7 @@
"200": { "200": {
"description": "A successful response.", "description": "A successful response.",
"schema": { "schema": {
"$ref": "#/definitions/v1UserPhone" "$ref": "#/definitions/v1UserPhoneView"
} }
} }
}, },
@ -3202,7 +3219,7 @@
"200": { "200": {
"description": "A successful response.", "description": "A successful response.",
"schema": { "schema": {
"$ref": "#/definitions/v1UserProfile" "$ref": "#/definitions/v1UserProfileView"
} }
} }
}, },
@ -3959,6 +3976,25 @@
} }
} }
}, },
"v1Iam": {
"type": "object",
"properties": {
"global_org_id": {
"type": "string"
},
"iam_project_id": {
"type": "string"
},
"set_up_done": {
"type": "boolean",
"format": "boolean"
},
"set_up_started": {
"type": "boolean",
"format": "boolean"
}
}
},
"v1MFAState": { "v1MFAState": {
"type": "string", "type": "string",
"enum": [ "enum": [
@ -5960,6 +5996,41 @@
} }
} }
}, },
"v1UserAddressView": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"country": {
"type": "string"
},
"locality": {
"type": "string"
},
"postal_code": {
"type": "string"
},
"region": {
"type": "string"
},
"street_address": {
"type": "string"
},
"sequence": {
"type": "string",
"format": "uint64"
},
"creation_date": {
"type": "string",
"format": "date-time"
},
"change_date": {
"type": "string",
"format": "date-time"
}
}
},
"v1UserEmail": { "v1UserEmail": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -5987,6 +6058,33 @@
} }
} }
}, },
"v1UserEmailView": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"email": {
"type": "string"
},
"is_email_verified": {
"type": "boolean",
"format": "boolean"
},
"sequence": {
"type": "string",
"format": "uint64"
},
"creation_date": {
"type": "string",
"format": "date-time"
},
"change_date": {
"type": "string",
"format": "date-time"
}
}
},
"v1UserGrant": { "v1UserGrant": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -6242,6 +6340,33 @@
} }
} }
}, },
"v1UserPhoneView": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"phone": {
"type": "string"
},
"is_phone_verified": {
"type": "boolean",
"format": "boolean"
},
"sequence": {
"type": "string",
"format": "uint64"
},
"creation_date": {
"type": "string",
"format": "date-time"
},
"change_date": {
"type": "string",
"format": "date-time"
}
}
},
"v1UserProfile": { "v1UserProfile": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -6283,6 +6408,56 @@
} }
} }
}, },
"v1UserProfileView": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"nick_name": {
"type": "string"
},
"display_name": {
"type": "string"
},
"preferred_language": {
"type": "string"
},
"gender": {
"$ref": "#/definitions/v1Gender"
},
"user_name": {
"type": "string"
},
"sequence": {
"type": "string",
"format": "uint64"
},
"creation_date": {
"type": "string",
"format": "date-time"
},
"change_date": {
"type": "string",
"format": "date-time"
},
"login_names": {
"type": "array",
"items": {
"type": "string"
}
},
"preferred_login_name": {
"type": "string"
}
}
},
"v1UserSearchKey": { "v1UserSearchKey": {
"type": "string", "type": "string",
"enum": [ "enum": [
@ -6454,6 +6629,15 @@
}, },
"resource_owner": { "resource_owner": {
"type": "string" "type": "string"
},
"login_names": {
"type": "array",
"items": {
"type": "string"
}
},
"preferred_login_name": {
"type": "string"
} }
} }
} }

View File

@ -6,7 +6,6 @@ import (
"github.com/caos/zitadel/internal/api" "github.com/caos/zitadel/internal/api"
"github.com/caos/zitadel/internal/api/auth" "github.com/caos/zitadel/internal/api/auth"
grpc_util "github.com/caos/zitadel/internal/api/grpc" grpc_util "github.com/caos/zitadel/internal/api/grpc"
"github.com/caos/zitadel/internal/errors"
"github.com/golang/protobuf/ptypes/empty" "github.com/golang/protobuf/ptypes/empty"
) )
@ -18,9 +17,6 @@ func (s *Server) CreateProject(ctx context.Context, in *ProjectCreateRequest) (*
return projectFromModel(project), nil return projectFromModel(project), nil
} }
func (s *Server) UpdateProject(ctx context.Context, in *ProjectUpdateRequest) (*Project, error) { func (s *Server) UpdateProject(ctx context.Context, in *ProjectUpdateRequest) (*Project, error) {
if s.IsZitadel(ctx, in.Id) {
return nil, errors.ThrowInvalidArgument(nil, "GRPC-SFH8d", "Zitadel Project should not be updated")
}
project, err := s.project.UpdateProject(ctx, projectUpdateToModel(in)) project, err := s.project.UpdateProject(ctx, projectUpdateToModel(in))
if err != nil { if err != nil {
return nil, err return nil, err
@ -28,9 +24,6 @@ func (s *Server) UpdateProject(ctx context.Context, in *ProjectUpdateRequest) (*
return projectFromModel(project), nil return projectFromModel(project), nil
} }
func (s *Server) DeactivateProject(ctx context.Context, in *ProjectID) (*Project, error) { func (s *Server) DeactivateProject(ctx context.Context, in *ProjectID) (*Project, error) {
if s.IsZitadel(ctx, in.Id) {
return nil, errors.ThrowInvalidArgument(nil, "GRPC-PS9cs", "Zitadel Project should not be deactivated")
}
project, err := s.project.DeactivateProject(ctx, in.Id) project, err := s.project.DeactivateProject(ctx, in.Id)
if err != nil { if err != nil {
return nil, err return nil, err
@ -38,9 +31,6 @@ func (s *Server) DeactivateProject(ctx context.Context, in *ProjectID) (*Project
return projectFromModel(project), nil return projectFromModel(project), nil
} }
func (s *Server) ReactivateProject(ctx context.Context, in *ProjectID) (*Project, error) { func (s *Server) ReactivateProject(ctx context.Context, in *ProjectID) (*Project, error) {
if s.IsZitadel(ctx, in.Id) {
return nil, errors.ThrowInvalidArgument(nil, "GRPC-LSpe2", "Zitadel Project should not be reactivated")
}
project, err := s.project.ReactivateProject(ctx, in.Id) project, err := s.project.ReactivateProject(ctx, in.Id)
if err != nil { if err != nil {
return nil, err return nil, err
@ -85,9 +75,6 @@ func (s *Server) GetGrantedProjectByID(ctx context.Context, in *ProjectGrantID)
} }
func (s *Server) AddProjectRole(ctx context.Context, in *ProjectRoleAdd) (*ProjectRole, error) { func (s *Server) AddProjectRole(ctx context.Context, in *ProjectRoleAdd) (*ProjectRole, error) {
if s.IsZitadel(ctx, in.Id) {
return nil, errors.ThrowInvalidArgument(nil, "GRPC-PS9cs", "Zitadel Project should not get new role")
}
role, err := s.project.AddProjectRole(ctx, projectRoleAddToModel(in)) role, err := s.project.AddProjectRole(ctx, projectRoleAddToModel(in))
if err != nil { if err != nil {
return nil, err return nil, err
@ -95,9 +82,6 @@ func (s *Server) AddProjectRole(ctx context.Context, in *ProjectRoleAdd) (*Proje
return projectRoleFromModel(role), nil return projectRoleFromModel(role), nil
} }
func (s *Server) ChangeProjectRole(ctx context.Context, in *ProjectRoleChange) (*ProjectRole, error) { func (s *Server) ChangeProjectRole(ctx context.Context, in *ProjectRoleChange) (*ProjectRole, error) {
if s.IsZitadel(ctx, in.Id) {
return nil, errors.ThrowInvalidArgument(nil, "GRPC-LASj8", "Zitadel Project should not change roles")
}
role, err := s.project.ChangeProjectRole(ctx, projectRoleChangeToModel(in)) role, err := s.project.ChangeProjectRole(ctx, projectRoleChangeToModel(in))
if err != nil { if err != nil {
return nil, err return nil, err
@ -106,9 +90,6 @@ func (s *Server) ChangeProjectRole(ctx context.Context, in *ProjectRoleChange) (
} }
func (s *Server) RemoveProjectRole(ctx context.Context, in *ProjectRoleRemove) (*empty.Empty, error) { func (s *Server) RemoveProjectRole(ctx context.Context, in *ProjectRoleRemove) (*empty.Empty, error) {
if s.IsZitadel(ctx, in.Id) {
return nil, errors.ThrowInvalidArgument(nil, "GRPC-Psn7s", "do not remove roles from Zitadel Project")
}
err := s.project.RemoveProjectRole(ctx, in.Id, in.Key) err := s.project.RemoveProjectRole(ctx, in.Id, in.Key)
return &empty.Empty{}, err return &empty.Empty{}, err
} }
@ -131,14 +112,3 @@ func (s *Server) ProjectChanges(ctx context.Context, changesRequest *ChangeReque
} }
return projectChangesToResponse(response, changesRequest.GetSequenceOffset(), changesRequest.GetLimit()), nil return projectChangesToResponse(response, changesRequest.GetSequenceOffset(), changesRequest.GetLimit()), nil
} }
func (s *Server) IsZitadel(ctx context.Context, projectID string) bool {
iam, err := s.iam.IamByID(ctx, s.systemDefaults.IamID)
if err != nil {
return false
}
if iam.IamProjectID == projectID {
return true
}
return false
}

View File

@ -9,12 +9,12 @@ import (
"github.com/golang/protobuf/ptypes/empty" "github.com/golang/protobuf/ptypes/empty"
) )
func (s *Server) GetUserByID(ctx context.Context, id *UserID) (*User, error) { func (s *Server) GetUserByID(ctx context.Context, id *UserID) (*UserView, error) {
user, err := s.user.UserByID(ctx, id.Id) user, err := s.user.UserByID(ctx, id.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return userFromModel(user), nil return userViewFromModel(user), nil
} }
func (s *Server) GetUserByEmailGlobal(ctx context.Context, email *UserEmailID) (*UserView, error) { func (s *Server) GetUserByEmailGlobal(ctx context.Context, email *UserEmailID) (*UserView, error) {
@ -96,12 +96,12 @@ func (s *Server) DeleteUser(ctx context.Context, in *UserID) (*empty.Empty, erro
return nil, errors.ThrowUnimplemented(nil, "GRPC-as4fg", "Not implemented") return nil, errors.ThrowUnimplemented(nil, "GRPC-as4fg", "Not implemented")
} }
func (s *Server) GetUserProfile(ctx context.Context, in *UserID) (*UserProfile, error) { func (s *Server) GetUserProfile(ctx context.Context, in *UserID) (*UserProfileView, error) {
profile, err := s.user.ProfileByID(ctx, in.Id) profile, err := s.user.ProfileByID(ctx, in.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return profileFromModel(profile), nil return profileViewFromModel(profile), nil
} }
func (s *Server) UpdateUserProfile(ctx context.Context, request *UpdateUserProfileRequest) (*UserProfile, error) { func (s *Server) UpdateUserProfile(ctx context.Context, request *UpdateUserProfileRequest) (*UserProfile, error) {
@ -112,12 +112,12 @@ func (s *Server) UpdateUserProfile(ctx context.Context, request *UpdateUserProfi
return profileFromModel(profile), nil return profileFromModel(profile), nil
} }
func (s *Server) GetUserEmail(ctx context.Context, in *UserID) (*UserEmail, error) { func (s *Server) GetUserEmail(ctx context.Context, in *UserID) (*UserEmailView, error) {
email, err := s.user.EmailByID(ctx, in.Id) email, err := s.user.EmailByID(ctx, in.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return emailFromModel(email), nil return emailViewFromModel(email), nil
} }
func (s *Server) ChangeUserEmail(ctx context.Context, request *UpdateUserEmailRequest) (*UserEmail, error) { func (s *Server) ChangeUserEmail(ctx context.Context, request *UpdateUserEmailRequest) (*UserEmail, error) {
@ -133,12 +133,12 @@ func (s *Server) ResendEmailVerificationMail(ctx context.Context, in *UserID) (*
return &empty.Empty{}, err return &empty.Empty{}, err
} }
func (s *Server) GetUserPhone(ctx context.Context, in *UserID) (*UserPhone, error) { func (s *Server) GetUserPhone(ctx context.Context, in *UserID) (*UserPhoneView, error) {
phone, err := s.user.PhoneByID(ctx, in.Id) phone, err := s.user.PhoneByID(ctx, in.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return phoneFromModel(phone), nil return phoneViewFromModel(phone), nil
} }
func (s *Server) ChangeUserPhone(ctx context.Context, request *UpdateUserPhoneRequest) (*UserPhone, error) { func (s *Server) ChangeUserPhone(ctx context.Context, request *UpdateUserPhoneRequest) (*UserPhone, error) {
@ -154,12 +154,12 @@ func (s *Server) ResendPhoneVerificationCode(ctx context.Context, in *UserID) (*
return &empty.Empty{}, err return &empty.Empty{}, err
} }
func (s *Server) GetUserAddress(ctx context.Context, in *UserID) (*UserAddress, error) { func (s *Server) GetUserAddress(ctx context.Context, in *UserID) (*UserAddressView, error) {
address, err := s.user.AddressByID(ctx, in.Id) address, err := s.user.AddressByID(ctx, in.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return addressFromModel(address), nil return addressViewFromModel(address), nil
} }
func (s *Server) UpdateUserAddress(ctx context.Context, request *UpdateUserAddressRequest) (*UserAddress, error) { func (s *Server) UpdateUserAddress(ctx context.Context, request *UpdateUserAddressRequest) (*UserAddress, error) {

View File

@ -160,6 +160,30 @@ func profileFromModel(profile *usr_model.Profile) *UserProfile {
} }
} }
func profileViewFromModel(profile *usr_model.Profile) *UserProfileView {
creationDate, err := ptypes.TimestampProto(profile.CreationDate)
logging.Log("GRPC-sk8sk").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(profile.ChangeDate)
logging.Log("GRPC-s30Ks'").OnError(err).Debug("unable to parse timestamp")
return &UserProfileView{
Id: profile.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: profile.Sequence,
UserName: profile.UserName,
FirstName: profile.FirstName,
LastName: profile.LastName,
DisplayName: profile.DisplayName,
NickName: profile.NickName,
PreferredLanguage: profile.PreferredLanguage.String(),
Gender: genderFromModel(profile.Gender),
LoginNames: profile.LoginNames,
PreferredLoginName: profile.PreferredLoginName,
}
}
func updateProfileToModel(u *UpdateUserProfileRequest) *usr_model.Profile { func updateProfileToModel(u *UpdateUserProfileRequest) *usr_model.Profile {
preferredLanguage, err := language.Parse(u.PreferredLanguage) preferredLanguage, err := language.Parse(u.PreferredLanguage)
logging.Log("GRPC-d8k2s").OnError(err).Debug("language malformed") logging.Log("GRPC-d8k2s").OnError(err).Debug("language malformed")
@ -192,6 +216,23 @@ func emailFromModel(email *usr_model.Email) *UserEmail {
} }
} }
func emailViewFromModel(email *usr_model.Email) *UserEmailView {
creationDate, err := ptypes.TimestampProto(email.CreationDate)
logging.Log("GRPC-sKefs").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(email.ChangeDate)
logging.Log("GRPC-0isjD").OnError(err).Debug("unable to parse timestamp")
return &UserEmailView{
Id: email.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: email.Sequence,
Email: email.EmailAddress,
IsEmailVerified: email.IsEmailVerified,
}
}
func updateEmailToModel(e *UpdateUserEmailRequest) *usr_model.Email { func updateEmailToModel(e *UpdateUserEmailRequest) *usr_model.Email {
return &usr_model.Email{ return &usr_model.Email{
ObjectRoot: models.ObjectRoot{AggregateID: e.Id}, ObjectRoot: models.ObjectRoot{AggregateID: e.Id},
@ -217,6 +258,22 @@ func phoneFromModel(phone *usr_model.Phone) *UserPhone {
} }
} }
func phoneViewFromModel(phone *usr_model.Phone) *UserPhoneView {
creationDate, err := ptypes.TimestampProto(phone.CreationDate)
logging.Log("GRPC-6gSj").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(phone.ChangeDate)
logging.Log("GRPC-lKs8f").OnError(err).Debug("unable to parse timestamp")
return &UserPhoneView{
Id: phone.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: phone.Sequence,
Phone: phone.PhoneNumber,
IsPhoneVerified: phone.IsPhoneVerified,
}
}
func updatePhoneToModel(e *UpdateUserPhoneRequest) *usr_model.Phone { func updatePhoneToModel(e *UpdateUserPhoneRequest) *usr_model.Phone {
return &usr_model.Phone{ return &usr_model.Phone{
ObjectRoot: models.ObjectRoot{AggregateID: e.Id}, ObjectRoot: models.ObjectRoot{AggregateID: e.Id},
@ -245,6 +302,26 @@ func addressFromModel(address *usr_model.Address) *UserAddress {
} }
} }
func addressViewFromModel(address *usr_model.Address) *UserAddressView {
creationDate, err := ptypes.TimestampProto(address.CreationDate)
logging.Log("GRPC-67stC").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(address.ChangeDate)
logging.Log("GRPC-0jSfs").OnError(err).Debug("unable to parse timestamp")
return &UserAddressView{
Id: address.AggregateID,
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: address.Sequence,
Country: address.Country,
StreetAddress: address.StreetAddress,
Region: address.Region,
PostalCode: address.PostalCode,
Locality: address.Locality,
}
}
func updateAddressToModel(address *UpdateUserAddressRequest) *usr_model.Address { func updateAddressToModel(address *UpdateUserAddressRequest) *usr_model.Address {
return &usr_model.Address{ return &usr_model.Address{
ObjectRoot: models.ObjectRoot{AggregateID: address.Id}, ObjectRoot: models.ObjectRoot{AggregateID: address.Id},
@ -287,27 +364,29 @@ func userViewFromModel(user *usr_model.UserView) *UserView {
logging.Log("GRPC-dl9ws").OnError(err).Debug("unable to parse timestamp") logging.Log("GRPC-dl9ws").OnError(err).Debug("unable to parse timestamp")
return &UserView{ return &UserView{
Id: user.ID, Id: user.ID,
State: userStateFromModel(user.State), State: userStateFromModel(user.State),
CreationDate: creationDate, CreationDate: creationDate,
ChangeDate: changeDate, ChangeDate: changeDate,
LastLogin: lastLogin, LastLogin: lastLogin,
PasswordChanged: passwordChanged, PasswordChanged: passwordChanged,
Sequence: user.Sequence, Sequence: user.Sequence,
ResourceOwner: user.ResourceOwner, ResourceOwner: user.ResourceOwner,
UserName: user.UserName, UserName: user.UserName,
FirstName: user.FirstName, FirstName: user.FirstName,
LastName: user.LastName, LastName: user.LastName,
NickName: user.NickName, NickName: user.NickName,
Email: user.Email, Email: user.Email,
IsEmailVerified: user.IsEmailVerified, IsEmailVerified: user.IsEmailVerified,
Phone: user.Phone, Phone: user.Phone,
IsPhoneVerified: user.IsPhoneVerified, IsPhoneVerified: user.IsPhoneVerified,
Country: user.Country, Country: user.Country,
Locality: user.Locality, Locality: user.Locality,
Region: user.Region, Region: user.Region,
PostalCode: user.PostalCode, PostalCode: user.PostalCode,
StreetAddress: user.StreetAddress, StreetAddress: user.StreetAddress,
LoginNames: user.LoginNames,
PreferredLoginName: user.PreferredLoginName,
} }
} }

View File

@ -51,8 +51,19 @@ service ManagementService {
}; };
} }
//IAM
rpc GetIam(google.protobuf.Empty) returns (Iam) {
option (google.api.http) = {
get: "/iam"
};
option (caos.zitadel.utils.v1.auth_option) = {
permission: "authenticated"
};
}
//USER //USER
rpc GetUserByID(UserID) returns (User) { rpc GetUserByID(UserID) returns (UserView) {
option (google.api.http) = { option (google.api.http) = {
get: "/users/{id}" get: "/users/{id}"
}; };
@ -199,7 +210,7 @@ service ManagementService {
} }
//USER_PROFILE //USER_PROFILE
rpc GetUserProfile(UserID) returns (UserProfile) { rpc GetUserProfile(UserID) returns (UserProfileView) {
option (google.api.http) = { option (google.api.http) = {
get: "/users/{id}/profile" get: "/users/{id}/profile"
}; };
@ -221,7 +232,7 @@ service ManagementService {
} }
//USER_EMAIL //USER_EMAIL
rpc GetUserEmail(UserID) returns (UserEmail) { rpc GetUserEmail(UserID) returns (UserEmailView) {
option (google.api.http) = { option (google.api.http) = {
get: "/users/{id}/email" get: "/users/{id}/email"
}; };
@ -254,7 +265,7 @@ service ManagementService {
} }
//USER_PHONE //USER_PHONE
rpc GetUserPhone(UserID) returns (UserPhone) { rpc GetUserPhone(UserID) returns (UserPhoneView) {
option (google.api.http) = { option (google.api.http) = {
get: "/users/{id}/phone" get: "/users/{id}/phone"
}; };
@ -287,7 +298,7 @@ service ManagementService {
} }
//USER_ADDRESS //USER_ADDRESS
rpc GetUserAddress(UserID) returns (UserAddress) { rpc GetUserAddress(UserID) returns (UserAddressView) {
option (google.api.http) = { option (google.api.http) = {
get: "/users/{id}/address" get: "/users/{id}/address"
}; };
@ -1266,6 +1277,13 @@ service ManagementService {
} }
} }
message Iam {
string global_org_id = 1;
string iam_project_id = 2;
bool set_up_done = 3;
bool set_up_started = 4;
}
message ChangeRequest { message ChangeRequest {
string id = 1; string id = 1;
string sec_id = 2; string sec_id = 2;
@ -1399,6 +1417,8 @@ message UserView {
string street_address = 22; string street_address = 22;
uint64 sequence = 23; uint64 sequence = 23;
string resource_owner = 24; string resource_owner = 24;
repeated string login_names = 25;
string preferred_login_name = 27;
} }
message UserSearchRequest { message UserSearchRequest {
@ -1456,6 +1476,22 @@ message UserProfile {
google.protobuf.Timestamp change_date = 11; google.protobuf.Timestamp change_date = 11;
} }
message UserProfileView {
string id = 1;
string first_name = 2;
string last_name = 3;
string nick_name = 4;
string display_name = 5;
string preferred_language = 6;
Gender gender = 7;
string user_name = 8;
uint64 sequence = 9;
google.protobuf.Timestamp creation_date = 10;
google.protobuf.Timestamp change_date = 11;
repeated string login_names = 12;
string preferred_login_name = 27;
}
message UpdateUserProfileRequest { message UpdateUserProfileRequest {
string id = 1; string id = 1;
string first_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; string first_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
@ -1475,6 +1511,15 @@ message UserEmail {
google.protobuf.Timestamp change_date = 6; google.protobuf.Timestamp change_date = 6;
} }
message UserEmailView {
string id = 1;
string email = 2;
bool is_email_verified = 3;
uint64 sequence = 4;
google.protobuf.Timestamp creation_date = 5;
google.protobuf.Timestamp change_date = 6;
}
message UpdateUserEmailRequest { message UpdateUserEmailRequest {
string id = 1; string id = 1;
string email = 2 [(validate.rules).string = {min_len: 1, max_len: 200}]; string email = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
@ -1490,6 +1535,15 @@ message UserPhone {
google.protobuf.Timestamp change_date = 7; google.protobuf.Timestamp change_date = 7;
} }
message UserPhoneView {
string id = 1;
string phone = 2;
bool is_phone_verified = 3;
uint64 sequence = 5;
google.protobuf.Timestamp creation_date = 6;
google.protobuf.Timestamp change_date = 7;
}
message UpdateUserPhoneRequest { message UpdateUserPhoneRequest {
string id = 1; string id = 1;
string phone = 2 [(validate.rules).string = {min_len: 1, max_len: 20}]; string phone = 2 [(validate.rules).string = {min_len: 1, max_len: 20}];
@ -1508,6 +1562,18 @@ message UserAddress {
google.protobuf.Timestamp change_date = 9; google.protobuf.Timestamp change_date = 9;
} }
message UserAddressView {
string id = 1;
string country = 2;
string locality = 3;
string postal_code = 4;
string region = 5;
string street_address = 6;
uint64 sequence = 7;
google.protobuf.Timestamp creation_date = 8;
google.protobuf.Timestamp change_date = 9;
}
message UpdateUserAddressRequest { message UpdateUserAddressRequest {
string id = 1; string id = 1;
string country = 2 [(validate.rules).string = {max_len: 200}]; string country = 2 [(validate.rules).string = {max_len: 200}];