fix: add avatar url in members, user grants, session and oidc responses (#1852)

* fix: add avatar url in members, user grants, session and oidc responses

* fix auth request tests
This commit is contained in:
Livio Amstutz 2021-06-11 13:20:39 +02:00 committed by GitHub
parent 1e77b8aeae
commit 770994e143
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 368 additions and 207 deletions

View File

@ -90,6 +90,7 @@ AuthZ:
Auth: Auth:
SearchLimit: 1000 SearchLimit: 1000
Domain: $ZITADEL_DEFAULT_DOMAIN Domain: $ZITADEL_DEFAULT_DOMAIN
APIDomain: $ZITADEL_API_DOMAIN
Eventstore: Eventstore:
ServiceName: 'authAPI' ServiceName: 'authAPI'
Repository: Repository:
@ -139,6 +140,7 @@ Auth:
Admin: Admin:
SearchLimit: 1000 SearchLimit: 1000
Domain: $ZITADEL_DEFAULT_DOMAIN Domain: $ZITADEL_DEFAULT_DOMAIN
APIDomain: $ZITADEL_API_DOMAIN
Eventstore: Eventstore:
ServiceName: 'Admin' ServiceName: 'Admin'
Repository: Repository:
@ -176,6 +178,7 @@ Admin:
Mgmt: Mgmt:
SearchLimit: 1000 SearchLimit: 1000
Domain: $ZITADEL_DEFAULT_DOMAIN Domain: $ZITADEL_DEFAULT_DOMAIN
APIDomain: $ZITADEL_API_DOMAIN
Eventstore: Eventstore:
ServiceName: 'ManagementAPI' ServiceName: 'ManagementAPI'
Repository: Repository:

View File

@ -59,6 +59,7 @@ title: zitadel/member.proto
| first_name | string | - | | | first_name | string | - | |
| last_name | string | - | | | last_name | string | - | |
| display_name | string | - | | | display_name | string | - | |
| avatar_url | string | - | |

View File

@ -237,6 +237,7 @@ this query is always equals
| display_name | string | - | | | display_name | string | - | |
| preferred_language | string | - | | | preferred_language | string | - | |
| gender | Gender | - | | | gender | Gender | - | |
| avatar_url | string | - | |
@ -291,6 +292,7 @@ this query is always equals
| login_name | string | - | | | login_name | string | - | |
| display_name | string | - | | | display_name | string | - | |
| details | zitadel.v1.ObjectDetails | - | | | details | zitadel.v1.ObjectDetails | - | |
| avatar_url | string | - | |
@ -357,6 +359,7 @@ UserTypeQuery is always equals
| project_id | string | - | | | project_id | string | - | |
| project_name | string | - | | | project_name | string | - | |
| project_grant_id | string | - | | | project_grant_id | string | - | |
| avatar_url | string | - | |

1
go.sum
View File

@ -1116,7 +1116,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View File

@ -23,11 +23,12 @@ import (
) )
type IAMRepository struct { type IAMRepository struct {
Eventstore v1.Eventstore Eventstore v1.Eventstore
SearchLimit uint64 SearchLimit uint64
View *admin_view.View View *admin_view.View
SystemDefaults systemdefaults.SystemDefaults SystemDefaults systemdefaults.SystemDefaults
Roles []string Roles []string
PrefixAvatarURL string
} }
func (repo *IAMRepository) IAMMemberByID(ctx context.Context, iamID, userID string) (*iam_model.IAMMemberView, error) { func (repo *IAMRepository) IAMMemberByID(ctx context.Context, iamID, userID string) (*iam_model.IAMMemberView, error) {
@ -35,7 +36,7 @@ func (repo *IAMRepository) IAMMemberByID(ctx context.Context, iamID, userID stri
if err != nil { if err != nil {
return nil, err return nil, err
} }
return iam_es_model.IAMMemberToModel(member), nil return iam_es_model.IAMMemberToModel(member, repo.PrefixAvatarURL), nil
} }
func (repo *IAMRepository) SearchIAMMembers(ctx context.Context, request *iam_model.IAMMemberSearchRequest) (*iam_model.IAMMemberSearchResponse, error) { func (repo *IAMRepository) SearchIAMMembers(ctx context.Context, request *iam_model.IAMMemberSearchRequest) (*iam_model.IAMMemberSearchResponse, error) {
@ -53,7 +54,7 @@ func (repo *IAMRepository) SearchIAMMembers(ctx context.Context, request *iam_mo
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
TotalResult: count, TotalResult: count,
Result: iam_es_model.IAMMembersToModel(members), Result: iam_es_model.IAMMembersToModel(members, repo.PrefixAvatarURL),
} }
if err == nil { if err == nil {
result.Sequence = sequence.CurrentSequence result.Sequence = sequence.CurrentSequence
@ -340,7 +341,7 @@ func (repo *IAMRepository) SearchIAMMembersx(ctx context.Context, request *iam_m
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
TotalResult: count, TotalResult: count,
Result: iam_es_model.IAMMembersToModel(members), Result: iam_es_model.IAMMembersToModel(members, repo.PrefixAvatarURL),
} }
if err == nil { if err == nil {
result.Sequence = sequence.CurrentSequence result.Sequence = sequence.CurrentSequence

View File

@ -13,10 +13,11 @@ import (
) )
type UserRepo struct { type UserRepo struct {
SearchLimit uint64 SearchLimit uint64
Eventstore v1.Eventstore Eventstore v1.Eventstore
View *view.View View *view.View
SystemDefaults systemdefaults.SystemDefaults SystemDefaults systemdefaults.SystemDefaults
PrefixAvatarURL string
} }
func (repo *UserRepo) Health(ctx context.Context) error { func (repo *UserRepo) Health(ctx context.Context) error {
@ -34,7 +35,7 @@ func (repo *UserRepo) SearchUsers(ctx context.Context, request *model.UserSearch
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
TotalResult: count, TotalResult: count,
Result: usr_view_model.UsersToModel(users), Result: usr_view_model.UsersToModel(users, repo.PrefixAvatarURL),
} }
if sequenceErr == nil { if sequenceErr == nil {
result.Sequence = sequence.CurrentSequence result.Sequence = sequence.CurrentSequence

View File

@ -132,7 +132,9 @@ func (m *IAMMember) processUser(event *es_models.Event) (err error) {
usr_es_model.UserEmailChanged, usr_es_model.UserEmailChanged,
usr_es_model.HumanProfileChanged, usr_es_model.HumanProfileChanged,
usr_es_model.HumanEmailChanged, usr_es_model.HumanEmailChanged,
usr_es_model.MachineChanged: usr_es_model.MachineChanged,
usr_es_model.HumanAvatarAdded,
usr_es_model.HumanAvatarRemoved:
members, err := m.view.IAMMembersByUserID(event.AggregateID) members, err := m.view.IAMMembersByUserID(event.AggregateID)
if err != nil { if err != nil {
return err return err
@ -165,6 +167,9 @@ func (m *IAMMember) fillData(member *iam_view_model.IAMMemberView) (err error) {
func (m *IAMMember) fillUserData(member *iam_view_model.IAMMemberView, user *view_model.UserView) error { func (m *IAMMember) fillUserData(member *iam_view_model.IAMMemberView, user *view_model.UserView) error {
org, err := m.getOrgByID(context.Background(), user.ResourceOwner) org, err := m.getOrgByID(context.Background(), user.ResourceOwner)
if err != nil {
return err
}
policy := org.OrgIamPolicy policy := org.OrgIamPolicy
if policy == nil { if policy == nil {
policy, err = m.getDefaultOrgIAMPolicy(context.TODO()) policy, err = m.getDefaultOrgIAMPolicy(context.TODO())
@ -174,11 +179,13 @@ func (m *IAMMember) fillUserData(member *iam_view_model.IAMMemberView, user *vie
} }
member.UserName = user.UserName member.UserName = user.UserName
member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
member.UserResourceOwner = user.ResourceOwner
if user.HumanView != nil { if user.HumanView != nil {
member.FirstName = user.FirstName member.FirstName = user.FirstName
member.LastName = user.LastName member.LastName = user.LastName
member.DisplayName = user.FirstName + " " + user.LastName member.DisplayName = user.FirstName + " " + user.LastName
member.Email = user.Email member.Email = user.Email
member.AvatarKey = user.AvatarKey
} }
if user.MachineView != nil { if user.MachineView != nil {
member.DisplayName = user.MachineView.Name member.DisplayName = user.MachineView.Name

View File

@ -2,6 +2,7 @@ package eventsourcing
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/eventstore" "github.com/caos/zitadel/internal/admin/repository/eventsourcing/eventstore"
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/spooler" "github.com/caos/zitadel/internal/admin/repository/eventsourcing/spooler"
admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
@ -18,6 +19,7 @@ type Config struct {
View types.SQL View types.SQL
Spooler spooler.SpoolerConfig Spooler spooler.SpoolerConfig
Domain string Domain string
APIDomain string
} }
type EsRepository struct { type EsRepository struct {
@ -44,6 +46,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, s
} }
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, systemDefaults, static, localDevMode) spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, systemDefaults, static, localDevMode)
assetsAPI := conf.APIDomain + "/assets/v1/"
return &EsRepository{ return &EsRepository{
spooler: spool, spooler: spool,
@ -54,11 +57,12 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, s
SystemDefaults: systemDefaults, SystemDefaults: systemDefaults,
}, },
IAMRepository: eventstore.IAMRepository{ IAMRepository: eventstore.IAMRepository{
Eventstore: es, Eventstore: es,
View: view, View: view,
SystemDefaults: systemDefaults, SystemDefaults: systemDefaults,
SearchLimit: conf.SearchLimit, SearchLimit: conf.SearchLimit,
Roles: roles, Roles: roles,
PrefixAvatarURL: assetsAPI,
}, },
AdministratorRepo: eventstore.AdministratorRepo{ AdministratorRepo: eventstore.AdministratorRepo{
View: view, View: view,
@ -70,10 +74,11 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, s
SystemDefaults: systemDefaults, SystemDefaults: systemDefaults,
}, },
UserRepo: eventstore.UserRepo{ UserRepo: eventstore.UserRepo{
Eventstore: es, Eventstore: es,
View: view, View: view,
SearchLimit: conf.SearchLimit, SearchLimit: conf.SearchLimit,
SystemDefaults: systemDefaults, SystemDefaults: systemDefaults,
PrefixAvatarURL: assetsAPI,
}, },
}, nil }, nil
} }

View File

@ -24,6 +24,7 @@ func IAMMemberToPb(m *iam_model.IAMMemberView) *member_pb.Member {
FirstName: m.FirstName, FirstName: m.FirstName,
LastName: m.LastName, LastName: m.LastName,
DisplayName: m.DisplayName, DisplayName: m.DisplayName,
AvatarUrl: m.AvatarURL,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
m.Sequence, m.Sequence,
m.CreationDate, m.CreationDate,

View File

@ -24,6 +24,7 @@ func OrgMemberToPb(m *org_model.OrgMemberView) *member_pb.Member {
FirstName: m.FirstName, FirstName: m.FirstName,
LastName: m.LastName, LastName: m.LastName,
DisplayName: m.DisplayName, DisplayName: m.DisplayName,
AvatarUrl: m.AvatarURL,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
m.Sequence, m.Sequence,
m.CreationDate, m.CreationDate,

View File

@ -24,6 +24,7 @@ func ProjectGrantMemberToPb(m *proj_model.ProjectGrantMemberView) *member_pb.Mem
FirstName: m.FirstName, FirstName: m.FirstName,
LastName: m.LastName, LastName: m.LastName,
DisplayName: m.DisplayName, DisplayName: m.DisplayName,
AvatarUrl: m.AvatarURL,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
m.Sequence, m.Sequence,
m.CreationDate, m.CreationDate,

View File

@ -24,6 +24,7 @@ func ProjectMemberToPb(m *proj_model.ProjectMemberView) *member_pb.Member {
FirstName: m.FirstName, FirstName: m.FirstName,
LastName: m.LastName, LastName: m.LastName,
DisplayName: m.DisplayName, DisplayName: m.DisplayName,
AvatarUrl: m.AvatarURL,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
m.Sequence, m.Sequence,
m.CreationDate, m.CreationDate,

View File

@ -56,6 +56,7 @@ func HumanToPb(view *model.HumanView) *user_pb.Human {
DisplayName: view.DisplayName, DisplayName: view.DisplayName,
PreferredLanguage: view.PreferredLanguage, PreferredLanguage: view.PreferredLanguage,
Gender: GenderToPb(view.Gender), Gender: GenderToPb(view.Gender),
AvatarUrl: view.AvatarURL,
}, },
Email: &user_pb.Email{ Email: &user_pb.Email{
Email: view.Email, Email: view.Email,
@ -83,6 +84,7 @@ func ProfileToPb(profile *model.Profile) *user_pb.Profile {
DisplayName: profile.DisplayName, DisplayName: profile.DisplayName,
PreferredLanguage: profile.PreferredLanguage.String(), PreferredLanguage: profile.PreferredLanguage.String(),
Gender: GenderToPb(profile.Gender), Gender: GenderToPb(profile.Gender),
AvatarUrl: profile.AvatarURL,
} }
} }

View File

@ -24,6 +24,7 @@ func UserSessionToPb(session *user_model.UserSessionView) *user.Session {
LoginName: session.LoginName, LoginName: session.LoginName,
DisplayName: session.DisplayName, DisplayName: session.DisplayName,
AuthState: SessionStateToPb(session.State), AuthState: SessionStateToPb(session.State),
AvatarUrl: session.AvatarURL,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
session.Sequence, session.Sequence,
session.CreationDate, session.CreationDate,

View File

@ -32,6 +32,7 @@ func UserGrantToPb(grant *usr_grant_model.UserGrantView) *user_pb.UserGrant {
ProjectId: grant.ProjectID, ProjectId: grant.ProjectID,
ProjectName: grant.ProjectName, ProjectName: grant.ProjectName,
ProjectGrantId: grant.GrantID, ProjectGrantId: grant.GrantID,
AvatarUrl: grant.AvatarURL,
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
grant.Sequence, grant.Sequence,
grant.CreationDate, grant.CreationDate,

View File

@ -160,6 +160,7 @@ func (o *OPStorage) SetUserinfoFromScopes(ctx context.Context, userInfo oidc.Use
userInfo.SetGender(oidc.Gender(getGender(user.Gender))) userInfo.SetGender(oidc.Gender(getGender(user.Gender)))
locale, _ := language.Parse(user.PreferredLanguage) locale, _ := language.Parse(user.PreferredLanguage)
userInfo.SetLocale(locale) userInfo.SetLocale(locale)
userInfo.SetPicture(user.AvatarURL)
} else { } else {
userInfo.SetName(user.MachineView.Name) userInfo.SetName(user.MachineView.Name)
} }

View File

@ -2,9 +2,10 @@ package eventstore
import ( import (
"context" "context"
"time"
"github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
"time"
"github.com/caos/logging" "github.com/caos/logging"
@ -57,9 +58,11 @@ type AuthRequestRepo struct {
type userSessionViewProvider interface { type userSessionViewProvider interface {
UserSessionByIDs(string, string) (*user_view_model.UserSessionView, error) UserSessionByIDs(string, string) (*user_view_model.UserSessionView, error)
UserSessionsByAgentID(string) ([]*user_view_model.UserSessionView, error) UserSessionsByAgentID(string) ([]*user_view_model.UserSessionView, error)
PrefixAvatarURL() string
} }
type userViewProvider interface { type userViewProvider interface {
UserByID(string) (*user_view_model.UserView, error) UserByID(string) (*user_view_model.UserView, error)
PrefixAvatarURL() string
} }
type loginPolicyViewProvider interface { type loginPolicyViewProvider interface {
@ -616,6 +619,7 @@ func (repo *AuthRequestRepo) usersForUserSelection(request *domain.AuthRequest)
DisplayName: session.DisplayName, DisplayName: session.DisplayName,
UserName: session.UserName, UserName: session.UserName,
LoginName: session.LoginName, LoginName: session.LoginName,
ResourceOwner: session.ResourceOwner,
AvatarKey: session.AvatarKey, AvatarKey: session.AvatarKey,
UserSessionState: auth_req_model.UserSessionStateToDomain(session.State), UserSessionState: auth_req_model.UserSessionStateToDomain(session.State),
SelectionPossible: request.RequestedOrgID == "" || request.RequestedOrgID == session.ResourceOwner, SelectionPossible: request.RequestedOrgID == "" || request.RequestedOrgID == session.ResourceOwner,
@ -767,7 +771,7 @@ func userSessionsByUserAgentID(provider userSessionViewProvider, agentID string)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return user_view_model.UserSessionsToModel(session), nil return user_view_model.UserSessionsToModel(session, provider.PrefixAvatarURL()), nil
} }
func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eventProvider userEventProvider, agentID string, user *user_model.UserView) (*user_model.UserSessionView, error) { func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eventProvider userEventProvider, agentID string, user *user_model.UserView) (*user_model.UserSessionView, error) {
@ -781,7 +785,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
events, err := eventProvider.UserEventsByID(ctx, user.ID, session.Sequence) events, err := eventProvider.UserEventsByID(ctx, user.ID, session.Sequence)
if err != nil { if err != nil {
logging.Log("EVENT-Hse6s").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events") logging.Log("EVENT-Hse6s").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events")
return user_view_model.UserSessionToModel(session), nil return user_view_model.UserSessionToModel(session, provider.PrefixAvatarURL()), nil
} }
sessionCopy := *session sessionCopy := *session
for _, event := range events { for _, event := range events {
@ -806,7 +810,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
eventData, err := user_view_model.UserSessionFromEvent(event) eventData, err := user_view_model.UserSessionFromEvent(event)
if err != nil { if err != nil {
logging.Log("EVENT-sdgT3").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error getting event data") logging.Log("EVENT-sdgT3").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error getting event data")
return user_view_model.UserSessionToModel(session), nil return user_view_model.UserSessionToModel(session, provider.PrefixAvatarURL()), nil
} }
if eventData.UserAgentID != agentID { if eventData.UserAgentID != agentID {
continue continue
@ -817,7 +821,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
err := sessionCopy.AppendEvent(event) err := sessionCopy.AppendEvent(event)
logging.Log("EVENT-qbhj3").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("error appending event") logging.Log("EVENT-qbhj3").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("error appending event")
} }
return user_view_model.UserSessionToModel(&sessionCopy), nil return user_view_model.UserSessionToModel(&sessionCopy, provider.PrefixAvatarURL()), nil
} }
func activeUserByID(ctx context.Context, userViewProvider userViewProvider, userEventProvider userEventProvider, orgViewProvider orgViewProvider, userID string) (*user_model.UserView, error) { func activeUserByID(ctx context.Context, userViewProvider userViewProvider, userEventProvider userEventProvider, orgViewProvider orgViewProvider, userID string) (*user_model.UserView, error) {
@ -856,24 +860,24 @@ func userByID(ctx context.Context, viewProvider userViewProvider, eventProvider
events, err := eventProvider.UserEventsByID(ctx, userID, user.Sequence) events, err := eventProvider.UserEventsByID(ctx, userID, user.Sequence)
if err != nil { if err != nil {
logging.Log("EVENT-dfg42").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events") logging.Log("EVENT-dfg42").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events")
return user_view_model.UserToModel(user), nil return user_view_model.UserToModel(user, viewProvider.PrefixAvatarURL()), nil
} }
if len(events) == 0 { if len(events) == 0 {
if viewErr != nil { if viewErr != nil {
return nil, viewErr return nil, viewErr
} }
return user_view_model.UserToModel(user), viewErr return user_view_model.UserToModel(user, viewProvider.PrefixAvatarURL()), viewErr
} }
userCopy := *user userCopy := *user
for _, event := range events { for _, event := range events {
if err := userCopy.AppendEvent(event); err != nil { if err := userCopy.AppendEvent(event); err != nil {
return user_view_model.UserToModel(user), nil return user_view_model.UserToModel(user, viewProvider.PrefixAvatarURL()), nil
} }
} }
if userCopy.State == int32(user_model.UserStateDeleted) { if userCopy.State == int32(user_model.UserStateDeleted) {
return nil, errors.ThrowNotFound(nil, "EVENT-3F9so", "Errors.User.NotFound") return nil, errors.ThrowNotFound(nil, "EVENT-3F9so", "Errors.User.NotFound")
} }
return user_view_model.UserToModel(&userCopy), nil return user_view_model.UserToModel(&userCopy, viewProvider.PrefixAvatarURL()), nil
} }
func linkExternalIDPs(ctx context.Context, userCommandProvider userCommandProvider, request *domain.AuthRequest) error { func linkExternalIDPs(ctx context.Context, userCommandProvider userCommandProvider, request *domain.AuthRequest) error {

View File

@ -3,20 +3,19 @@ package eventstore
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"github.com/caos/zitadel/internal/domain"
"testing" "testing"
"time" "time"
iam_model "github.com/caos/zitadel/internal/iam/model"
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view" "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/auth_request/repository/cache" "github.com/caos/zitadel/internal/auth_request/repository/cache"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/v1/models" es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/model"
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
org_model "github.com/caos/zitadel/internal/org/model" org_model "github.com/caos/zitadel/internal/org/model"
org_view_model "github.com/caos/zitadel/internal/org/repository/view/model" org_view_model "github.com/caos/zitadel/internal/org/repository/view/model"
proj_view_model "github.com/caos/zitadel/internal/project/repository/view/model" proj_view_model "github.com/caos/zitadel/internal/project/repository/view/model"
@ -36,6 +35,10 @@ func (m *mockViewNoUserSession) UserSessionsByAgentID(string) ([]*user_view_mode
return nil, nil return nil, nil
} }
func (m *mockViewNoUserSession) PrefixAvatarURL() string {
return ""
}
type mockViewErrUserSession struct{} type mockViewErrUserSession struct{}
func (m *mockViewErrUserSession) UserSessionByIDs(string, string) (*user_view_model.UserSessionView, error) { func (m *mockViewErrUserSession) UserSessionByIDs(string, string) (*user_view_model.UserSessionView, error) {
@ -46,6 +49,10 @@ func (m *mockViewErrUserSession) UserSessionsByAgentID(string) ([]*user_view_mod
return nil, errors.ThrowInternal(nil, "id", "internal error") return nil, errors.ThrowInternal(nil, "id", "internal error")
} }
func (m *mockViewErrUserSession) PrefixAvatarURL() string {
return ""
}
type mockViewUserSession struct { type mockViewUserSession struct {
ExternalLoginVerification time.Time ExternalLoginVerification time.Time
PasswordlessVerification time.Time PasswordlessVerification time.Time
@ -83,12 +90,20 @@ func (m *mockViewUserSession) UserSessionsByAgentID(string) ([]*user_view_model.
return sessions, nil return sessions, nil
} }
func (m *mockViewUserSession) PrefixAvatarURL() string {
return "prefix/"
}
type mockViewNoUser struct{} type mockViewNoUser struct{}
func (m *mockViewNoUser) UserByID(string) (*user_view_model.UserView, error) { func (m *mockViewNoUser) UserByID(string) (*user_view_model.UserView, error) {
return nil, errors.ThrowNotFound(nil, "id", "user not found") return nil, errors.ThrowNotFound(nil, "id", "user not found")
} }
func (m *mockViewNoUser) PrefixAvatarURL() string {
return ""
}
type mockEventUser struct { type mockEventUser struct {
Event *es_models.Event Event *es_models.Event
} }
@ -152,6 +167,10 @@ func (m *mockViewUser) UserByID(string) (*user_view_model.UserView, error) {
}, nil }, nil
} }
func (m *mockViewUser) PrefixAvatarURL() string {
return ""
}
type mockViewOrg struct { type mockViewOrg struct {
State org_model.OrgState State org_model.OrgState
} }
@ -291,11 +310,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
UserID: "id1", UserID: "id1",
LoginName: "loginname1", LoginName: "loginname1",
SelectionPossible: true, SelectionPossible: true,
ResourceOwner: "orgID1",
}, },
{ {
UserID: "id2", UserID: "id2",
LoginName: "loginname2", LoginName: "loginname2",
SelectionPossible: true, SelectionPossible: true,
ResourceOwner: "orgID2",
}, },
}, },
}}, }},
@ -329,11 +350,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
UserID: "id1", UserID: "id1",
LoginName: "loginname1", LoginName: "loginname1",
SelectionPossible: true, SelectionPossible: true,
ResourceOwner: "orgID1",
}, },
{ {
UserID: "id2", UserID: "id2",
LoginName: "loginname2", LoginName: "loginname2",
SelectionPossible: false, SelectionPossible: false,
ResourceOwner: "orgID2",
}, },
}, },
}}, }},

