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 (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/sdk"
org_model "github.com/caos/zitadel/internal/org/model"
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing"
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/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) {
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) {
@ -74,7 +80,11 @@ func (repo *UserRepo) ChangeMyProfile(ctx context.Context, profile *model.Profil
}
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) {
@ -101,7 +111,11 @@ func (repo *UserRepo) ResendMyEmailVerificationMail(ctx context.Context) 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) {
@ -120,7 +134,11 @@ func (repo *UserRepo) ResendMyPhoneVerificationCode(ctx context.Context) 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) {
@ -205,8 +223,23 @@ func (repo *UserRepo) SignOut(ctx context.Context, agentID, userID string) error
return repo.UserEvents.SignOut(ctx, agentID, userID)
}
func (repo *UserRepo) UserByID(ctx context.Context, userID string) (*model.User, error) {
return repo.UserEvents.UserByID(ctx, userID)
func (repo *UserRepo) UserByID(ctx context.Context, id string) (*model.UserView, error) {
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 {

View File

@ -101,23 +101,11 @@ func (u *User) fillLoginNames(user *view_model.UserView) (err error) {
if err != nil {
return err
}
user.LoginNames = getLoginNames(policy, user.UserName, org.Domains)
user.SetLoginNames(policy, org.Domains)
user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain)
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) {
switch event.Type {
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.OrgIamPolicyRemoved:
return u.fillLoginNamesOnOrgUsers(event)
case org_es_model.OrgDomainPrimarySet:
return u.fillPreferredLoginNamesOnOrgUsers(event)
default:
return u.view.ProcessedUserSequence(event.Sequence)
}
@ -134,6 +124,7 @@ func (u *User) ProcessOrg(event *models.Event) (err error) {
}
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 {
@ -148,7 +139,7 @@ func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error {
return err
}
for _, user := range users {
user.LoginNames = getLoginNames(policy, user.UserName, org.Domains)
user.SetLoginNames(policy, org.Domains)
err := u.view.PutUser(user, event.Sequence)
if err != nil {
return err
@ -157,6 +148,32 @@ func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error {
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 {
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)

View File

@ -25,7 +25,7 @@ type UserRepository interface {
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 {

View File

@ -2,6 +2,7 @@ package eventstore
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/api/auth"
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
@ -20,8 +21,23 @@ type UserRepo struct {
View *view.View
}
func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_model.User, err error) {
return repo.UserEvents.UserByID(ctx, id)
func (repo *UserRepo) UserByID(ctx context.Context, id string) (*usr_model.UserView, error) {
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) {
@ -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) {
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) {
@ -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) {
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) {
@ -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) {
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) {
@ -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) {
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) {

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},
&ProjectGrantMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectGrantMember"), errorCount}, userEvents: repos.UserEvents},
&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},
&Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}},
&OrgMember{handler: handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount}, userEvents: repos.UserEvents},

View File

@ -1,6 +1,11 @@
package handler
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"
"time"
@ -9,13 +14,13 @@ import (
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/models"
"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"
)
type User struct {
handler
eventstore eventstore.Eventstore
orgEvents *org_events.OrgEventstore
}
const (
@ -33,15 +38,29 @@ func (p *User) EventQuery() (*models.SearchQuery, error) {
if err != nil {
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)
switch event.Type {
case es_model.UserAdded,
es_model.UserRegistered:
user.AppendEvent(event)
p.fillLoginNames(user)
case es_model.UserProfileChanged,
es_model.UserEmailChanged,
es_model.UserEmailVerified,
@ -68,7 +87,89 @@ func (p *User) Process(event *models.Event) (err error) {
if err != nil {
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 {

View File

@ -23,6 +23,10 @@ func (v *View) GetGlobalUserByEmail(email string) (*model.UserView, error) {
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) {
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)
}
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)
if err != nil {
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 {

View File

@ -7,7 +7,7 @@ import (
)
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)
RegisterUser(ctx context.Context, user *model.User, resourceOwner string) (*model.User, error)
DeactivateUser(ctx context.Context, id string) (*model.User, error)

View File

@ -2,8 +2,8 @@ package model
import (
es_models "github.com/caos/zitadel/internal/eventstore/models"
"strings"
"github.com/golang/protobuf/ptypes/timestamp"
"strings"
)
type Org struct {
@ -57,6 +57,15 @@ func (o *Org) ContainsDomain(domain *OrgDomain) bool {
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 {
for _, member := range o.Members {
if member.UserID == userID {

View File

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

View File

@ -1,6 +1,8 @@
package model
import (
"github.com/caos/zitadel/internal/eventstore/models"
"golang.org/x/text/language"
"time"
req_model "github.com/caos/zitadel/internal/auth_request/model"
@ -18,6 +20,7 @@ type UserView struct {
PasswordChanged time.Time
LastLogin time.Time
UserName string
PreferredLoginName string
LoginNames []string
FirstName string
LastName string
@ -121,3 +124,69 @@ func (u *UserView) MfaTypesAllowed(level req_model.MfaLevel) []req_model.MfaType
}
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) {
err := user.CheckOrgIamPolicy(orgIamPolicy)
if err != nil {
return nil, nil, err
}
if !user.IsValid() {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "User is invalid")
}

View File

@ -2,6 +2,7 @@ package model
import (
"encoding/json"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/lib/pq"
"time"
@ -39,6 +40,7 @@ type UserView struct {
LastLogin time.Time `json:"-" gorm:"column:last_login"`
UserName string `json:"userName" gorm:"column:user_name"`
LoginNames pq.StringArray `json:"-" gorm:"column:login_names"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
FirstName string `json:"firstName" gorm:"column:first_name"`
LastName string `json:"lastName" gorm:"column:last_name"`
NickName string `json:"nickName" gorm:"column:nick_name"`
@ -74,6 +76,7 @@ func UserFromModel(user *model.UserView) *UserView {
LastLogin: user.LastLogin,
UserName: user.UserName,
LoginNames: user.LoginNames,
PreferredLoginName: user.PreferredLoginName,
FirstName: user.FirstName,
LastName: user.LastName,
NickName: user.NickName,
@ -108,6 +111,8 @@ func UserToModel(user *UserView) *model.UserView {
PasswordChangeRequired: user.PasswordChangeRequired,
PasswordChanged: user.PasswordChanged,
LastLogin: user.LastLogin,
PreferredLoginName: user.PreferredLoginName,
LoginNames: user.LoginNames,
UserName: user.UserName,
FirstName: user.FirstName,
LastName: user.LastName,
@ -140,6 +145,24 @@ func UsersToModel(users []*UserView) []*model.UserView {
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) {
u.ChangeDate = event.CreationDate
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": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1UserAddress"
"$ref": "#/definitions/v1UserAddressView"
}
}
},
@ -185,7 +185,7 @@
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1UserEmail"
"$ref": "#/definitions/v1UserEmailView"
}
}
},
@ -387,7 +387,7 @@
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1UserPhone"
"$ref": "#/definitions/v1UserPhoneView"
}
}
},
@ -480,7 +480,7 @@
"200": {
"description": "A successful response.",
"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": {
"type": "object",
"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": {
"type": "string",
"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": {
"type": "object",
"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": {
"type": "string",
"enum": [

View File

@ -8,36 +8,36 @@ import (
"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)
if err != nil {
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)
if err != nil {
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)
if err != nil {
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)
if err != nil {
return nil, err
}
return addressFromModel(address), nil
return addressViewFromModel(address), nil
}
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 {
preferredLanguage, err := language.Parse(u.PreferredLanguage)
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 {
return &usr_model.Email{
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 {
return &usr_model.Phone{
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 {
return &usr_model.Address{
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 {
switch scope {
case scopeOpenID:
userInfo.Subject = user.AggregateID
userInfo.Subject = user.ID
case scopeEmail:
if user.Email == nil {
continue
}
userInfo.Email = user.EmailAddress
userInfo.Email = user.Email
userInfo.EmailVerified = user.IsEmailVerified
case scopeProfile:
if user.Profile == nil {
continue
}
userInfo.Name = user.FirstName + " " + user.LastName
userInfo.Name = user.DisplayName
userInfo.FamilyName = user.LastName
userInfo.GivenName = user.FirstName
userInfo.Nickname = user.NickName
@ -65,15 +59,9 @@ func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID string, sc
userInfo.UpdatedAt = user.ChangeDate
userInfo.Gender = oidc.Gender(getGender(user.Gender))
case scopePhone:
if user.Phone == nil {
continue
}
userInfo.PhoneNumber = user.PhoneNumber
userInfo.PhoneNumber = user.Phone
userInfo.PhoneNumberVerified = user.IsPhoneVerified
case scopeAddress:
if user.Address == nil {
continue
}
userInfo.Address.StreetAddress = user.StreetAddress
userInfo.Address.Locality = user.Locality
userInfo.Address.Region = user.Region

View File

@ -62,7 +62,7 @@ service AuthService {
}
//User
rpc GetMyUserProfile(google.protobuf.Empty) returns (UserProfile) {
rpc GetMyUserProfile(google.protobuf.Empty) returns (UserProfileView) {
option (google.api.http) = {
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) = {
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) = {
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) = {
get: "/users/me/address"
};
@ -326,6 +326,8 @@ message User {
string street_address = 23;
bool password_change_required = 24;
uint64 sequence = 25;
repeated string login_names = 26;
string preferred_login_name = 27;
}
enum UserState {
@ -359,6 +361,22 @@ message UserProfile {
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 {
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}];
@ -377,6 +395,15 @@ message UserEmail {
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 {
string code = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
}
@ -399,6 +426,15 @@ message UserPhone {
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 {
string phone = 1 [(validate.rules).string = {min_len: 1, max_len: 20}];
}
@ -419,6 +455,18 @@ message UserAddress {
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 {
string country = 1 [(validate.rules).string = {max_len: 200}];
string locality = 2 [(validate.rules).string = {max_len: 200}];

View File

@ -3,7 +3,6 @@ package grpc
import (
"context"
"github.com/caos/zitadel/internal/errors"
"github.com/golang/protobuf/ptypes/empty"
)
@ -38,9 +37,6 @@ func (s *Server) UpdateApplication(ctx context.Context, in *ApplicationUpdate) (
return appFromModel(app), nil
}
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)
if err != nil {
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) {
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)
return &empty.Empty{}, err
}
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))
if err != nil {
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) {
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)
if err != nil {
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{
"/caos.zitadel.management.api.v1.ManagementService/GetIam": utils_auth.Option{
Permission: "authenticated",
CheckParam: "",
},
"/caos.zitadel.management.api.v1.ManagementService/GetUserByID": utils_auth.Option{
Permission: "user.read",
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) {
var protoReq UserID
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) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@ -5950,6 +5979,8 @@ var (
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_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_GetIam_0 = runtime.ForwardResponseMessage
forward_ManagementService_GetUserByID_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": {
"post": {
"operationId": "AddMyOrgDomain",
@ -2663,7 +2680,7 @@
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1User"
"$ref": "#/definitions/v1UserView"
}
}
},
@ -2904,7 +2921,7 @@
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1UserAddress"
"$ref": "#/definitions/v1UserAddressView"
}
}
},
@ -3003,7 +3020,7 @@
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1UserEmail"
"$ref": "#/definitions/v1UserEmailView"
}
}
},
@ -3115,7 +3132,7 @@
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1UserPhone"
"$ref": "#/definitions/v1UserPhoneView"
}
}
},
@ -3202,7 +3219,7 @@
"200": {
"description": "A successful response.",
"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": {
"type": "string",
"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": {
"type": "object",
"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": {
"type": "object",
"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": {
"type": "object",
"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": {
"type": "string",
"enum": [
@ -6454,6 +6629,15 @@
},
"resource_owner": {
"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/auth"
grpc_util "github.com/caos/zitadel/internal/api/grpc"
"github.com/caos/zitadel/internal/errors"
"github.com/golang/protobuf/ptypes/empty"
)
@ -18,9 +17,6 @@ func (s *Server) CreateProject(ctx context.Context, in *ProjectCreateRequest) (*
return projectFromModel(project), nil
}
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))
if err != nil {
return nil, err
@ -28,9 +24,6 @@ func (s *Server) UpdateProject(ctx context.Context, in *ProjectUpdateRequest) (*
return projectFromModel(project), nil
}
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)
if err != nil {
return nil, err
@ -38,9 +31,6 @@ func (s *Server) DeactivateProject(ctx context.Context, in *ProjectID) (*Project
return projectFromModel(project), nil
}
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)
if err != nil {
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) {
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))
if err != nil {
return nil, err
@ -95,9 +82,6 @@ func (s *Server) AddProjectRole(ctx context.Context, in *ProjectRoleAdd) (*Proje
return projectRoleFromModel(role), nil
}
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))
if err != nil {
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) {
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)
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
}
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"
)
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)
if err != nil {
return nil, err
}
return userFromModel(user), nil
return userViewFromModel(user), nil
}
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")
}
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)
if err != nil {
return nil, err
}
return profileFromModel(profile), nil
return profileViewFromModel(profile), nil
}
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
}
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)
if err != nil {
return nil, err
}
return emailFromModel(email), nil
return emailViewFromModel(email), nil
}
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
}
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)
if err != nil {
return nil, err
}
return phoneFromModel(phone), nil
return phoneViewFromModel(phone), nil
}
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
}
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)
if err != nil {
return nil, err
}
return addressFromModel(address), nil
return addressViewFromModel(address), nil
}
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 {
preferredLanguage, err := language.Parse(u.PreferredLanguage)
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 {
return &usr_model.Email{
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 {
return &usr_model.Phone{
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 {
return &usr_model.Address{
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")
return &UserView{
Id: user.ID,
State: userStateFromModel(user.State),
CreationDate: creationDate,
ChangeDate: changeDate,
LastLogin: lastLogin,
PasswordChanged: passwordChanged,
Sequence: user.Sequence,
ResourceOwner: user.ResourceOwner,
UserName: user.UserName,
FirstName: user.FirstName,
LastName: user.LastName,
NickName: user.NickName,
Email: user.Email,
IsEmailVerified: user.IsEmailVerified,
Phone: user.Phone,
IsPhoneVerified: user.IsPhoneVerified,
Country: user.Country,
Locality: user.Locality,
Region: user.Region,
PostalCode: user.PostalCode,
StreetAddress: user.StreetAddress,
Id: user.ID,
State: userStateFromModel(user.State),
CreationDate: creationDate,
ChangeDate: changeDate,
LastLogin: lastLogin,
PasswordChanged: passwordChanged,
Sequence: user.Sequence,
ResourceOwner: user.ResourceOwner,
UserName: user.UserName,
FirstName: user.FirstName,
LastName: user.LastName,
NickName: user.NickName,
Email: user.Email,
IsEmailVerified: user.IsEmailVerified,
Phone: user.Phone,
IsPhoneVerified: user.IsPhoneVerified,
Country: user.Country,
Locality: user.Locality,
Region: user.Region,
PostalCode: user.PostalCode,
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
rpc GetUserByID(UserID) returns (User) {
rpc GetUserByID(UserID) returns (UserView) {
option (google.api.http) = {
get: "/users/{id}"
};
@ -199,7 +210,7 @@ service ManagementService {
}
//USER_PROFILE
rpc GetUserProfile(UserID) returns (UserProfile) {
rpc GetUserProfile(UserID) returns (UserProfileView) {
option (google.api.http) = {
get: "/users/{id}/profile"
};
@ -221,7 +232,7 @@ service ManagementService {
}
//USER_EMAIL
rpc GetUserEmail(UserID) returns (UserEmail) {
rpc GetUserEmail(UserID) returns (UserEmailView) {
option (google.api.http) = {
get: "/users/{id}/email"
};
@ -254,7 +265,7 @@ service ManagementService {
}
//USER_PHONE
rpc GetUserPhone(UserID) returns (UserPhone) {
rpc GetUserPhone(UserID) returns (UserPhoneView) {
option (google.api.http) = {
get: "/users/{id}/phone"
};
@ -287,7 +298,7 @@ service ManagementService {
}
//USER_ADDRESS
rpc GetUserAddress(UserID) returns (UserAddress) {
rpc GetUserAddress(UserID) returns (UserAddressView) {
option (google.api.http) = {
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 {
string id = 1;
string sec_id = 2;
@ -1399,6 +1417,8 @@ message UserView {
string street_address = 22;
uint64 sequence = 23;
string resource_owner = 24;
repeated string login_names = 25;
string preferred_login_name = 27;
}
message UserSearchRequest {
@ -1456,6 +1476,22 @@ message UserProfile {
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 {
string id = 1;
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;
}
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 {
string id = 1;
string email = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
@ -1490,6 +1535,15 @@ message UserPhone {
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 {
string id = 1;
string phone = 2 [(validate.rules).string = {min_len: 1, max_len: 20}];
@ -1508,6 +1562,18 @@ message UserAddress {
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 {
string id = 1;
string country = 2 [(validate.rules).string = {max_len: 200}];