From 770994e1437dd19377fa31e1e6ba148d728f8d74 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 11 Jun 2021 13:20:39 +0200 Subject: [PATCH] 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 --- cmd/zitadel/startup.yaml | 3 ++ docs/docs/apis/proto/member.md | 1 + docs/docs/apis/proto/user.md | 3 ++ go.sum | 1 - .../eventsourcing/eventstore/iam.go | 17 ++++---- .../eventsourcing/eventstore/user.go | 11 ++--- .../eventsourcing/handler/iam_member.go | 9 ++++- .../repository/eventsourcing/repository.go | 23 ++++++----- internal/api/grpc/member/iam_member.go | 1 + internal/api/grpc/member/org_member.go | 1 + .../api/grpc/member/project_grant_member.go | 1 + internal/api/grpc/member/project_member.go | 1 + internal/api/grpc/user/converter.go | 2 + internal/api/grpc/user/session.go | 1 + internal/api/grpc/user/user_grant.go | 1 + internal/api/oidc/client.go | 1 + .../eventsourcing/eventstore/auth_request.go | 22 +++++----- .../eventstore/auth_request_test.go | 31 ++++++++++++-- .../eventsourcing/eventstore/user.go | 23 ++++++----- .../eventsourcing/eventstore/user_grant.go | 15 +++---- .../eventsourcing/eventstore/user_session.go | 2 +- .../eventsourcing/handler/user_grant.go | 9 ++++- .../repository/eventsourcing/repository.go | 14 ++++--- .../repository/eventsourcing/view/view.go | 20 ++++++---- internal/domain/human_profile.go | 10 ++++- internal/domain/next_step.go | 1 + internal/iam/model/iam_member_view.go | 2 + .../eventsourcing/model/label_policy.go | 18 +-------- .../iam/repository/view/model/iam_member.go | 33 +++++---------- .../eventsourcing/eventstore/org.go | 21 +++++----- .../eventsourcing/eventstore/project.go | 23 ++++++----- .../eventsourcing/eventstore/user.go | 17 ++++---- .../eventsourcing/eventstore/user_grant.go | 17 ++++---- .../eventsourcing/handler/org_member.go | 9 ++++- .../handler/project_grant_member.go | 7 +++- .../eventsourcing/handler/project_member.go | 10 ++++- .../eventsourcing/handler/user_grant.go | 6 ++- .../repository/eventsourcing/repository.go | 10 +++-- internal/org/model/org_member_view.go | 2 + .../org/repository/view/model/org_member.go | 17 +++++--- .../model/project_grant_member_view.go | 2 + internal/project/model/project_member_view.go | 2 + .../view/model/project_grant_member.go | 11 +++-- .../repository/view/model/project_member.go | 37 ++++++++++------- .../login/static/templates/select_user.html | 3 +- internal/user/model/profile.go | 2 +- internal/user/model/user_session_view.go | 1 + internal/user/model/user_view.go | 2 + internal/user/repository/view/model/user.go | 10 +++-- .../repository/view/model/user_session.go | 8 ++-- internal/usergrant/model/user_grant_view.go | 1 + .../repository/view/model/user_grant.go | 40 ++++++++++--------- migrations/cockroach/V1.49__avatar.sql | 16 ++++++++ proto/zitadel/member.proto | 6 +++ proto/zitadel/user.proto | 18 +++++++++ 55 files changed, 368 insertions(+), 207 deletions(-) create mode 100644 migrations/cockroach/V1.49__avatar.sql diff --git a/cmd/zitadel/startup.yaml b/cmd/zitadel/startup.yaml index 7e0d4ac384..f532287638 100644 --- a/cmd/zitadel/startup.yaml +++ b/cmd/zitadel/startup.yaml @@ -90,6 +90,7 @@ AuthZ: Auth: SearchLimit: 1000 Domain: $ZITADEL_DEFAULT_DOMAIN + APIDomain: $ZITADEL_API_DOMAIN Eventstore: ServiceName: 'authAPI' Repository: @@ -139,6 +140,7 @@ Auth: Admin: SearchLimit: 1000 Domain: $ZITADEL_DEFAULT_DOMAIN + APIDomain: $ZITADEL_API_DOMAIN Eventstore: ServiceName: 'Admin' Repository: @@ -176,6 +178,7 @@ Admin: Mgmt: SearchLimit: 1000 Domain: $ZITADEL_DEFAULT_DOMAIN + APIDomain: $ZITADEL_API_DOMAIN Eventstore: ServiceName: 'ManagementAPI' Repository: diff --git a/docs/docs/apis/proto/member.md b/docs/docs/apis/proto/member.md index 2fa36d34de..3d5bfe5ea8 100644 --- a/docs/docs/apis/proto/member.md +++ b/docs/docs/apis/proto/member.md @@ -59,6 +59,7 @@ title: zitadel/member.proto | first_name | string | - | | | last_name | string | - | | | display_name | string | - | | +| avatar_url | string | - | | diff --git a/docs/docs/apis/proto/user.md b/docs/docs/apis/proto/user.md index e4d841e12b..a3c7e05969 100644 --- a/docs/docs/apis/proto/user.md +++ b/docs/docs/apis/proto/user.md @@ -237,6 +237,7 @@ this query is always equals | display_name | string | - | | | preferred_language | string | - | | | gender | Gender | - | | +| avatar_url | string | - | | @@ -291,6 +292,7 @@ this query is always equals | login_name | string | - | | | display_name | string | - | | | details | zitadel.v1.ObjectDetails | - | | +| avatar_url | string | - | | @@ -357,6 +359,7 @@ UserTypeQuery is always equals | project_id | string | - | | | project_name | string | - | | | project_grant_id | string | - | | +| avatar_url | string | - | | diff --git a/go.sum b/go.sum index 8d109b628b..0c6d0291c2 100644 --- a/go.sum +++ b/go.sum @@ -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-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-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-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/internal/admin/repository/eventsourcing/eventstore/iam.go b/internal/admin/repository/eventsourcing/eventstore/iam.go index 670809d271..e6446209b1 100644 --- a/internal/admin/repository/eventsourcing/eventstore/iam.go +++ b/internal/admin/repository/eventsourcing/eventstore/iam.go @@ -23,11 +23,12 @@ import ( ) type IAMRepository struct { - Eventstore v1.Eventstore - SearchLimit uint64 - View *admin_view.View - SystemDefaults systemdefaults.SystemDefaults - Roles []string + Eventstore v1.Eventstore + SearchLimit uint64 + View *admin_view.View + SystemDefaults systemdefaults.SystemDefaults + Roles []string + PrefixAvatarURL string } 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 { 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) { @@ -53,7 +54,7 @@ func (repo *IAMRepository) SearchIAMMembers(ctx context.Context, request *iam_mo Offset: request.Offset, Limit: request.Limit, TotalResult: count, - Result: iam_es_model.IAMMembersToModel(members), + Result: iam_es_model.IAMMembersToModel(members, repo.PrefixAvatarURL), } if err == nil { result.Sequence = sequence.CurrentSequence @@ -340,7 +341,7 @@ func (repo *IAMRepository) SearchIAMMembersx(ctx context.Context, request *iam_m Offset: request.Offset, Limit: request.Limit, TotalResult: count, - Result: iam_es_model.IAMMembersToModel(members), + Result: iam_es_model.IAMMembersToModel(members, repo.PrefixAvatarURL), } if err == nil { result.Sequence = sequence.CurrentSequence diff --git a/internal/admin/repository/eventsourcing/eventstore/user.go b/internal/admin/repository/eventsourcing/eventstore/user.go index 90a434ddca..b9b13ef05f 100644 --- a/internal/admin/repository/eventsourcing/eventstore/user.go +++ b/internal/admin/repository/eventsourcing/eventstore/user.go @@ -13,10 +13,11 @@ import ( ) type UserRepo struct { - SearchLimit uint64 - Eventstore v1.Eventstore - View *view.View - SystemDefaults systemdefaults.SystemDefaults + SearchLimit uint64 + Eventstore v1.Eventstore + View *view.View + SystemDefaults systemdefaults.SystemDefaults + PrefixAvatarURL string } 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, Limit: request.Limit, TotalResult: count, - Result: usr_view_model.UsersToModel(users), + Result: usr_view_model.UsersToModel(users, repo.PrefixAvatarURL), } if sequenceErr == nil { result.Sequence = sequence.CurrentSequence diff --git a/internal/admin/repository/eventsourcing/handler/iam_member.go b/internal/admin/repository/eventsourcing/handler/iam_member.go index a3cb908bba..10ccb7fd54 100644 --- a/internal/admin/repository/eventsourcing/handler/iam_member.go +++ b/internal/admin/repository/eventsourcing/handler/iam_member.go @@ -132,7 +132,9 @@ func (m *IAMMember) processUser(event *es_models.Event) (err error) { usr_es_model.UserEmailChanged, usr_es_model.HumanProfileChanged, 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) if err != nil { 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 { org, err := m.getOrgByID(context.Background(), user.ResourceOwner) + if err != nil { + return err + } policy := org.OrgIamPolicy if policy == nil { 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.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) + member.UserResourceOwner = user.ResourceOwner if user.HumanView != nil { member.FirstName = user.FirstName member.LastName = user.LastName member.DisplayName = user.FirstName + " " + user.LastName member.Email = user.Email + member.AvatarKey = user.AvatarKey } if user.MachineView != nil { member.DisplayName = user.MachineView.Name diff --git a/internal/admin/repository/eventsourcing/repository.go b/internal/admin/repository/eventsourcing/repository.go index b3806f35c6..be7d5ae86d 100644 --- a/internal/admin/repository/eventsourcing/repository.go +++ b/internal/admin/repository/eventsourcing/repository.go @@ -2,6 +2,7 @@ package eventsourcing import ( "context" + "github.com/caos/zitadel/internal/admin/repository/eventsourcing/eventstore" "github.com/caos/zitadel/internal/admin/repository/eventsourcing/spooler" admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" @@ -18,6 +19,7 @@ type Config struct { View types.SQL Spooler spooler.SpoolerConfig Domain string + APIDomain string } 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) + assetsAPI := conf.APIDomain + "/assets/v1/" return &EsRepository{ spooler: spool, @@ -54,11 +57,12 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, s SystemDefaults: systemDefaults, }, IAMRepository: eventstore.IAMRepository{ - Eventstore: es, - View: view, - SystemDefaults: systemDefaults, - SearchLimit: conf.SearchLimit, - Roles: roles, + Eventstore: es, + View: view, + SystemDefaults: systemDefaults, + SearchLimit: conf.SearchLimit, + Roles: roles, + PrefixAvatarURL: assetsAPI, }, AdministratorRepo: eventstore.AdministratorRepo{ View: view, @@ -70,10 +74,11 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, s SystemDefaults: systemDefaults, }, UserRepo: eventstore.UserRepo{ - Eventstore: es, - View: view, - SearchLimit: conf.SearchLimit, - SystemDefaults: systemDefaults, + Eventstore: es, + View: view, + SearchLimit: conf.SearchLimit, + SystemDefaults: systemDefaults, + PrefixAvatarURL: assetsAPI, }, }, nil } diff --git a/internal/api/grpc/member/iam_member.go b/internal/api/grpc/member/iam_member.go index 67f0527414..a1c716d105 100644 --- a/internal/api/grpc/member/iam_member.go +++ b/internal/api/grpc/member/iam_member.go @@ -24,6 +24,7 @@ func IAMMemberToPb(m *iam_model.IAMMemberView) *member_pb.Member { FirstName: m.FirstName, LastName: m.LastName, DisplayName: m.DisplayName, + AvatarUrl: m.AvatarURL, Details: object.ToViewDetailsPb( m.Sequence, m.CreationDate, diff --git a/internal/api/grpc/member/org_member.go b/internal/api/grpc/member/org_member.go index 3c613eaf5f..a5f2b434c6 100644 --- a/internal/api/grpc/member/org_member.go +++ b/internal/api/grpc/member/org_member.go @@ -24,6 +24,7 @@ func OrgMemberToPb(m *org_model.OrgMemberView) *member_pb.Member { FirstName: m.FirstName, LastName: m.LastName, DisplayName: m.DisplayName, + AvatarUrl: m.AvatarURL, Details: object.ToViewDetailsPb( m.Sequence, m.CreationDate, diff --git a/internal/api/grpc/member/project_grant_member.go b/internal/api/grpc/member/project_grant_member.go index 3b8c52906e..d807fd4b44 100644 --- a/internal/api/grpc/member/project_grant_member.go +++ b/internal/api/grpc/member/project_grant_member.go @@ -24,6 +24,7 @@ func ProjectGrantMemberToPb(m *proj_model.ProjectGrantMemberView) *member_pb.Mem FirstName: m.FirstName, LastName: m.LastName, DisplayName: m.DisplayName, + AvatarUrl: m.AvatarURL, Details: object.ToViewDetailsPb( m.Sequence, m.CreationDate, diff --git a/internal/api/grpc/member/project_member.go b/internal/api/grpc/member/project_member.go index b3206d8722..afd63bea08 100644 --- a/internal/api/grpc/member/project_member.go +++ b/internal/api/grpc/member/project_member.go @@ -24,6 +24,7 @@ func ProjectMemberToPb(m *proj_model.ProjectMemberView) *member_pb.Member { FirstName: m.FirstName, LastName: m.LastName, DisplayName: m.DisplayName, + AvatarUrl: m.AvatarURL, Details: object.ToViewDetailsPb( m.Sequence, m.CreationDate, diff --git a/internal/api/grpc/user/converter.go b/internal/api/grpc/user/converter.go index 4740b62d23..e71e34e0cf 100644 --- a/internal/api/grpc/user/converter.go +++ b/internal/api/grpc/user/converter.go @@ -56,6 +56,7 @@ func HumanToPb(view *model.HumanView) *user_pb.Human { DisplayName: view.DisplayName, PreferredLanguage: view.PreferredLanguage, Gender: GenderToPb(view.Gender), + AvatarUrl: view.AvatarURL, }, Email: &user_pb.Email{ Email: view.Email, @@ -83,6 +84,7 @@ func ProfileToPb(profile *model.Profile) *user_pb.Profile { DisplayName: profile.DisplayName, PreferredLanguage: profile.PreferredLanguage.String(), Gender: GenderToPb(profile.Gender), + AvatarUrl: profile.AvatarURL, } } diff --git a/internal/api/grpc/user/session.go b/internal/api/grpc/user/session.go index 94dd0de80d..db24507f87 100644 --- a/internal/api/grpc/user/session.go +++ b/internal/api/grpc/user/session.go @@ -24,6 +24,7 @@ func UserSessionToPb(session *user_model.UserSessionView) *user.Session { LoginName: session.LoginName, DisplayName: session.DisplayName, AuthState: SessionStateToPb(session.State), + AvatarUrl: session.AvatarURL, Details: object.ToViewDetailsPb( session.Sequence, session.CreationDate, diff --git a/internal/api/grpc/user/user_grant.go b/internal/api/grpc/user/user_grant.go index a06ceb73d5..c9f3133afa 100644 --- a/internal/api/grpc/user/user_grant.go +++ b/internal/api/grpc/user/user_grant.go @@ -32,6 +32,7 @@ func UserGrantToPb(grant *usr_grant_model.UserGrantView) *user_pb.UserGrant { ProjectId: grant.ProjectID, ProjectName: grant.ProjectName, ProjectGrantId: grant.GrantID, + AvatarUrl: grant.AvatarURL, Details: object.ToViewDetailsPb( grant.Sequence, grant.CreationDate, diff --git a/internal/api/oidc/client.go b/internal/api/oidc/client.go index 44fadccadf..7b6f7d910e 100644 --- a/internal/api/oidc/client.go +++ b/internal/api/oidc/client.go @@ -160,6 +160,7 @@ func (o *OPStorage) SetUserinfoFromScopes(ctx context.Context, userInfo oidc.Use userInfo.SetGender(oidc.Gender(getGender(user.Gender))) locale, _ := language.Parse(user.PreferredLanguage) userInfo.SetLocale(locale) + userInfo.SetPicture(user.AvatarURL) } else { userInfo.SetName(user.MachineView.Name) } diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index c6ccd12ff5..dd13381140 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -2,9 +2,10 @@ package eventstore import ( "context" + "time" + "github.com/caos/zitadel/internal/command" "github.com/caos/zitadel/internal/domain" - "time" "github.com/caos/logging" @@ -57,9 +58,11 @@ type AuthRequestRepo struct { type userSessionViewProvider interface { UserSessionByIDs(string, string) (*user_view_model.UserSessionView, error) UserSessionsByAgentID(string) ([]*user_view_model.UserSessionView, error) + PrefixAvatarURL() string } type userViewProvider interface { UserByID(string) (*user_view_model.UserView, error) + PrefixAvatarURL() string } type loginPolicyViewProvider interface { @@ -616,6 +619,7 @@ func (repo *AuthRequestRepo) usersForUserSelection(request *domain.AuthRequest) DisplayName: session.DisplayName, UserName: session.UserName, LoginName: session.LoginName, + ResourceOwner: session.ResourceOwner, AvatarKey: session.AvatarKey, UserSessionState: auth_req_model.UserSessionStateToDomain(session.State), SelectionPossible: request.RequestedOrgID == "" || request.RequestedOrgID == session.ResourceOwner, @@ -767,7 +771,7 @@ func userSessionsByUserAgentID(provider userSessionViewProvider, agentID string) if err != nil { 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) { @@ -781,7 +785,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve events, err := eventProvider.UserEventsByID(ctx, user.ID, session.Sequence) if err != nil { 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 for _, event := range events { @@ -806,7 +810,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve eventData, err := user_view_model.UserSessionFromEvent(event) if err != nil { 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 { continue @@ -817,7 +821,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve err := sessionCopy.AppendEvent(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) { @@ -856,24 +860,24 @@ func userByID(ctx context.Context, viewProvider userViewProvider, eventProvider events, err := eventProvider.UserEventsByID(ctx, userID, user.Sequence) if err != nil { 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 viewErr != nil { return nil, viewErr } - return user_view_model.UserToModel(user), viewErr + return user_view_model.UserToModel(user, viewProvider.PrefixAvatarURL()), viewErr } userCopy := *user for _, event := range events { 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) { 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 { diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go index 87fdb00cac..760e1422f4 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go @@ -3,20 +3,19 @@ package eventstore import ( "context" "encoding/json" - "github.com/caos/zitadel/internal/domain" "testing" "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/caos/zitadel/internal/auth/repository/eventsourcing/view" "github.com/caos/zitadel/internal/auth_request/model" "github.com/caos/zitadel/internal/auth_request/repository/cache" + "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/errors" 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_view_model "github.com/caos/zitadel/internal/org/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 } +func (m *mockViewNoUserSession) PrefixAvatarURL() string { + return "" +} + type mockViewErrUserSession struct{} 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") } +func (m *mockViewErrUserSession) PrefixAvatarURL() string { + return "" +} + type mockViewUserSession struct { ExternalLoginVerification time.Time PasswordlessVerification time.Time @@ -83,12 +90,20 @@ func (m *mockViewUserSession) UserSessionsByAgentID(string) ([]*user_view_model. return sessions, nil } +func (m *mockViewUserSession) PrefixAvatarURL() string { + return "prefix/" +} + type mockViewNoUser struct{} func (m *mockViewNoUser) UserByID(string) (*user_view_model.UserView, error) { return nil, errors.ThrowNotFound(nil, "id", "user not found") } +func (m *mockViewNoUser) PrefixAvatarURL() string { + return "" +} + type mockEventUser struct { Event *es_models.Event } @@ -152,6 +167,10 @@ func (m *mockViewUser) UserByID(string) (*user_view_model.UserView, error) { }, nil } +func (m *mockViewUser) PrefixAvatarURL() string { + return "" +} + type mockViewOrg struct { State org_model.OrgState } @@ -291,11 +310,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { UserID: "id1", LoginName: "loginname1", SelectionPossible: true, + ResourceOwner: "orgID1", }, { UserID: "id2", LoginName: "loginname2", SelectionPossible: true, + ResourceOwner: "orgID2", }, }, }}, @@ -329,11 +350,13 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { UserID: "id1", LoginName: "loginname1", SelectionPossible: true, + ResourceOwner: "orgID1", }, { UserID: "id2", LoginName: "loginname2", SelectionPossible: false, + ResourceOwner: "orgID2", }, }, }}, diff --git a/internal/auth/repository/eventsourcing/eventstore/user.go b/internal/auth/repository/eventsourcing/eventstore/user.go index 04b2db735a..191a7bbbfc 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user.go +++ b/internal/auth/repository/eventsourcing/eventstore/user.go @@ -24,10 +24,11 @@ import ( ) type UserRepo struct { - SearchLimit uint64 - Eventstore v1.Eventstore - View *view.View - SystemDefaults systemdefaults.SystemDefaults + SearchLimit uint64 + Eventstore v1.Eventstore + View *view.View + SystemDefaults systemdefaults.SystemDefaults + PrefixAvatarURL string } 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) if err != nil { 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 for _, event := range events { 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) { 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) { @@ -179,18 +180,18 @@ func (repo *UserRepo) UserByLoginName(ctx context.Context, loginname string) (*m events, err := repo.getUserEvents(ctx, user.ID, user.Sequence) if err != nil { 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 for _, event := range events { 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) { 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) { 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, Limit: request.Limit, TotalResult: count, - Result: usr_view_model.UsersToModel(users), + Result: usr_view_model.UsersToModel(users, repo.PrefixAvatarURL), } if sequenceErr == nil { result.Sequence = sequence.CurrentSequence diff --git a/internal/auth/repository/eventsourcing/eventstore/user_grant.go b/internal/auth/repository/eventsourcing/eventstore/user_grant.go index ab8ab93a24..b229d78823 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user_grant.go +++ b/internal/auth/repository/eventsourcing/eventstore/user_grant.go @@ -21,11 +21,12 @@ import ( ) type UserGrantRepo struct { - SearchLimit uint64 - View *view.View - IamID string - Auth authz.Config - AuthZRepo *authz_repo.EsRepository + SearchLimit uint64 + View *view.View + IamID string + Auth authz.Config + AuthZRepo *authz_repo.EsRepository + PrefixAvatarURL string } 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, Limit: request.Limit, TotalResult: count, - Result: model.UserGrantsToModel(grants), + Result: model.UserGrantsToModel(grants, repo.PrefixAvatarURL), } if err == nil { result.Sequence = sequence.CurrentSequence @@ -234,7 +235,7 @@ func (repo *UserGrantRepo) UserGrantsByProjectAndUserID(projectID, userID string if err != nil { 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) { diff --git a/internal/auth/repository/eventsourcing/eventstore/user_session.go b/internal/auth/repository/eventsourcing/eventstore/user_session.go index 28246193c3..9be553e6f1 100644 --- a/internal/auth/repository/eventsourcing/eventstore/user_session.go +++ b/internal/auth/repository/eventsourcing/eventstore/user_session.go @@ -18,7 +18,7 @@ func (repo *UserSessionRepo) GetMyUserSessions(ctx context.Context) ([]*usr_mode if err != nil { return nil, err } - return model.UserSessionsToModel(userSessions), nil + return model.UserSessionsToModel(userSessions, repo.View.PrefixAvatarURL()), nil } func (repo *UserSessionRepo) ActiveUserSessionCount() int64 { diff --git a/internal/auth/repository/eventsourcing/handler/user_grant.go b/internal/auth/repository/eventsourcing/handler/user_grant.go index 66b5c8242f..4296a95203 100644 --- a/internal/auth/repository/eventsourcing/handler/user_grant.go +++ b/internal/auth/repository/eventsourcing/handler/user_grant.go @@ -2,10 +2,11 @@ package handler import ( "context" + "strings" + "github.com/caos/zitadel/internal/eventstore/v1" iam_model "github.com/caos/zitadel/internal/iam/model" iam_view "github.com/caos/zitadel/internal/iam/repository/view" - "strings" es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk" 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.HumanProfileChanged, 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) if err != nil { 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) { grant.UserName = user.UserName + grant.UserResourceOwner = user.ResourceOwner if user.HumanView != nil { grant.FirstName = user.FirstName grant.LastName = user.LastName grant.DisplayName = user.FirstName + " " + user.LastName grant.Email = user.Email + grant.AvatarKey = user.AvatarKey } if user.MachineView != nil { grant.DisplayName = user.MachineView.Name diff --git a/internal/auth/repository/eventsourcing/repository.go b/internal/auth/repository/eventsourcing/repository.go index 283fe1959d..0373c2fedf 100644 --- a/internal/auth/repository/eventsourcing/repository.go +++ b/internal/auth/repository/eventsourcing/repository.go @@ -24,6 +24,7 @@ import ( type Config struct { SearchLimit uint64 Domain string + APIDomain string Eventstore v1.Config AuthRequest cache.Config View types.SQL @@ -63,7 +64,9 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co } 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 { return nil, err } @@ -78,10 +81,11 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co locker := spooler.NewLocker(sqlClient) userRepo := eventstore.UserRepo{ - SearchLimit: conf.SearchLimit, - Eventstore: es, - View: view, - SystemDefaults: systemDefaults, + SearchLimit: conf.SearchLimit, + Eventstore: es, + View: view, + SystemDefaults: systemDefaults, + PrefixAvatarURL: assetsAPI, } return &EsRepository{ spool, diff --git a/internal/auth/repository/eventsourcing/view/view.go b/internal/auth/repository/eventsourcing/view/view.go index fc33fccddf..7016ec9b93 100644 --- a/internal/auth/repository/eventsourcing/view/view.go +++ b/internal/auth/repository/eventsourcing/view/view.go @@ -10,23 +10,29 @@ import ( ) type View struct { - Db *gorm.DB - keyAlgorithm crypto.EncryptionAlgorithm - idGenerator id.Generator + Db *gorm.DB + keyAlgorithm crypto.EncryptionAlgorithm + 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) if err != nil { return nil, err } return &View{ - Db: gorm, - keyAlgorithm: keyAlgorithm, - idGenerator: idGenerator, + Db: gorm, + keyAlgorithm: keyAlgorithm, + idGenerator: idGenerator, + prefixAvatarURL: prefixAvatarURL, }, nil } func (v *View) Health() (err error) { return v.Db.DB().Ping() } + +func (v *View) PrefixAvatarURL() string { + return v.prefixAvatarURL +} diff --git a/internal/domain/human_profile.go b/internal/domain/human_profile.go index ab0989b37e..6ac7563806 100644 --- a/internal/domain/human_profile.go +++ b/internal/domain/human_profile.go @@ -1,8 +1,9 @@ package domain import ( - es_models "github.com/caos/zitadel/internal/eventstore/v1/models" "golang.org/x/text/language" + + es_models "github.com/caos/zitadel/internal/eventstore/v1/models" ) type Profile struct { @@ -21,3 +22,10 @@ type Profile struct { func (p *Profile) IsValid() bool { return p.FirstName != "" && p.LastName != "" } + +func AvatarURL(prefix, resourceOwner, key string) string { + if prefix == "" || resourceOwner == "" || key == "" { + return "" + } + return prefix + resourceOwner + "/" + key +} diff --git a/internal/domain/next_step.go b/internal/domain/next_step.go index 396258b1ef..50b62de6ba 100644 --- a/internal/domain/next_step.go +++ b/internal/domain/next_step.go @@ -48,6 +48,7 @@ type UserSelection struct { UserSessionState UserSessionState SelectionPossible bool AvatarKey string + ResourceOwner string } type UserSessionState int32 diff --git a/internal/iam/model/iam_member_view.go b/internal/iam/model/iam_member_view.go index 593184a72d..e842b1d597 100644 --- a/internal/iam/model/iam_member_view.go +++ b/internal/iam/model/iam_member_view.go @@ -16,6 +16,8 @@ type IAMMemberView struct { LastName string DisplayName string PreferredLoginName string + AvatarURL string + UserResourceOwner string Roles []string CreationDate time.Time ChangeDate time.Time diff --git a/internal/iam/repository/eventsourcing/model/label_policy.go b/internal/iam/repository/eventsourcing/model/label_policy.go index d5540b29fb..fbe4455f3c 100644 --- a/internal/iam/repository/eventsourcing/model/label_policy.go +++ b/internal/iam/repository/eventsourcing/model/label_policy.go @@ -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 { i.DefaultLabelPolicy = new(LabelPolicy) 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 { err := json.Unmarshal(event.Data, p) 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 } diff --git a/internal/iam/repository/view/model/iam_member.go b/internal/iam/repository/view/model/iam_member.go index 384dd7bb7a..65541fbc9c 100644 --- a/internal/iam/repository/view/model/iam_member.go +++ b/internal/iam/repository/view/model/iam_member.go @@ -4,13 +4,14 @@ import ( "encoding/json" "time" - es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" - "github.com/caos/logging" + "github.com/lib/pq" + + "github.com/caos/zitadel/internal/domain" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/iam/model" - "github.com/lib/pq" + es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model" ) const ( @@ -33,28 +34,14 @@ type IAMMemberView struct { Roles pq.StringArray `json:"roles" gorm:"column:roles"` Sequence uint64 `json:"-" gorm:"column:sequence"` 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"` ChangeDate time.Time `json:"-" gorm:"column:change_date"` } -func IAMMemberViewFromModel(member *model.IAMMemberView) *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 { +func IAMMemberToModel(member *IAMMemberView, prefixAvatarURL string) *model.IAMMemberView { return &model.IAMMemberView{ UserID: member.UserID, IAMID: member.IAMID, @@ -64,6 +51,8 @@ func IAMMemberToModel(member *IAMMemberView) *model.IAMMemberView { LastName: member.LastName, DisplayName: member.DisplayName, PreferredLoginName: member.PreferredLoginName, + AvatarURL: domain.AvatarURL(prefixAvatarURL, member.UserResourceOwner, member.AvatarKey), + UserResourceOwner: member.UserResourceOwner, Roles: member.Roles, Sequence: member.Sequence, 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)) for i, r := range roles { - result[i] = IAMMemberToModel(r) + result[i] = IAMMemberToModel(r, prefixAvatarURL) } return result } diff --git a/internal/management/repository/eventsourcing/eventstore/org.go b/internal/management/repository/eventsourcing/eventstore/org.go index cfc26963e5..acac0382e2 100644 --- a/internal/management/repository/eventsourcing/eventstore/org.go +++ b/internal/management/repository/eventsourcing/eventstore/org.go @@ -31,11 +31,12 @@ import ( ) type OrgRepository struct { - SearchLimit uint64 - Eventstore v1.Eventstore - View *mgmt_view.View - Roles []string - SystemDefaults systemdefaults.SystemDefaults + SearchLimit uint64 + Eventstore v1.Eventstore + View *mgmt_view.View + Roles []string + SystemDefaults systemdefaults.SystemDefaults + PrefixAvatarURL string } 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 { 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) { @@ -140,7 +141,7 @@ func (repo *OrgRepository) SearchMyOrgMembers(ctx context.Context, request *org_ Offset: request.Offset, Limit: request.Limit, TotalResult: count, - Result: model.OrgMembersToModel(members), + Result: model.OrgMembersToModel(members, repo.PrefixAvatarURL), } if sequenceErr == nil { result.Sequence = sequence.CurrentSequence @@ -653,18 +654,18 @@ func (repo *OrgRepository) userByID(ctx context.Context, id string) (*usr_model. } if esErr != nil { 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 for _, event := range events { 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) { 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) { diff --git a/internal/management/repository/eventsourcing/eventstore/project.go b/internal/management/repository/eventsourcing/eventstore/project.go index 1bc59e7bea..785bf46ad1 100644 --- a/internal/management/repository/eventsourcing/eventstore/project.go +++ b/internal/management/repository/eventsourcing/eventstore/project.go @@ -31,10 +31,11 @@ import ( type ProjectRepo struct { v1.Eventstore - SearchLimit uint64 - View *view.View - Roles []string - IAMID string + SearchLimit uint64 + View *view.View + Roles []string + IAMID string + PrefixAvatarURL string } 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 { 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) { @@ -154,7 +155,7 @@ func (repo *ProjectRepo) SearchProjectMembers(ctx context.Context, request *proj Offset: request.Offset, Limit: request.Limit, TotalResult: uint64(count), - Result: model.ProjectMembersToModel(members), + Result: model.ProjectMembersToModel(members, repo.PrefixAvatarURL), } if sequenceErr == nil { result.Sequence = sequence.CurrentSequence @@ -442,7 +443,7 @@ func (repo *ProjectRepo) ProjectGrantMemberByID(ctx context.Context, projectID, if err != nil { 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) { @@ -491,7 +492,7 @@ func (repo *ProjectRepo) SearchProjectGrantMembers(ctx context.Context, request Offset: request.Offset, Limit: request.Limit, TotalResult: uint64(count), - Result: model.ProjectGrantMembersToModel(members), + Result: model.ProjectGrantMembersToModel(members, repo.PrefixAvatarURL), } if sequenceErr == nil { result.Sequence = sequence.CurrentSequence @@ -542,18 +543,18 @@ func (repo *ProjectRepo) userByID(ctx context.Context, id string) (*usr_model.Us } if esErr != nil { 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 for _, event := range events { 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) { 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) { diff --git a/internal/management/repository/eventsourcing/eventstore/user.go b/internal/management/repository/eventsourcing/eventstore/user.go index 5def953f2d..dcfe9adaff 100644 --- a/internal/management/repository/eventsourcing/eventstore/user.go +++ b/internal/management/repository/eventsourcing/eventstore/user.go @@ -27,9 +27,10 @@ import ( type UserRepo struct { v1.Eventstore - SearchLimit uint64 - View *view.View - SystemDefaults systemdefaults.SystemDefaults + SearchLimit uint64 + View *view.View + SystemDefaults systemdefaults.SystemDefaults + PrefixAvatarURL string } 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 { 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 for _, event := range events { 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) { 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) { @@ -78,7 +79,7 @@ func (repo *UserRepo) SearchUsers(ctx context.Context, request *usr_model.UserSe Offset: request.Offset, Limit: request.Limit, TotalResult: count, - Result: model.UsersToModel(users), + Result: model.UsersToModel(users, repo.PrefixAvatarURL), } if sequenceErr == nil { result.Sequence = sequence.CurrentSequence @@ -118,7 +119,7 @@ func (repo *UserRepo) GetUserByLoginNameGlobal(ctx context.Context, loginName st if err != nil { 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) { diff --git a/internal/management/repository/eventsourcing/eventstore/user_grant.go b/internal/management/repository/eventsourcing/eventstore/user_grant.go index 9f4bc8daf9..4f2d774f28 100644 --- a/internal/management/repository/eventsourcing/eventstore/user_grant.go +++ b/internal/management/repository/eventsourcing/eventstore/user_grant.go @@ -13,8 +13,9 @@ import ( ) type UserGrantRepo struct { - SearchLimit uint64 - View *view.View + SearchLimit uint64 + View *view.View + PrefixAvatarURL string } 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 { 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) { @@ -30,7 +31,7 @@ func (repo *UserGrantRepo) UserGrantsByProjectID(ctx context.Context, projectID if err != nil { 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) { @@ -38,7 +39,7 @@ func (repo *UserGrantRepo) UserGrantsByProjectIDAndRoleKey(ctx context.Context, if err != nil { 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) { @@ -46,7 +47,7 @@ func (repo *UserGrantRepo) UserGrantsByProjectAndGrantID(ctx context.Context, pr if err != nil { 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) { @@ -54,7 +55,7 @@ func (repo *UserGrantRepo) UserGrantsByUserID(ctx context.Context, userID string if err != nil { 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) { @@ -79,7 +80,7 @@ func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_ Offset: request.Offset, Limit: request.Limit, TotalResult: count, - Result: model.UserGrantsToModel(grants), + Result: model.UserGrantsToModel(grants, repo.PrefixAvatarURL), } if sequenceErr == nil { result.Sequence = sequence.CurrentSequence diff --git a/internal/management/repository/eventsourcing/handler/org_member.go b/internal/management/repository/eventsourcing/handler/org_member.go index 9433b72b06..d46c84d2e7 100644 --- a/internal/management/repository/eventsourcing/handler/org_member.go +++ b/internal/management/repository/eventsourcing/handler/org_member.go @@ -131,7 +131,9 @@ func (m *OrgMember) processUser(event *es_models.Event) (err error) { usr_es_model.UserEmailChanged, usr_es_model.HumanProfileChanged, 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) if err != nil { 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 { org, err := m.getOrgByID(context.Background(), user.ResourceOwner) + if err != nil { + return err + } policy := org.OrgIamPolicy if policy == nil { 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.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) + member.UserResourceOwner = user.ResourceOwner if user.HumanView != nil { member.FirstName = user.FirstName member.LastName = user.LastName member.DisplayName = user.DisplayName member.Email = user.Email + member.AvatarKey = user.AvatarKey } if user.MachineView != nil { member.DisplayName = user.MachineView.Name diff --git a/internal/management/repository/eventsourcing/handler/project_grant_member.go b/internal/management/repository/eventsourcing/handler/project_grant_member.go index af21ecf1e4..a82eaf3c6c 100644 --- a/internal/management/repository/eventsourcing/handler/project_grant_member.go +++ b/internal/management/repository/eventsourcing/handler/project_grant_member.go @@ -2,6 +2,7 @@ package handler import ( "context" + "github.com/caos/zitadel/internal/domain" caos_errs "github.com/caos/zitadel/internal/errors" "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.HumanProfileChanged, 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) if err != nil { return err @@ -183,11 +186,13 @@ func (p *ProjectGrantMember) fillUserData(member *view_model.ProjectGrantMemberV } member.UserName = user.UserName member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) + member.UserResourceOwner = user.ResourceOwner if user.HumanView != nil { member.FirstName = user.FirstName member.LastName = user.LastName member.DisplayName = user.DisplayName member.Email = user.Email + member.AvatarKey = user.AvatarKey } if user.MachineView != nil { member.DisplayName = user.MachineView.Name diff --git a/internal/management/repository/eventsourcing/handler/project_member.go b/internal/management/repository/eventsourcing/handler/project_member.go index fc2573d414..0a67f4a095 100644 --- a/internal/management/repository/eventsourcing/handler/project_member.go +++ b/internal/management/repository/eventsourcing/handler/project_member.go @@ -2,6 +2,7 @@ package handler import ( "context" + "github.com/caos/zitadel/internal/domain" caos_errs "github.com/caos/zitadel/internal/errors" "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.HumanProfileChanged, 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) if err != nil { 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 { org, err := p.getOrgByID(context.Background(), user.ResourceOwner) + if err != nil { + return err + } policy := org.OrgIamPolicy if policy == nil { policy, err = p.getDefaultOrgIAMPolicy(context.TODO()) @@ -177,11 +183,13 @@ func (p *ProjectMember) fillUserData(member *view_model.ProjectMemberView, user } member.UserName = user.UserName member.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) + member.UserResourceOwner = user.ResourceOwner if user.HumanView != nil { member.FirstName = user.FirstName member.LastName = user.LastName member.Email = user.Email member.DisplayName = user.DisplayName + member.AvatarKey = user.AvatarKey } if user.MachineView != nil { member.DisplayName = user.MachineView.Name diff --git a/internal/management/repository/eventsourcing/handler/user_grant.go b/internal/management/repository/eventsourcing/handler/user_grant.go index 1b3b53318e..68a9d4ddfa 100644 --- a/internal/management/repository/eventsourcing/handler/user_grant.go +++ b/internal/management/repository/eventsourcing/handler/user_grant.go @@ -129,7 +129,9 @@ func (u *UserGrant) processUser(event *es_models.Event) (err error) { usr_es_model.UserEmailChanged, usr_es_model.HumanProfileChanged, 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) if err != nil { 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) { grant.UserName = user.UserName + grant.UserResourceOwner = user.ResourceOwner if user.HumanView != nil { grant.FirstName = user.FirstName grant.LastName = user.LastName grant.DisplayName = user.FirstName + " " + user.LastName grant.Email = user.Email + grant.AvatarKey = user.AvatarKey } if user.MachineView != nil { grant.DisplayName = user.MachineView.Name diff --git a/internal/management/repository/eventsourcing/repository.go b/internal/management/repository/eventsourcing/repository.go index 92d47a9e57..00803786a0 100644 --- a/internal/management/repository/eventsourcing/repository.go +++ b/internal/management/repository/eventsourcing/repository.go @@ -16,6 +16,7 @@ import ( type Config struct { SearchLimit uint64 Domain string + APIDomain string Eventstore v1.Config View types.SQL 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) + assetsAPI := conf.APIDomain + "/assets/v1/" return &EsRepository{ spooler: spool, - OrgRepository: eventstore.OrgRepository{conf.SearchLimit, es, view, roles, systemDefaults}, - ProjectRepo: eventstore.ProjectRepo{es, conf.SearchLimit, view, roles, systemDefaults.IamID}, - UserRepo: eventstore.UserRepo{es, conf.SearchLimit, view, systemDefaults}, - UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, view}, + OrgRepository: eventstore.OrgRepository{conf.SearchLimit, es, view, roles, systemDefaults, assetsAPI}, + ProjectRepo: eventstore.ProjectRepo{es, conf.SearchLimit, view, roles, systemDefaults.IamID, assetsAPI}, + UserRepo: eventstore.UserRepo{es, conf.SearchLimit, view, systemDefaults, assetsAPI}, + UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, view, assetsAPI}, IAMRepository: eventstore.IAMRepository{IAMV2Query: queries}, FeaturesRepo: eventstore.FeaturesRepo{es, view, conf.SearchLimit, systemDefaults}, view: view, diff --git a/internal/org/model/org_member_view.go b/internal/org/model/org_member_view.go index 3206cebae2..dc2c5be7fe 100644 --- a/internal/org/model/org_member_view.go +++ b/internal/org/model/org_member_view.go @@ -16,6 +16,8 @@ type OrgMemberView struct { LastName string DisplayName string PreferredLoginName string + AvatarURL string + UserResourceOwner string Roles []string CreationDate time.Time ChangeDate time.Time diff --git a/internal/org/repository/view/model/org_member.go b/internal/org/repository/view/model/org_member.go index 5167464bd5..e27c33e0b4 100644 --- a/internal/org/repository/view/model/org_member.go +++ b/internal/org/repository/view/model/org_member.go @@ -4,13 +4,14 @@ import ( "encoding/json" "time" - es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" - "github.com/caos/logging" + "github.com/lib/pq" + + "github.com/caos/zitadel/internal/domain" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/org/model" - "github.com/lib/pq" + es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" ) const ( @@ -33,12 +34,14 @@ type OrgMemberView struct { Roles pq.StringArray `json:"roles" gorm:"column:roles"` Sequence uint64 `json:"-" gorm:"column:sequence"` 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"` 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{ UserID: member.UserID, OrgID: member.OrgID, @@ -49,16 +52,18 @@ func OrgMemberToModel(member *OrgMemberView) *model.OrgMemberView { DisplayName: member.DisplayName, PreferredLoginName: member.PreferredLoginName, Roles: member.Roles, + AvatarURL: domain.AvatarURL(prefixAvatarURL, member.UserResourceOwner, member.AvatarKey), + UserResourceOwner: member.UserResourceOwner, Sequence: member.Sequence, CreationDate: member.CreationDate, ChangeDate: member.ChangeDate, } } -func OrgMembersToModel(roles []*OrgMemberView) []*model.OrgMemberView { +func OrgMembersToModel(roles []*OrgMemberView, prefixAvatarURL string) []*model.OrgMemberView { result := make([]*model.OrgMemberView, len(roles)) for i, r := range roles { - result[i] = OrgMemberToModel(r) + result[i] = OrgMemberToModel(r, prefixAvatarURL) } return result } diff --git a/internal/project/model/project_grant_member_view.go b/internal/project/model/project_grant_member_view.go index 7aa41af0b9..8d1b6e715e 100644 --- a/internal/project/model/project_grant_member_view.go +++ b/internal/project/model/project_grant_member_view.go @@ -17,6 +17,8 @@ type ProjectGrantMemberView struct { LastName string DisplayName string PreferredLoginName string + AvatarURL string + UserResourceOwner string Roles []string CreationDate time.Time ChangeDate time.Time diff --git a/internal/project/model/project_member_view.go b/internal/project/model/project_member_view.go index 959e6785a6..3616ba282b 100644 --- a/internal/project/model/project_member_view.go +++ b/internal/project/model/project_member_view.go @@ -16,6 +16,8 @@ type ProjectMemberView struct { LastName string DisplayName string PreferredLoginName string + AvatarURL string + UserResourceOwner string Roles []string CreationDate time.Time ChangeDate time.Time diff --git a/internal/project/repository/view/model/project_grant_member.go b/internal/project/repository/view/model/project_grant_member.go index 72d653661d..446f618028 100644 --- a/internal/project/repository/view/model/project_grant_member.go +++ b/internal/project/repository/view/model/project_grant_member.go @@ -7,6 +7,7 @@ import ( "github.com/caos/logging" "github.com/lib/pq" + "github.com/caos/zitadel/internal/domain" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/project/model" @@ -35,12 +36,14 @@ type ProjectGrantMemberView struct { Roles pq.StringArray `json:"roles" gorm:"column:roles"` Sequence uint64 `json:"-" gorm:"column:sequence"` 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"` 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{ UserID: member.UserID, GrantID: member.GrantID, @@ -51,6 +54,8 @@ func ProjectGrantMemberToModel(member *ProjectGrantMemberView) *model.ProjectGra LastName: member.LastName, DisplayName: member.DisplayName, PreferredLoginName: member.PreferredLoginName, + AvatarURL: domain.AvatarURL(prefixAvatarURL, member.UserResourceOwner, member.AvatarKey), + UserResourceOwner: member.UserResourceOwner, Roles: member.Roles, Sequence: member.Sequence, 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)) for i, r := range roles { - result[i] = ProjectGrantMemberToModel(r) + result[i] = ProjectGrantMemberToModel(r, prefixAvatarURL) } return result } diff --git a/internal/project/repository/view/model/project_member.go b/internal/project/repository/view/model/project_member.go index 5c24695f07..7472754aa9 100644 --- a/internal/project/repository/view/model/project_member.go +++ b/internal/project/repository/view/model/project_member.go @@ -5,11 +5,13 @@ import ( "time" "github.com/caos/logging" + "github.com/lib/pq" + + "github.com/caos/zitadel/internal/domain" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/v1/models" "github.com/caos/zitadel/internal/project/model" es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" - "github.com/lib/pq" ) const ( @@ -32,31 +34,36 @@ type ProjectMemberView struct { Roles pq.StringArray `json:"roles" gorm:"column:roles"` Sequence uint64 `json:"-" gorm:"column:sequence"` 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"` 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{ - UserID: member.UserID, - ProjectID: member.ProjectID, - 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, + UserID: member.UserID, + ProjectID: member.ProjectID, + UserName: member.UserName, + Email: member.Email, + FirstName: member.FirstName, + LastName: member.LastName, + DisplayName: member.DisplayName, + PreferredLoginName: member.PreferredLoginName, + AvatarURL: domain.AvatarURL(prefixAvatarURL, member.UserResourceOwner, member.AvatarKey), + UserResourceOwner: member.UserResourceOwner, + 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)) for i, r := range roles { - result[i] = ProjectMemberToModel(r) + result[i] = ProjectMemberToModel(r, prefixAvatarURL) } return result } diff --git a/internal/ui/login/static/templates/select_user.html b/internal/ui/login/static/templates/select_user.html index 5a2206df67..f71a252f0c 100644 --- a/internal/ui/login/static/templates/select_user.html +++ b/internal/ui/login/static/templates/select_user.html @@ -24,7 +24,6 @@
{{ if .Users }} {{ $displayLoginNameSuffix := and .OrgID (not .DisplayLoginNameSuffix)}} - {{ $orgID := .OrgID }} {{ range $user := .Users }} {{ $sessionState := (printf "UserSelection.SessionState%v" $user.UserSessionState) }}