View File

@ -24,10 +24,11 @@ import (
) )
type UserRepo struct { type UserRepo struct {
SearchLimit uint64 SearchLimit uint64
Eventstore v1.Eventstore Eventstore v1.Eventstore
View *view.View View *view.View
SystemDefaults systemdefaults.SystemDefaults SystemDefaults systemdefaults.SystemDefaults
PrefixAvatarURL string
} }
func (repo *UserRepo) Health(ctx context.Context) error { func (repo *UserRepo) Health(ctx context.Context) error {
@ -153,18 +154,18 @@ func (repo *UserRepo) UserByID(ctx context.Context, id string) (*model.UserView,
events, err := repo.getUserEvents(ctx, id, user.Sequence) events, err := repo.getUserEvents(ctx, id, user.Sequence)
if err != nil { if err != nil {
logging.Log("EVENT-PSoc3").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events") logging.Log("EVENT-PSoc3").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events")
return usr_view_model.UserToModel(user), nil return usr_view_model.UserToModel(user, repo.PrefixAvatarURL), nil
} }
userCopy := *user userCopy := *user
for _, event := range events { for _, event := range events {
if err := userCopy.AppendEvent(event); err != nil { if err := userCopy.AppendEvent(event); err != nil {
return usr_view_model.UserToModel(user), nil return usr_view_model.UserToModel(user, repo.PrefixAvatarURL), nil
} }
} }
if userCopy.State == int32(model.UserStateDeleted) { if userCopy.State == int32(model.UserStateDeleted) {
return nil, errors.ThrowNotFound(nil, "EVENT-vZ8us", "Errors.User.NotFound") return nil, errors.ThrowNotFound(nil, "EVENT-vZ8us", "Errors.User.NotFound")
} }
return usr_view_model.UserToModel(&userCopy), nil return usr_view_model.UserToModel(&userCopy, repo.PrefixAvatarURL), nil
} }
func (repo *UserRepo) UserEventsByID(ctx context.Context, id string, sequence uint64) ([]*models.Event, error) { func (repo *UserRepo) UserEventsByID(ctx context.Context, id string, sequence uint64) ([]*models.Event, error) {
@ -179,18 +180,18 @@ func (repo *UserRepo) UserByLoginName(ctx context.Context, loginname string) (*m
events, err := repo.getUserEvents(ctx, user.ID, user.Sequence) events, err := repo.getUserEvents(ctx, user.ID, user.Sequence)
if err != nil { if err != nil {
logging.Log("EVENT-PSoc3").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events") logging.Log("EVENT-PSoc3").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events")
return usr_view_model.UserToModel(user), nil return usr_view_model.UserToModel(user, repo.PrefixAvatarURL), nil
} }
userCopy := *user userCopy := *user
for _, event := range events { for _, event := range events {
if err := userCopy.AppendEvent(event); err != nil { if err := userCopy.AppendEvent(event); err != nil {
return usr_view_model.UserToModel(user), nil return usr_view_model.UserToModel(user, repo.PrefixAvatarURL), nil
} }
} }
if userCopy.State == int32(model.UserStateDeleted) { if userCopy.State == int32(model.UserStateDeleted) {
return nil, errors.ThrowNotFound(nil, "EVENT-vZ8us", "Errors.User.NotFound") return nil, errors.ThrowNotFound(nil, "EVENT-vZ8us", "Errors.User.NotFound")
} }
return usr_view_model.UserToModel(&userCopy), nil return usr_view_model.UserToModel(&userCopy, repo.PrefixAvatarURL), nil
} }
func (repo *UserRepo) MyUserChanges(ctx context.Context, lastSequence uint64, limit uint64, sortAscending bool, retention time.Duration) (*model.UserChanges, error) { func (repo *UserRepo) MyUserChanges(ctx context.Context, lastSequence uint64, limit uint64, sortAscending bool, retention time.Duration) (*model.UserChanges, error) {
changes, err := repo.getUserChanges(ctx, authz.GetCtxData(ctx).UserID, lastSequence, limit, sortAscending, retention) changes, err := repo.getUserChanges(ctx, authz.GetCtxData(ctx).UserID, lastSequence, limit, sortAscending, retention)
@ -233,7 +234,7 @@ func (repo *UserRepo) SearchUsers(ctx context.Context, request *model.UserSearch
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
TotalResult: count, TotalResult: count,
Result: usr_view_model.UsersToModel(users), Result: usr_view_model.UsersToModel(users, repo.PrefixAvatarURL),
} }
if sequenceErr == nil { if sequenceErr == nil {
result.Sequence = sequence.CurrentSequence result.Sequence = sequence.CurrentSequence

View File

@ -21,11 +21,12 @@ import (
) )
type UserGrantRepo struct { type UserGrantRepo struct {
SearchLimit uint64 SearchLimit uint64
View *view.View View *view.View
IamID string IamID string
Auth authz.Config Auth authz.Config
AuthZRepo *authz_repo.EsRepository AuthZRepo *authz_repo.EsRepository
PrefixAvatarURL string
} }
func (repo *UserGrantRepo) SearchMyUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) { func (repo *UserGrantRepo) SearchMyUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) {
@ -44,7 +45,7 @@ func (repo *UserGrantRepo) SearchMyUserGrants(ctx context.Context, request *gran
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
TotalResult: count, TotalResult: count,
Result: model.UserGrantsToModel(grants), Result: model.UserGrantsToModel(grants, repo.PrefixAvatarURL),
} }
if err == nil { if err == nil {
result.Sequence = sequence.CurrentSequence result.Sequence = sequence.CurrentSequence
@ -234,7 +235,7 @@ func (repo *UserGrantRepo) UserGrantsByProjectAndUserID(projectID, userID string
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.UserGrantsToModel(grants), nil return model.UserGrantsToModel(grants, repo.PrefixAvatarURL), nil
} }
func (repo *UserGrantRepo) userOrg(ctxData authz.CtxData) (*grant_model.ProjectOrgSearchResponse, error) { func (repo *UserGrantRepo) userOrg(ctxData authz.CtxData) (*grant_model.ProjectOrgSearchResponse, error) {

View File

@ -18,7 +18,7 @@ func (repo *UserSessionRepo) GetMyUserSessions(ctx context.Context) ([]*usr_mode
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.UserSessionsToModel(userSessions), nil return model.UserSessionsToModel(userSessions, repo.View.PrefixAvatarURL()), nil
} }
func (repo *UserSessionRepo) ActiveUserSessionCount() int64 { func (repo *UserSessionRepo) ActiveUserSessionCount() int64 {

View File

@ -2,10 +2,11 @@ package handler
import ( import (
"context" "context"
"strings"
"github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1"
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
iam_view "github.com/caos/zitadel/internal/iam/repository/view" iam_view "github.com/caos/zitadel/internal/iam/repository/view"
"strings"
es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk" es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk"
org_view "github.com/caos/zitadel/internal/org/repository/view" org_view "github.com/caos/zitadel/internal/org/repository/view"
@ -149,7 +150,9 @@ func (u *UserGrant) processUser(event *es_models.Event) (err error) {
usr_es_model.UserEmailChanged, usr_es_model.UserEmailChanged,
usr_es_model.HumanProfileChanged, usr_es_model.HumanProfileChanged,
usr_es_model.HumanEmailChanged, usr_es_model.HumanEmailChanged,
usr_es_model.MachineChanged: usr_es_model.MachineChanged,
usr_es_model.HumanAvatarAdded,
usr_es_model.HumanAvatarRemoved:
grants, err := u.view.UserGrantsByUserID(event.AggregateID) grants, err := u.view.UserGrantsByUserID(event.AggregateID)
if err != nil { if err != nil {
return err return err
@ -396,11 +399,13 @@ func (u *UserGrant) fillData(grant *view_model.UserGrantView, resourceOwner stri
func (u *UserGrant) fillUserData(grant *view_model.UserGrantView, user *model.UserView) { func (u *UserGrant) fillUserData(grant *view_model.UserGrantView, user *model.UserView) {
grant.UserName = user.UserName grant.UserName = user.UserName
grant.UserResourceOwner = user.ResourceOwner
if user.HumanView != nil { if user.HumanView != nil {
grant.FirstName = user.FirstName grant.FirstName = user.FirstName
grant.LastName = user.LastName grant.LastName = user.LastName
grant.DisplayName = user.FirstName + " " + user.LastName grant.DisplayName = user.FirstName + " " + user.LastName
grant.Email = user.Email grant.Email = user.Email
grant.AvatarKey = user.AvatarKey
} }
if user.MachineView != nil { if user.MachineView != nil {
grant.DisplayName = user.MachineView.Name grant.DisplayName = user.MachineView.Name

View File

@ -24,6 +24,7 @@ import (
type Config struct { type Config struct {
SearchLimit uint64 SearchLimit uint64
Domain string Domain string
APIDomain string
Eventstore v1.Config Eventstore v1.Config
AuthRequest cache.Config AuthRequest cache.Config
View types.SQL View types.SQL
@ -63,7 +64,9 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
} }
idGenerator := id.SonyFlakeGenerator idGenerator := id.SonyFlakeGenerator
view, err := auth_view.StartView(sqlClient, keyAlgorithm, idGenerator) assetsAPI := conf.APIDomain + "/assets/v1/"
view, err := auth_view.StartView(sqlClient, keyAlgorithm, idGenerator, assetsAPI)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -78,10 +81,11 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
locker := spooler.NewLocker(sqlClient) locker := spooler.NewLocker(sqlClient)
userRepo := eventstore.UserRepo{ userRepo := eventstore.UserRepo{
SearchLimit: conf.SearchLimit, SearchLimit: conf.SearchLimit,
Eventstore: es, Eventstore: es,
View: view, View: view,
SystemDefaults: systemDefaults, SystemDefaults: systemDefaults,
PrefixAvatarURL: assetsAPI,
} }
return &EsRepository{ return &EsRepository{
spool, spool,

View File

@ -10,23 +10,29 @@ import (
) )
type View struct { type View struct {
Db *gorm.DB Db *gorm.DB
keyAlgorithm crypto.EncryptionAlgorithm keyAlgorithm crypto.EncryptionAlgorithm
idGenerator id.Generator idGenerator id.Generator
prefixAvatarURL string
} }
func StartView(sqlClient *sql.DB, keyAlgorithm crypto.EncryptionAlgorithm, idGenerator id.Generator) (*View, error) { func StartView(sqlClient *sql.DB, keyAlgorithm crypto.EncryptionAlgorithm, idGenerator id.Generator, prefixAvatarURL string) (*View, error) {
gorm, err := gorm.Open("postgres", sqlClient) gorm, err := gorm.Open("postgres", sqlClient)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &View{ return &View{
Db: gorm, Db: gorm,
keyAlgorithm: keyAlgorithm, keyAlgorithm: keyAlgorithm,
idGenerator: idGenerator, idGenerator: idGenerator,
prefixAvatarURL: prefixAvatarURL,
}, nil }, nil
} }
func (v *View) Health() (err error) { func (v *View) Health() (err error) {
return v.Db.DB().Ping() return v.Db.DB().Ping()
} }
func (v *View) PrefixAvatarURL() string {
return v.prefixAvatarURL
}

View File

@ -1,8 +1,9 @@
package domain package domain
import ( import (
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
"golang.org/x/text/language" "golang.org/x/text/language"
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
) )
type Profile struct { type Profile struct {
@ -21,3 +22,10 @@ type Profile struct {
func (p *Profile) IsValid() bool { func (p *Profile) IsValid() bool {
return p.FirstName != "" && p.LastName != "" return p.FirstName != "" && p.LastName != ""
} }
func AvatarURL(prefix, resourceOwner, key string) string {
if prefix == "" || resourceOwner == "" || key == "" {
return ""
}
return prefix + resourceOwner + "/" + key
}

View File

@ -48,6 +48,7 @@ type UserSelection struct {
UserSessionState UserSessionState UserSessionState UserSessionState
SelectionPossible bool SelectionPossible bool
AvatarKey string AvatarKey string
ResourceOwner string
} }
type UserSessionState int32 type UserSessionState int32

View File

@ -16,6 +16,8 @@ type IAMMemberView struct {
LastName string LastName string
DisplayName string DisplayName string
PreferredLoginName string PreferredLoginName string
AvatarURL string
UserResourceOwner string
Roles []string Roles []string
CreationDate time.Time CreationDate time.Time
ChangeDate time.Time ChangeDate time.Time

View File

@ -38,22 +38,6 @@ func LabelPolicyToModel(policy *LabelPolicy) *iam_model.LabelPolicy {
} }
} }
func LabelPolicyFromModel(policy *iam_model.LabelPolicy) *LabelPolicy {
return &LabelPolicy{
ObjectRoot: policy.ObjectRoot,
State: int32(policy.State),
PrimaryColor: policy.PrimaryColor,
BackgroundColor: policy.BackgroundColor,
WarnColor: policy.WarnColor,
FontColor: policy.FontColor,
PrimaryColorDark: policy.PrimaryColorDark,
BackgroundColorDark: policy.BackgroundColorDark,
WarnColorDark: policy.WarnColorDark,
FontColorDark: policy.FontColorDark,
HideLoginNameSuffix: policy.HideLoginNameSuffix,
}
}
func (i *IAM) appendAddLabelPolicyEvent(event *es_models.Event) error { func (i *IAM) appendAddLabelPolicyEvent(event *es_models.Event) error {
i.DefaultLabelPolicy = new(LabelPolicy) i.DefaultLabelPolicy = new(LabelPolicy)
err := i.DefaultLabelPolicy.SetDataLabel(event) err := i.DefaultLabelPolicy.SetDataLabel(event)
@ -71,7 +55,7 @@ func (i *IAM) appendChangeLabelPolicyEvent(event *es_models.Event) error {
func (p *LabelPolicy) SetDataLabel(event *es_models.Event) error { func (p *LabelPolicy) SetDataLabel(event *es_models.Event) error {
err := json.Unmarshal(event.Data, p) err := json.Unmarshal(event.Data, p)
if err != nil { if err != nil {
return errors.ThrowInternal(err, "MODEL-ikjhf", "unable to unmarshal data") return errors.ThrowInternal(err, "MODEL-Gdgwq", "unable to unmarshal data")
} }
return nil return nil
} }

View File

@ -4,13 +4,14 @@ import (
"encoding/json" "encoding/json"
"time" "time"
es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/lib/pq"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/iam/model"
"github.com/lib/pq" es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
) )
const ( const (
@ -33,28 +34,14 @@ type IAMMemberView struct {
Roles pq.StringArray `json:"roles" gorm:"column:roles"` Roles pq.StringArray `json:"roles" gorm:"column:roles"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
AvatarKey string `json:"-" gorm:"column:avatar_key"`
UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`
} }
func IAMMemberViewFromModel(member *model.IAMMemberView) *IAMMemberView { func IAMMemberToModel(member *IAMMemberView, prefixAvatarURL string) *model.IAMMemberView {
return &IAMMemberView{
UserID: member.UserID,
IAMID: member.IAMID,
UserName: member.UserName,
Email: member.Email,
FirstName: member.FirstName,
LastName: member.LastName,
DisplayName: member.DisplayName,
Roles: member.Roles,
Sequence: member.Sequence,
CreationDate: member.CreationDate,
ChangeDate: member.ChangeDate,
}
}
func IAMMemberToModel(member *IAMMemberView) *model.IAMMemberView {
return &model.IAMMemberView{ return &model.IAMMemberView{
UserID: member.UserID, UserID: member.UserID,
IAMID: member.IAMID, IAMID: member.IAMID,
@ -64,6 +51,8 @@ func IAMMemberToModel(member *IAMMemberView) *model.IAMMemberView {
LastName: member.LastName, LastName: member.LastName,
DisplayName: member.DisplayName, DisplayName: member.DisplayName,
PreferredLoginName: member.PreferredLoginName, PreferredLoginName: member.PreferredLoginName,
AvatarURL: domain.AvatarURL(prefixAvatarURL, member.UserResourceOwner, member.AvatarKey),
UserResourceOwner: member.UserResourceOwner,
Roles: member.Roles, Roles: member.Roles,
Sequence: member.Sequence, Sequence: member.Sequence,
CreationDate: member.CreationDate, CreationDate: member.CreationDate,
@ -71,10 +60,10 @@ func IAMMemberToModel(member *IAMMemberView) *model.IAMMemberView {
} }
} }
func IAMMembersToModel(roles []*IAMMemberView) []*model.IAMMemberView { func IAMMembersToModel(roles []*IAMMemberView, prefixAvatarURL string) []*model.IAMMemberView {
result := make([]*model.IAMMemberView, len(roles)) result := make([]*model.IAMMemberView, len(roles))
for i, r := range roles { for i, r := range roles {
result[i] = IAMMemberToModel(r) result[i] = IAMMemberToModel(r, prefixAvatarURL)
} }
return result return result
} }

View File

@ -31,11 +31,12 @@ import (
) )
type OrgRepository struct { type OrgRepository struct {
SearchLimit uint64 SearchLimit uint64
Eventstore v1.Eventstore Eventstore v1.Eventstore
View *mgmt_view.View View *mgmt_view.View
Roles []string Roles []string
SystemDefaults systemdefaults.SystemDefaults SystemDefaults systemdefaults.SystemDefaults
PrefixAvatarURL string
} }
func (repo *OrgRepository) OrgByID(ctx context.Context, id string) (*org_model.OrgView, error) { func (repo *OrgRepository) OrgByID(ctx context.Context, id string) (*org_model.OrgView, error) {
@ -121,7 +122,7 @@ func (repo *OrgRepository) OrgMemberByID(ctx context.Context, orgID, userID stri
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.OrgMemberToModel(member), nil return model.OrgMemberToModel(member, repo.PrefixAvatarURL), nil
} }
func (repo *OrgRepository) SearchMyOrgMembers(ctx context.Context, request *org_model.OrgMemberSearchRequest) (*org_model.OrgMemberSearchResponse, error) { func (repo *OrgRepository) SearchMyOrgMembers(ctx context.Context, request *org_model.OrgMemberSearchRequest) (*org_model.OrgMemberSearchResponse, error) {
@ -140,7 +141,7 @@ func (repo *OrgRepository) SearchMyOrgMembers(ctx context.Context, request *org_
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
TotalResult: count, TotalResult: count,
Result: model.OrgMembersToModel(members), Result: model.OrgMembersToModel(members, repo.PrefixAvatarURL),
} }
if sequenceErr == nil { if sequenceErr == nil {
result.Sequence = sequence.CurrentSequence result.Sequence = sequence.CurrentSequence
@ -653,18 +654,18 @@ func (repo *OrgRepository) userByID(ctx context.Context, id string) (*usr_model.
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-PSoc3").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-PSoc3").WithError(esErr).Debug("error retrieving new events")
return usr_es_model.UserToModel(user), nil return usr_es_model.UserToModel(user, repo.PrefixAvatarURL), nil
} }
userCopy := *user userCopy := *user
for _, event := range events { for _, event := range events {
if err := userCopy.AppendEvent(event); err != nil { if err := userCopy.AppendEvent(event); err != nil {
return usr_es_model.UserToModel(user), nil return usr_es_model.UserToModel(user, repo.PrefixAvatarURL), nil
} }
} }
if userCopy.State == int32(usr_es_model.UserStateDeleted) { if userCopy.State == int32(usr_es_model.UserStateDeleted) {
return nil, errors.ThrowNotFound(nil, "EVENT-3n8Fs", "Errors.User.NotFound") return nil, errors.ThrowNotFound(nil, "EVENT-3n8Fs", "Errors.User.NotFound")
} }
return usr_es_model.UserToModel(&userCopy), nil return usr_es_model.UserToModel(&userCopy, repo.PrefixAvatarURL), nil
} }
func (r *OrgRepository) getUserEvents(ctx context.Context, userID string, sequence uint64) ([]*models.Event, error) { func (r *OrgRepository) getUserEvents(ctx context.Context, userID string, sequence uint64) ([]*models.Event, error) {

View File

@ -31,10 +31,11 @@ import (
type ProjectRepo struct { type ProjectRepo struct {
v1.Eventstore v1.Eventstore
SearchLimit uint64 SearchLimit uint64
View *view.View View *view.View
Roles []string Roles []string
IAMID string IAMID string
PrefixAvatarURL string
} }
func (repo *ProjectRepo) ProjectByID(ctx context.Context, id string) (*proj_model.ProjectView, error) { func (repo *ProjectRepo) ProjectByID(ctx context.Context, id string) (*proj_model.ProjectView, error) {
@ -136,7 +137,7 @@ func (repo *ProjectRepo) ProjectMemberByID(ctx context.Context, projectID, userI
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.ProjectMemberToModel(member), nil return model.ProjectMemberToModel(member, repo.PrefixAvatarURL), nil
} }
func (repo *ProjectRepo) SearchProjectMembers(ctx context.Context, request *proj_model.ProjectMemberSearchRequest) (*proj_model.ProjectMemberSearchResponse, error) { func (repo *ProjectRepo) SearchProjectMembers(ctx context.Context, request *proj_model.ProjectMemberSearchRequest) (*proj_model.ProjectMemberSearchResponse, error) {
@ -154,7 +155,7 @@ func (repo *ProjectRepo) SearchProjectMembers(ctx context.Context, request *proj
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
TotalResult: uint64(count), TotalResult: uint64(count),
Result: model.ProjectMembersToModel(members), Result: model.ProjectMembersToModel(members, repo.PrefixAvatarURL),
} }
if sequenceErr == nil { if sequenceErr == nil {
result.Sequence = sequence.CurrentSequence result.Sequence = sequence.CurrentSequence
@ -442,7 +443,7 @@ func (repo *ProjectRepo) ProjectGrantMemberByID(ctx context.Context, projectID,
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.ProjectGrantMemberToModel(member), nil return model.ProjectGrantMemberToModel(member, repo.PrefixAvatarURL), nil
} }
func (repo *ProjectRepo) SearchProjectGrantRoles(ctx context.Context, projectID, grantID string, request *proj_model.ProjectRoleSearchRequest) (*proj_model.ProjectRoleSearchResponse, error) { func (repo *ProjectRepo) SearchProjectGrantRoles(ctx context.Context, projectID, grantID string, request *proj_model.ProjectRoleSearchRequest) (*proj_model.ProjectRoleSearchResponse, error) {
@ -491,7 +492,7 @@ func (repo *ProjectRepo) SearchProjectGrantMembers(ctx context.Context, request
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
TotalResult: uint64(count), TotalResult: uint64(count),
Result: model.ProjectGrantMembersToModel(members), Result: model.ProjectGrantMembersToModel(members, repo.PrefixAvatarURL),
} }
if sequenceErr == nil { if sequenceErr == nil {
result.Sequence = sequence.CurrentSequence result.Sequence = sequence.CurrentSequence
@ -542,18 +543,18 @@ func (repo *ProjectRepo) userByID(ctx context.Context, id string) (*usr_model.Us
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-PSoc3").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-PSoc3").WithError(esErr).Debug("error retrieving new events")
return usr_es_model.UserToModel(user), nil return usr_es_model.UserToModel(user, repo.PrefixAvatarURL), nil
} }
userCopy := *user userCopy := *user
for _, event := range events { for _, event := range events {
if err := userCopy.AppendEvent(event); err != nil { if err := userCopy.AppendEvent(event); err != nil {
return usr_es_model.UserToModel(user), nil return usr_es_model.UserToModel(user, repo.PrefixAvatarURL), nil
} }
} }
if userCopy.State == int32(usr_model.UserStateDeleted) { if userCopy.State == int32(usr_model.UserStateDeleted) {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-2m0Fs", "Errors.User.NotFound") return nil, caos_errs.ThrowNotFound(nil, "EVENT-2m0Fs", "Errors.User.NotFound")
} }
return usr_es_model.UserToModel(&userCopy), nil return usr_es_model.UserToModel(&userCopy, repo.PrefixAvatarURL), nil
} }
func (r *ProjectRepo) getUserEvents(ctx context.Context, userID string, sequence uint64) ([]*models.Event, error) { func (r *ProjectRepo) getUserEvents(ctx context.Context, userID string, sequence uint64) ([]*models.Event, error) {

View File

@ -27,9 +27,10 @@ import (
type UserRepo struct { type UserRepo struct {
v1.Eventstore v1.Eventstore
SearchLimit uint64 SearchLimit uint64
View *view.View View *view.View
SystemDefaults systemdefaults.SystemDefaults SystemDefaults systemdefaults.SystemDefaults
PrefixAvatarURL string
} }
func (repo *UserRepo) UserByID(ctx context.Context, id string) (*usr_model.UserView, error) { func (repo *UserRepo) UserByID(ctx context.Context, id string) (*usr_model.UserView, error) {
@ -46,18 +47,18 @@ func (repo *UserRepo) UserByID(ctx context.Context, id string) (*usr_model.UserV
} }
if esErr != nil { if esErr != nil {
logging.Log("EVENT-PSoc3").WithError(esErr).Debug("error retrieving new events") logging.Log("EVENT-PSoc3").WithError(esErr).Debug("error retrieving new events")
return model.UserToModel(user), nil return model.UserToModel(user, repo.PrefixAvatarURL), nil
} }
userCopy := *user userCopy := *user
for _, event := range events { for _, event := range events {
if err := userCopy.AppendEvent(event); err != nil { if err := userCopy.AppendEvent(event); err != nil {
return model.UserToModel(user), nil return model.UserToModel(user, repo.PrefixAvatarURL), nil
} }
} }
if userCopy.State == int32(usr_model.UserStateDeleted) { if userCopy.State == int32(usr_model.UserStateDeleted) {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-4Fm9s", "Errors.User.NotFound") return nil, caos_errs.ThrowNotFound(nil, "EVENT-4Fm9s", "Errors.User.NotFound")
} }
return model.UserToModel(&userCopy), nil return model.UserToModel(&userCopy, repo.PrefixAvatarURL), nil
} }
func (repo *UserRepo) SearchUsers(ctx context.Context, request *usr_model.UserSearchRequest, ensureLimit bool) (*usr_model.UserSearchResponse, error) { func (repo *UserRepo) SearchUsers(ctx context.Context, request *usr_model.UserSearchRequest, ensureLimit bool) (*usr_model.UserSearchResponse, error) {
@ -78,7 +79,7 @@ func (repo *UserRepo) SearchUsers(ctx context.Context, request *usr_model.UserSe
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
TotalResult: count, TotalResult: count,
Result: model.UsersToModel(users), Result: model.UsersToModel(users, repo.PrefixAvatarURL),
} }
if sequenceErr == nil { if sequenceErr == nil {
result.Sequence = sequence.CurrentSequence result.Sequence = sequence.CurrentSequence
@ -118,7 +119,7 @@ func (repo *UserRepo) GetUserByLoginNameGlobal(ctx context.Context, loginName st
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.UserToModel(user), nil return model.UserToModel(user, repo.PrefixAvatarURL), nil
} }
func (repo *UserRepo) IsUserUnique(ctx context.Context, userName, email string) (bool, error) { func (repo *UserRepo) IsUserUnique(ctx context.Context, userName, email string) (bool, error) {

View File

@ -13,8 +13,9 @@ import (
) )
type UserGrantRepo struct { type UserGrantRepo struct {
SearchLimit uint64 SearchLimit uint64
View *view.View View *view.View
PrefixAvatarURL string
} }
func (repo *UserGrantRepo) UserGrantByID(ctx context.Context, grantID string) (*grant_model.UserGrantView, error) { func (repo *UserGrantRepo) UserGrantByID(ctx context.Context, grantID string) (*grant_model.UserGrantView, error) {
@ -22,7 +23,7 @@ func (repo *UserGrantRepo) UserGrantByID(ctx context.Context, grantID string) (*
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.UserGrantToModel(grant), nil return model.UserGrantToModel(grant, repo.PrefixAvatarURL), nil
} }
func (repo *UserGrantRepo) UserGrantsByProjectID(ctx context.Context, projectID string) ([]*grant_model.UserGrantView, error) { func (repo *UserGrantRepo) UserGrantsByProjectID(ctx context.Context, projectID string) ([]*grant_model.UserGrantView, error) {
@ -30,7 +31,7 @@ func (repo *UserGrantRepo) UserGrantsByProjectID(ctx context.Context, projectID
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.UserGrantsToModel(grants), nil return model.UserGrantsToModel(grants, repo.PrefixAvatarURL), nil
} }
func (repo *UserGrantRepo) UserGrantsByProjectIDAndRoleKey(ctx context.Context, projectID, roleKey string) ([]*grant_model.UserGrantView, error) { func (repo *UserGrantRepo) UserGrantsByProjectIDAndRoleKey(ctx context.Context, projectID, roleKey string) ([]*grant_model.UserGrantView, error) {
@ -38,7 +39,7 @@ func (repo *UserGrantRepo) UserGrantsByProjectIDAndRoleKey(ctx context.Context,
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.UserGrantsToModel(grants), nil return model.UserGrantsToModel(grants, repo.PrefixAvatarURL), nil
} }
func (repo *UserGrantRepo) UserGrantsByProjectAndGrantID(ctx context.Context, projectID, grantID string) ([]*grant_model.UserGrantView, error) { func (repo *UserGrantRepo) UserGrantsByProjectAndGrantID(ctx context.Context, projectID, grantID string) ([]*grant_model.UserGrantView, error) {
@ -46,7 +47,7 @@ func (repo *UserGrantRepo) UserGrantsByProjectAndGrantID(ctx context.Context, pr
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.UserGrantsToModel(grants), nil return model.UserGrantsToModel(grants, repo.PrefixAvatarURL), nil
} }
func (repo *UserGrantRepo) UserGrantsByUserID(ctx context.Context, userID string) ([]*grant_model.UserGrantView, error) { func (repo *UserGrantRepo) UserGrantsByUserID(ctx context.Context, userID string) ([]*grant_model.UserGrantView, error) {
@ -54,7 +55,7 @@ func (repo *UserGrantRepo) UserGrantsByUserID(ctx context.Context, userID string
if err != nil { if err != nil {
return nil, err return nil, err
} }
return model.UserGrantsToModel(grants), nil return model.UserGrantsToModel(grants, repo.PrefixAvatarURL), nil
} }
func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) { func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) {
@ -79,7 +80,7 @@ func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_
Offset: request.Offset, Offset: request.Offset,
Limit: request.Limit, Limit: request.Limit,
TotalResult: count, TotalResult: count,
Result: model.UserGrantsToModel(grants), Result: model.UserGrantsToModel(grants, repo.PrefixAvatarURL),
} }
if sequenceErr == nil { if sequenceErr == nil {
result.Sequence = sequence.CurrentSequence result.Sequence = sequence.CurrentSequence

View File

@ -131,7 +131,9 @@ func (m *OrgMember) processUser(event *es_models.Event) (err error) {
usr_es_model.UserEmailChanged, usr_es_model.UserEmailChanged,
usr_es_model.HumanProfileChanged, usr_es_model.HumanProfileChanged,
usr_es_model.HumanEmailChanged, usr_es_model.HumanEmailChanged,
usr_es_model.MachineChanged: usr_es_model.MachineChanged,
usr_es_model.HumanAvatarAdded,
usr_es_model.HumanAvatarRemoved:
members, err := m.view.OrgMembersByUserID(event.AggregateID) members, err := m.view.OrgMembersByUserID(event.AggregateID)
if err != nil { if err != nil {
return err return err
@ -164,6 +166,9 @@ func (m *OrgMember) fillData(member *org_view_model.OrgMemberView) (err error) {
func (m *OrgMember) fillUserData(member *org_view_model.OrgMemberView, user *usr_view_model.UserView) error { func (m *OrgMember) fillUserData(member *org_view_model.OrgMemberView, user *usr_view_model.UserView) error {
org, err := m.getOrgByID(context.Background(), user.ResourceOwner) org, err := m.getOrgByID(context.Background(), user.ResourceOwner)
if err != nil {
return err
}
policy := org.OrgIamPolicy policy := org.OrgIamPolicy
if policy == nil { if policy == nil {
policy, err = m.getDefaultOrgIAMPolicy(context.TODO()) policy, err = m.getDefaultOrgIAMPolicy(context.TODO())
@ -173,11 +178,13 @@ func (m *OrgMember) fillUserData(member *org_view_model.OrgMemberView, user *usr
} }
member.UserName = user.UserName member.UserName = user.UserName
member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
member.UserResourceOwner = user.ResourceOwner
if user.HumanView != nil { if user.HumanView != nil {
member.FirstName = user.FirstName member.FirstName = user.FirstName
member.LastName = user.LastName member.LastName = user.LastName
member.DisplayName = user.DisplayName member.DisplayName = user.DisplayName
member.Email = user.Email member.Email = user.Email
member.AvatarKey = user.AvatarKey
} }
if user.MachineView != nil { if user.MachineView != nil {
member.DisplayName = user.MachineView.Name member.DisplayName = user.MachineView.Name

View File

@ -2,6 +2,7 @@ package handler
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1"
@ -139,7 +140,9 @@ func (p *ProjectGrantMember) processUser(event *es_models.Event) (err error) {
usr_es_model.UserEmailChanged, usr_es_model.UserEmailChanged,
usr_es_model.HumanProfileChanged, usr_es_model.HumanProfileChanged,
usr_es_model.HumanEmailChanged, usr_es_model.HumanEmailChanged,
usr_es_model.MachineChanged: usr_es_model.MachineChanged,
usr_es_model.HumanAvatarAdded,
usr_es_model.HumanAvatarRemoved:
members, err := p.view.ProjectGrantMembersByUserID(event.AggregateID) members, err := p.view.ProjectGrantMembersByUserID(event.AggregateID)
if err != nil { if err != nil {
return err return err
@ -183,11 +186,13 @@ func (p *ProjectGrantMember) fillUserData(member *view_model.ProjectGrantMemberV
} }
member.UserName = user.UserName member.UserName = user.UserName
member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
member.UserResourceOwner = user.ResourceOwner
if user.HumanView != nil { if user.HumanView != nil {
member.FirstName = user.FirstName member.FirstName = user.FirstName
member.LastName = user.LastName member.LastName = user.LastName
member.DisplayName = user.DisplayName member.DisplayName = user.DisplayName
member.Email = user.Email member.Email = user.Email
member.AvatarKey = user.AvatarKey
} }
if user.MachineView != nil { if user.MachineView != nil {
member.DisplayName = user.MachineView.Name member.DisplayName = user.MachineView.Name

View File

@ -2,6 +2,7 @@ package handler
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1" "github.com/caos/zitadel/internal/eventstore/v1"
@ -134,7 +135,9 @@ func (p *ProjectMember) processUser(event *es_models.Event) (err error) {
usr_es_model.UserEmailChanged, usr_es_model.UserEmailChanged,
usr_es_model.HumanProfileChanged, usr_es_model.HumanProfileChanged,
usr_es_model.HumanEmailChanged, usr_es_model.HumanEmailChanged,
usr_es_model.MachineChanged: usr_es_model.MachineChanged,
usr_es_model.HumanAvatarAdded,
usr_es_model.HumanAvatarRemoved:
members, err := p.view.ProjectMembersByUserID(event.AggregateID) members, err := p.view.ProjectMembersByUserID(event.AggregateID)
if err != nil { if err != nil {
return err return err
@ -168,6 +171,9 @@ func (p *ProjectMember) fillData(member *view_model.ProjectMemberView) (err erro
func (p *ProjectMember) fillUserData(member *view_model.ProjectMemberView, user *usr_view_model.UserView) error { func (p *ProjectMember) fillUserData(member *view_model.ProjectMemberView, user *usr_view_model.UserView) error {
org, err := p.getOrgByID(context.Background(), user.ResourceOwner) org, err := p.getOrgByID(context.Background(), user.ResourceOwner)
if err != nil {
return err
}
policy := org.OrgIamPolicy policy := org.OrgIamPolicy
if policy == nil { if policy == nil {
policy, err = p.getDefaultOrgIAMPolicy(context.TODO()) policy, err = p.getDefaultOrgIAMPolicy(context.TODO())
@ -177,11 +183,13 @@ func (p *ProjectMember) fillUserData(member *view_model.ProjectMemberView, user
} }
member.UserName = user.UserName member.UserName = user.UserName
member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
member.UserResourceOwner = user.ResourceOwner
if user.HumanView != nil { if user.HumanView != nil {
member.FirstName = user.FirstName member.FirstName = user.FirstName
member.LastName = user.LastName member.LastName = user.LastName
member.Email = user.Email member.Email = user.Email
member.DisplayName = user.DisplayName member.DisplayName = user.DisplayName
member.AvatarKey = user.AvatarKey
} }
if user.MachineView != nil { if user.MachineView != nil {
member.DisplayName = user.MachineView.Name member.DisplayName = user.MachineView.Name

View File

@ -129,7 +129,9 @@ func (u *UserGrant) processUser(event *es_models.Event) (err error) {
usr_es_model.UserEmailChanged, usr_es_model.UserEmailChanged,
usr_es_model.HumanProfileChanged, usr_es_model.HumanProfileChanged,
usr_es_model.HumanEmailChanged, usr_es_model.HumanEmailChanged,
usr_es_model.MachineChanged: usr_es_model.MachineChanged,
usr_es_model.HumanAvatarAdded,
usr_es_model.HumanAvatarRemoved:
grants, err := u.view.UserGrantsByUserID(event.AggregateID) grants, err := u.view.UserGrantsByUserID(event.AggregateID)
if err != nil { if err != nil {
return err return err
@ -218,11 +220,13 @@ func (u *UserGrant) fillData(grant *view_model.UserGrantView, resourceOwner stri
func (u *UserGrant) fillUserData(grant *view_model.UserGrantView, user *usr_view_model.UserView) { func (u *UserGrant) fillUserData(grant *view_model.UserGrantView, user *usr_view_model.UserView) {
grant.UserName = user.UserName grant.UserName = user.UserName
grant.UserResourceOwner = user.ResourceOwner
if user.HumanView != nil { if user.HumanView != nil {
grant.FirstName = user.FirstName grant.FirstName = user.FirstName
grant.LastName = user.LastName grant.LastName = user.LastName
grant.DisplayName = user.FirstName + " " + user.LastName grant.DisplayName = user.FirstName + " " + user.LastName
grant.Email = user.Email grant.Email = user.Email
grant.AvatarKey = user.AvatarKey
} }
if user.MachineView != nil { if user.MachineView != nil {
grant.DisplayName = user.MachineView.Name grant.DisplayName = user.MachineView.Name

View File

@ -16,6 +16,7 @@ import (
type Config struct { type Config struct {
SearchLimit uint64 SearchLimit uint64
Domain string Domain string
APIDomain string
Eventstore v1.Config Eventstore v1.Config
View types.SQL View types.SQL
Spooler spooler.SpoolerConfig Spooler spooler.SpoolerConfig
@ -49,13 +50,14 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, roles []string, querie
} }
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, systemDefaults, staticStorage) spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, systemDefaults, staticStorage)
assetsAPI := conf.APIDomain + "/assets/v1/"
return &EsRepository{ return &EsRepository{
spooler: spool, spooler: spool,
OrgRepository: eventstore.OrgRepository{conf.SearchLimit, es, view, roles, systemDefaults}, OrgRepository: eventstore.OrgRepository{conf.SearchLimit, es, view, roles, systemDefaults, assetsAPI},
ProjectRepo: eventstore.ProjectRepo{es, conf.SearchLimit, view, roles, systemDefaults.IamID}, ProjectRepo: eventstore.ProjectRepo{es, conf.SearchLimit, view, roles, systemDefaults.IamID, assetsAPI},
UserRepo: eventstore.UserRepo{es, conf.SearchLimit, view, systemDefaults}, UserRepo: eventstore.UserRepo{es, conf.SearchLimit, view, systemDefaults, assetsAPI},
UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, view}, UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, view, assetsAPI},
IAMRepository: eventstore.IAMRepository{IAMV2Query: queries}, IAMRepository: eventstore.IAMRepository{IAMV2Query: queries},
FeaturesRepo: eventstore.FeaturesRepo{es, view, conf.SearchLimit, systemDefaults}, FeaturesRepo: eventstore.FeaturesRepo{es, view, conf.SearchLimit, systemDefaults},
view: view, view: view,

View File

@ -16,6 +16,8 @@ type OrgMemberView struct {
LastName string LastName string
DisplayName string DisplayName string
PreferredLoginName string PreferredLoginName string
AvatarURL string
UserResourceOwner string
Roles []string Roles []string
CreationDate time.Time CreationDate time.Time
ChangeDate time.Time ChangeDate time.Time

View File

@ -4,13 +4,14 @@ import (
"encoding/json" "encoding/json"
"time" "time"
es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/lib/pq"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/org/model" "github.com/caos/zitadel/internal/org/model"
"github.com/lib/pq" es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
) )
const ( const (
@ -33,12 +34,14 @@ type OrgMemberView struct {
Roles pq.StringArray `json:"roles" gorm:"column:roles"` Roles pq.StringArray `json:"roles" gorm:"column:roles"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
AvatarKey string `json:"-" gorm:"column:avatar_key"`
UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`
} }
func OrgMemberToModel(member *OrgMemberView) *model.OrgMemberView { func OrgMemberToModel(member *OrgMemberView, prefixAvatarURL string) *model.OrgMemberView {
return &model.OrgMemberView{ return &model.OrgMemberView{
UserID: member.UserID, UserID: member.UserID,
OrgID: member.OrgID, OrgID: member.OrgID,
@ -49,16 +52,18 @@ func OrgMemberToModel(member *OrgMemberView) *model.OrgMemberView {
DisplayName: member.DisplayName, DisplayName: member.DisplayName,
PreferredLoginName: member.PreferredLoginName, PreferredLoginName: member.PreferredLoginName,
Roles: member.Roles, Roles: member.Roles,
AvatarURL: domain.AvatarURL(prefixAvatarURL, member.UserResourceOwner, member.AvatarKey),
UserResourceOwner: member.UserResourceOwner,
Sequence: member.Sequence, Sequence: member.Sequence,
CreationDate: member.CreationDate, CreationDate: member.CreationDate,
ChangeDate: member.ChangeDate, ChangeDate: member.ChangeDate,
} }
} }
func OrgMembersToModel(roles []*OrgMemberView) []*model.OrgMemberView { func OrgMembersToModel(roles []*OrgMemberView, prefixAvatarURL string) []*model.OrgMemberView {
result := make([]*model.OrgMemberView, len(roles)) result := make([]*model.OrgMemberView, len(roles))
for i, r := range roles { for i, r := range roles {
result[i] = OrgMemberToModel(r) result[i] = OrgMemberToModel(r, prefixAvatarURL)
} }
return result return result
} }

View File

@ -17,6 +17,8 @@ type ProjectGrantMemberView struct {
LastName string LastName string
DisplayName string DisplayName string
PreferredLoginName string PreferredLoginName string
AvatarURL string
UserResourceOwner string
Roles []string Roles []string
CreationDate time.Time CreationDate time.Time
ChangeDate time.Time ChangeDate time.Time

View File

@ -16,6 +16,8 @@ type ProjectMemberView struct {
LastName string LastName string
DisplayName string DisplayName string
PreferredLoginName string PreferredLoginName string
AvatarURL string
UserResourceOwner string
Roles []string Roles []string
CreationDate time.Time CreationDate time.Time
ChangeDate time.Time ChangeDate time.Time

View File

@ -7,6 +7,7 @@ import (
"github.com/caos/logging" "github.com/caos/logging"
"github.com/lib/pq" "github.com/lib/pq"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/model"
@ -35,12 +36,14 @@ type ProjectGrantMemberView struct {
Roles pq.StringArray `json:"roles" gorm:"column:roles"` Roles pq.StringArray `json:"roles" gorm:"column:roles"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
AvatarKey string `json:"-" gorm:"column:avatar_key"`
UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`
} }
func ProjectGrantMemberToModel(member *ProjectGrantMemberView) *model.ProjectGrantMemberView { func ProjectGrantMemberToModel(member *ProjectGrantMemberView, prefixAvatarURL string) *model.ProjectGrantMemberView {
return &model.ProjectGrantMemberView{ return &model.ProjectGrantMemberView{
UserID: member.UserID, UserID: member.UserID,
GrantID: member.GrantID, GrantID: member.GrantID,
@ -51,6 +54,8 @@ func ProjectGrantMemberToModel(member *ProjectGrantMemberView) *model.ProjectGra
LastName: member.LastName, LastName: member.LastName,
DisplayName: member.DisplayName, DisplayName: member.DisplayName,
PreferredLoginName: member.PreferredLoginName, PreferredLoginName: member.PreferredLoginName,
AvatarURL: domain.AvatarURL(prefixAvatarURL, member.UserResourceOwner, member.AvatarKey),
UserResourceOwner: member.UserResourceOwner,
Roles: member.Roles, Roles: member.Roles,
Sequence: member.Sequence, Sequence: member.Sequence,
CreationDate: member.CreationDate, CreationDate: member.CreationDate,
@ -58,10 +63,10 @@ func ProjectGrantMemberToModel(member *ProjectGrantMemberView) *model.ProjectGra
} }
} }
func ProjectGrantMembersToModel(roles []*ProjectGrantMemberView) []*model.ProjectGrantMemberView { func ProjectGrantMembersToModel(roles []*ProjectGrantMemberView, prefixAvatarURL string) []*model.ProjectGrantMemberView {
result := make([]*model.ProjectGrantMemberView, len(roles)) result := make([]*model.ProjectGrantMemberView, len(roles))
for i, r := range roles { for i, r := range roles {
result[i] = ProjectGrantMemberToModel(r) result[i] = ProjectGrantMemberToModel(r, prefixAvatarURL)
} }
return result return result
} }

View File

@ -5,11 +5,13 @@ import (
"time" "time"
"github.com/caos/logging" "github.com/caos/logging"
"github.com/lib/pq"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/model"
es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
"github.com/lib/pq"
) )
const ( const (
@ -32,31 +34,36 @@ type ProjectMemberView struct {
Roles pq.StringArray `json:"roles" gorm:"column:roles"` Roles pq.StringArray `json:"roles" gorm:"column:roles"`
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"`
AvatarKey string `json:"-" gorm:"column:avatar_key"`
UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`
} }
func ProjectMemberToModel(member *ProjectMemberView) *model.ProjectMemberView { func ProjectMemberToModel(member *ProjectMemberView, prefixAvatarURL string) *model.ProjectMemberView {
return &model.ProjectMemberView{ return &model.ProjectMemberView{
UserID: member.UserID, UserID: member.UserID,
ProjectID: member.ProjectID, ProjectID: member.ProjectID,
UserName: member.UserName, UserName: member.UserName,
Email: member.Email, Email: member.Email,
FirstName: member.FirstName, FirstName: member.FirstName,
LastName: member.LastName, LastName: member.LastName,
DisplayName: member.DisplayName, DisplayName: member.DisplayName,
Roles: member.Roles, PreferredLoginName: member.PreferredLoginName,
Sequence: member.Sequence, AvatarURL: domain.AvatarURL(prefixAvatarURL, member.UserResourceOwner, member.AvatarKey),
CreationDate: member.CreationDate, UserResourceOwner: member.UserResourceOwner,
ChangeDate: member.ChangeDate, Roles: member.Roles,
Sequence: member.Sequence,
CreationDate: member.CreationDate,
ChangeDate: member.ChangeDate,
} }
} }
func ProjectMembersToModel(roles []*ProjectMemberView) []*model.ProjectMemberView { func ProjectMembersToModel(roles []*ProjectMemberView, prefixAvatarURL string) []*model.ProjectMemberView {
result := make([]*model.ProjectMemberView, len(roles)) result := make([]*model.ProjectMemberView, len(roles))
for i, r := range roles { for i, r := range roles {
result[i] = ProjectMemberToModel(r) result[i] = ProjectMemberToModel(r, prefixAvatarURL)
} }
return result return result
} }

View File

@ -24,7 +24,6 @@
<div class="lgn-account-selection"> <div class="lgn-account-selection">
{{ if .Users }} {{ if .Users }}
{{ $displayLoginNameSuffix := and .OrgID (not .DisplayLoginNameSuffix)}} {{ $displayLoginNameSuffix := and .OrgID (not .DisplayLoginNameSuffix)}}
{{ $orgID := .OrgID }}
{{ range $user := .Users }} {{ range $user := .Users }}
{{ $sessionState := (printf "UserSelection.SessionState%v" $user.UserSessionState) }} {{ $sessionState := (printf "UserSelection.SessionState%v" $user.UserSessionState) }}
<button type="submit" name="userID" value="{{$user.UserID}}" class="lgn-account" <button type="submit" name="userID" value="{{$user.UserID}}" class="lgn-account"
@ -32,7 +31,7 @@
<div class="left"> <div class="left">
<div class="lgn-avatar" {{if not $user.AvatarKey}}loginname="{{$user.LoginName}}"{{end}}> <div class="lgn-avatar" {{if not $user.AvatarKey}}loginname="{{$user.LoginName}}"{{end}}>
{{if $user.AvatarKey}} {{if $user.AvatarKey}}
<img class="avatar-img" src="{{ avatarResource $orgID $user.AvatarKey }}" alt="user-avatar"> <img class="avatar-img" src="{{ avatarResource $user.ResourceOwner $user.AvatarKey }}" alt="user-avatar">
{{else}} {{else}}
<span class="initials">A</span> <span class="initials">A</span>
{{end}} {{end}}

View File

@ -17,7 +17,7 @@ type Profile struct {
Gender Gender Gender Gender
PreferredLoginName string PreferredLoginName string
LoginNames []string LoginNames []string
Avatar string AvatarURL string
} }
func (p *Profile) IsValid() bool { func (p *Profile) IsValid() bool {

View File

@ -20,6 +20,7 @@ type UserSessionView struct {
LoginName string LoginName string
DisplayName string DisplayName string
AvatarKey string AvatarKey string
AvatarURL string
SelectedIDPConfigID string SelectedIDPConfigID string
PasswordVerification time.Time PasswordVerification time.Time
PasswordlessVerification time.Time PasswordlessVerification time.Time

View File

@ -41,6 +41,7 @@ type HumanView struct {
NickName string NickName string
DisplayName string DisplayName string
AvatarKey string AvatarKey string
AvatarURL string
PreSignedAvatar *url.URL PreSignedAvatar *url.URL
PreferredLanguage string PreferredLanguage string
Gender Gender Gender Gender
@ -251,6 +252,7 @@ func (u *UserView) GetProfile() (*Profile, error) {
Gender: u.Gender, Gender: u.Gender,
PreferredLoginName: u.PreferredLoginName, PreferredLoginName: u.PreferredLoginName,
LoginNames: u.LoginNames, LoginNames: u.LoginNames,
AvatarURL: u.AvatarURL,
}, nil }, nil
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/lib/pq" "github.com/lib/pq"
req_model "github.com/caos/zitadel/internal/auth_request/model" req_model "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
iam_model "github.com/caos/zitadel/internal/iam/model" iam_model "github.com/caos/zitadel/internal/iam/model"
@ -135,7 +136,7 @@ func (m *MachineView) IsZero() bool {
return m == nil || m.Name == "" return m == nil || m.Name == ""
} }
func UserToModel(user *UserView) *model.UserView { func UserToModel(user *UserView, prefixAvatarURL string) *model.UserView {
userView := &model.UserView{ userView := &model.UserView{
ID: user.ID, ID: user.ID,
UserName: user.UserName, UserName: user.UserName,
@ -160,6 +161,7 @@ func UserToModel(user *UserView) *model.UserView {
NickName: user.NickName, NickName: user.NickName,
DisplayName: user.DisplayName, DisplayName: user.DisplayName,
AvatarKey: user.AvatarKey, AvatarKey: user.AvatarKey,
AvatarURL: domain.AvatarURL(prefixAvatarURL, user.ResourceOwner, user.AvatarKey),
PreferredLanguage: user.PreferredLanguage, PreferredLanguage: user.PreferredLanguage,
Gender: model.Gender(user.Gender), Gender: model.Gender(user.Gender),
Email: user.Email, Email: user.Email,
@ -187,10 +189,10 @@ func UserToModel(user *UserView) *model.UserView {
return userView return userView
} }
func UsersToModel(users []*UserView) []*model.UserView { func UsersToModel(users []*UserView, prefixAvatarURL string) []*model.UserView {
result := make([]*model.UserView, len(users)) result := make([]*model.UserView, len(users))
for i, p := range users { for i, p := range users {
result[i] = UserToModel(p) result[i] = UserToModel(p, prefixAvatarURL)
} }
return result return result
} }
@ -340,7 +342,7 @@ func (u *UserView) AppendEvent(event *models.Event) (err error) {
es_model.InitializedHumanCheckSucceeded: es_model.InitializedHumanCheckSucceeded:
u.InitRequired = false u.InitRequired = false
case es_model.HumanAvatarAdded: case es_model.HumanAvatarAdded:
u.setData(event) err = u.setData(event)
case es_model.HumanAvatarRemoved: case es_model.HumanAvatarRemoved:
u.AvatarKey = "" u.AvatarKey = ""
} }

View File

@ -7,6 +7,7 @@ import (
"github.com/caos/logging" "github.com/caos/logging"
req_model "github.com/caos/zitadel/internal/auth_request/model" req_model "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/model"
@ -51,7 +52,7 @@ func UserSessionFromEvent(event *models.Event) (*UserSessionView, error) {
return v, nil return v, nil
} }
func UserSessionToModel(userSession *UserSessionView) *model.UserSessionView { func UserSessionToModel(userSession *UserSessionView, prefixAvatarURL string) *model.UserSessionView {
return &model.UserSessionView{ return &model.UserSessionView{
ChangeDate: userSession.ChangeDate, ChangeDate: userSession.ChangeDate,
CreationDate: userSession.CreationDate, CreationDate: userSession.CreationDate,
@ -63,6 +64,7 @@ func UserSessionToModel(userSession *UserSessionView) *model.UserSessionView {
LoginName: userSession.LoginName, LoginName: userSession.LoginName,
DisplayName: userSession.DisplayName, DisplayName: userSession.DisplayName,
AvatarKey: userSession.AvatarKey, AvatarKey: userSession.AvatarKey,
AvatarURL: domain.AvatarURL(prefixAvatarURL, userSession.ResourceOwner, userSession.AvatarKey),
SelectedIDPConfigID: userSession.SelectedIDPConfigID, SelectedIDPConfigID: userSession.SelectedIDPConfigID,
PasswordVerification: userSession.PasswordVerification, PasswordVerification: userSession.PasswordVerification,
PasswordlessVerification: userSession.PasswordlessVerification, PasswordlessVerification: userSession.PasswordlessVerification,
@ -75,10 +77,10 @@ func UserSessionToModel(userSession *UserSessionView) *model.UserSessionView {
} }
} }
func UserSessionsToModel(userSessions []*UserSessionView) []*model.UserSessionView { func UserSessionsToModel(userSessions []*UserSessionView, prefixAvatarURL string) []*model.UserSessionView {
result := make([]*model.UserSessionView, len(userSessions)) result := make([]*model.UserSessionView, len(userSessions))
for i, s := range userSessions { for i, s := range userSessions {
result[i] = UserSessionToModel(s) result[i] = UserSessionToModel(s, prefixAvatarURL)
} }
return result return result
} }

View File

@ -21,6 +21,7 @@ type UserGrantView struct {
ProjectName string ProjectName string
OrgName string OrgName string
OrgPrimaryDomain string OrgPrimaryDomain string
AvatarURL string
RoleKeys []string RoleKeys []string
CreationDate time.Time CreationDate time.Time

View File

@ -7,6 +7,7 @@ import (
"github.com/caos/logging" "github.com/caos/logging"
"github.com/lib/pq" "github.com/lib/pq"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/usergrant/model" "github.com/caos/zitadel/internal/usergrant/model"
@ -32,21 +33,23 @@ const (
) )
type UserGrantView struct { type UserGrantView struct {
ID string `json:"-" gorm:"column:id;primary_key"` ID string `json:"-" gorm:"column:id;primary_key"`
ResourceOwner string `json:"-" gorm:"resource_owner"` ResourceOwner string `json:"-" gorm:"resource_owner"`
UserID string `json:"userId" gorm:"user_id"` UserID string `json:"userId" gorm:"user_id"`
ProjectID string `json:"projectId" gorm:"column:project_id"` ProjectID string `json:"projectId" gorm:"column:project_id"`
GrantID string `json:"grantId" gorm:"column:grant_id"` GrantID string `json:"grantId" gorm:"column:grant_id"`
UserName string `json:"-" gorm:"column:user_name"` UserName string `json:"-" gorm:"column:user_name"`
FirstName string `json:"-" gorm:"column:first_name"` FirstName string `json:"-" gorm:"column:first_name"`
LastName string `json:"-" gorm:"column:last_name"` LastName string `json:"-" gorm:"column:last_name"`
DisplayName string `json:"-" gorm:"column:display_name"` DisplayName string `json:"-" gorm:"column:display_name"`
Email string `json:"-" gorm:"column:email"` Email string `json:"-" gorm:"column:email"`
ProjectName string `json:"-" gorm:"column:project_name"` ProjectName string `json:"-" gorm:"column:project_name"`
ProjectOwner string `json:"-" gorm:"column:project_owner"` ProjectOwner string `json:"-" gorm:"column:project_owner"`
OrgName string `json:"-" gorm:"column:org_name"` OrgName string `json:"-" gorm:"column:org_name"`
OrgPrimaryDomain string `json:"-" gorm:"column:org_primary_domain"` OrgPrimaryDomain string `json:"-" gorm:"column:org_primary_domain"`
RoleKeys pq.StringArray `json:"roleKeys" gorm:"column:role_keys"` RoleKeys pq.StringArray `json:"roleKeys" gorm:"column:role_keys"`
AvatarKey string `json:"-" gorm:"column:avatar_key"`
UserResourceOwner string `json:"-" gorm:"column:user_resource_owner"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"` CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"` ChangeDate time.Time `json:"-" gorm:"column:change_date"`
@ -55,7 +58,7 @@ type UserGrantView struct {
Sequence uint64 `json:"-" gorm:"column:sequence"` Sequence uint64 `json:"-" gorm:"column:sequence"`
} }
func UserGrantToModel(grant *UserGrantView) *model.UserGrantView { func UserGrantToModel(grant *UserGrantView, prefixAvatarURL string) *model.UserGrantView {
return &model.UserGrantView{ return &model.UserGrantView{
ID: grant.ID, ID: grant.ID,
ResourceOwner: grant.ResourceOwner, ResourceOwner: grant.ResourceOwner,
@ -73,15 +76,16 @@ func UserGrantToModel(grant *UserGrantView) *model.UserGrantView {
OrgName: grant.OrgName, OrgName: grant.OrgName,
OrgPrimaryDomain: grant.OrgPrimaryDomain, OrgPrimaryDomain: grant.OrgPrimaryDomain,
RoleKeys: grant.RoleKeys, RoleKeys: grant.RoleKeys,
AvatarURL: domain.AvatarURL(prefixAvatarURL, grant.ResourceOwner, grant.AvatarKey),
Sequence: grant.Sequence, Sequence: grant.Sequence,
GrantID: grant.GrantID, GrantID: grant.GrantID,
} }
} }
func UserGrantsToModel(grants []*UserGrantView) []*model.UserGrantView { func UserGrantsToModel(grants []*UserGrantView, prefixAvatarURL string) []*model.UserGrantView {
result := make([]*model.UserGrantView, len(grants)) result := make([]*model.UserGrantView, len(grants))
for i, g := range grants { for i, g := range grants {
result[i] = UserGrantToModel(g) result[i] = UserGrantToModel(g, prefixAvatarURL)
} }
return result return result
} }

View File

@ -0,0 +1,16 @@
ALTER TABLE adminapi.iam_members ADD COLUMN avatar_key TEXT;
ALTER TABLE auth.user_grants ADD COLUMN avatar_key TEXT;
ALTER TABLE authz.user_grants ADD COLUMN avatar_key TEXT;
ALTER TABLE management.org_members ADD COLUMN avatar_key TEXT;
ALTER TABLE management.project_members ADD COLUMN avatar_key TEXT;
ALTER TABLE management.project_grant_members ADD COLUMN avatar_key TEXT;
ALTER TABLE management.user_grants ADD COLUMN avatar_key TEXT;
ALTER TABLE adminapi.iam_members ADD COLUMN user_resource_owner TEXT;
ALTER TABLE management.org_members ADD COLUMN user_resource_owner TEXT;
ALTER TABLE management.project_members ADD COLUMN user_resource_owner TEXT;
ALTER TABLE management.project_grant_members ADD COLUMN user_resource_owner TEXT;
ALTER TABLE auth.user_grants ADD COLUMN user_resource_owner TEXT;
ALTER TABLE authz.user_grants ADD COLUMN user_resource_owner TEXT;
ALTER TABLE management.user_grants ADD COLUMN user_resource_owner TEXT;

View File

@ -51,6 +51,12 @@ message Member {
example: "\"Gigi Giraffe\""; example: "\"Gigi Giraffe\"";
} }
]; ];
string avatar_url = 9 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "avatar url of the user"
example: "\"https://api.zitadel.ch/assets/v1/avatar-32432jkh4kj32\"";
}
];
} }
message SearchQuery { message SearchQuery {

View File

@ -113,6 +113,12 @@ message Profile {
description: "the gender of the human"; description: "the gender of the human";
} }
]; ];
string avatar_url = 7 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "avatar url of the user"
example: "\"https://api.zitadel.ch/assets/v1/avatar-32432jkh4kj32\"";
}
];
} }
message Email { message Email {
@ -509,6 +515,12 @@ message Session {
} }
]; ];
zitadel.v1.ObjectDetails details = 9; zitadel.v1.ObjectDetails details = 9;
string avatar_url = 10 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "avatar url of the user"
example: "\"https://api.zitadel.ch/assets/v1/avatar-32432jkh4kj32\"";
}
];
} }
enum SessionState { enum SessionState {
@ -641,6 +653,12 @@ message UserGrant {
example: "\"69629023906488334\"" example: "\"69629023906488334\""
} }
]; ];
string avatar_url = 17 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "avatar url of the user"
example: "\"https://api.zitadel.ch/assets/v1/avatar-32432jkh4kj32\"";
}
];
} }
enum UserGrantState { enum UserGrantState {