mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 20:57:24 +00:00
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:
parent
1e77b8aeae
commit
770994e143
@ -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:
|
||||
|
@ -59,6 +59,7 @@ title: zitadel/member.proto
|
||||
| first_name | string | - | |
|
||||
| last_name | string | - | |
|
||||
| display_name | string | - | |
|
||||
| avatar_url | string | - | |
|
||||
|
||||
|
||||
|
||||
|
@ -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 | - | |
|
||||
|
||||
|
||||
|
||||
|
1
go.sum
1
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=
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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",
|
||||
},
|
||||
},
|
||||
}},
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ type UserSelection struct {
|
||||
UserSessionState UserSessionState
|
||||
SelectionPossible bool
|
||||
AvatarKey string
|
||||
ResourceOwner string
|
||||
}
|
||||
|
||||
type UserSessionState int32
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -24,7 +24,6 @@
|
||||
<div class="lgn-account-selection">
|
||||
{{ if .Users }}
|
||||
{{ $displayLoginNameSuffix := and .OrgID (not .DisplayLoginNameSuffix)}}
|
||||
{{ $orgID := .OrgID }}
|
||||
{{ range $user := .Users }}
|
||||
{{ $sessionState := (printf "UserSelection.SessionState%v" $user.UserSessionState) }}
|
||||
<button type="submit" name="userID" value="{{$user.UserID}}" class="lgn-account"
|
||||
@ -32,7 +31,7 @@
|
||||
<div class="left">
|
||||
<div class="lgn-avatar" {{if not $user.AvatarKey}}loginname="{{$user.LoginName}}"{{end}}>
|
||||
{{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}}
|
||||
<span class="initials">A</span>
|
||||
{{end}}
|
||||
|
@ -17,7 +17,7 @@ type Profile struct {
|
||||
Gender Gender
|
||||
PreferredLoginName string
|
||||
LoginNames []string
|
||||
Avatar string
|
||||
AvatarURL string
|
||||
}
|
||||
|
||||
func (p *Profile) IsValid() bool {
|
||||
|
@ -20,6 +20,7 @@ type UserSessionView struct {
|
||||
LoginName string
|
||||
DisplayName string
|
||||
AvatarKey string
|
||||
AvatarURL string
|
||||
SelectedIDPConfigID string
|
||||
PasswordVerification time.Time
|
||||
PasswordlessVerification time.Time
|
||||
|
@ -41,6 +41,7 @@ type HumanView struct {
|
||||
NickName string
|
||||
DisplayName string
|
||||
AvatarKey string
|
||||
AvatarURL string
|
||||
PreSignedAvatar *url.URL
|
||||
PreferredLanguage string
|
||||
Gender Gender
|
||||
@ -251,6 +252,7 @@ func (u *UserView) GetProfile() (*Profile, error) {
|
||||
Gender: u.Gender,
|
||||
PreferredLoginName: u.PreferredLoginName,
|
||||
LoginNames: u.LoginNames,
|
||||
AvatarURL: u.AvatarURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/lib/pq"
|
||||
|
||||
req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
@ -135,7 +136,7 @@ func (m *MachineView) IsZero() bool {
|
||||
return m == nil || m.Name == ""
|
||||
}
|
||||
|
||||
func UserToModel(user *UserView) *model.UserView {
|
||||
func UserToModel(user *UserView, prefixAvatarURL string) *model.UserView {
|
||||
userView := &model.UserView{
|
||||
ID: user.ID,
|
||||
UserName: user.UserName,
|
||||
@ -160,6 +161,7 @@ func UserToModel(user *UserView) *model.UserView {
|
||||
NickName: user.NickName,
|
||||
DisplayName: user.DisplayName,
|
||||
AvatarKey: user.AvatarKey,
|
||||
AvatarURL: domain.AvatarURL(prefixAvatarURL, user.ResourceOwner, user.AvatarKey),
|
||||
PreferredLanguage: user.PreferredLanguage,
|
||||
Gender: model.Gender(user.Gender),
|
||||
Email: user.Email,
|
||||
@ -187,10 +189,10 @@ func UserToModel(user *UserView) *model.UserView {
|
||||
return userView
|
||||
}
|
||||
|
||||
func UsersToModel(users []*UserView) []*model.UserView {
|
||||
func UsersToModel(users []*UserView, prefixAvatarURL string) []*model.UserView {
|
||||
result := make([]*model.UserView, len(users))
|
||||
for i, p := range users {
|
||||
result[i] = UserToModel(p)
|
||||
result[i] = UserToModel(p, prefixAvatarURL)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@ -340,7 +342,7 @@ func (u *UserView) AppendEvent(event *models.Event) (err error) {
|
||||
es_model.InitializedHumanCheckSucceeded:
|
||||
u.InitRequired = false
|
||||
case es_model.HumanAvatarAdded:
|
||||
u.setData(event)
|
||||
err = u.setData(event)
|
||||
case es_model.HumanAvatarRemoved:
|
||||
u.AvatarKey = ""
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/caos/logging"
|
||||
|
||||
req_model "github.com/caos/zitadel/internal/auth_request/model"
|
||||
"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/user/model"
|
||||
@ -51,7 +52,7 @@ func UserSessionFromEvent(event *models.Event) (*UserSessionView, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func UserSessionToModel(userSession *UserSessionView) *model.UserSessionView {
|
||||
func UserSessionToModel(userSession *UserSessionView, prefixAvatarURL string) *model.UserSessionView {
|
||||
return &model.UserSessionView{
|
||||
ChangeDate: userSession.ChangeDate,
|
||||
CreationDate: userSession.CreationDate,
|
||||
@ -63,6 +64,7 @@ func UserSessionToModel(userSession *UserSessionView) *model.UserSessionView {
|
||||
LoginName: userSession.LoginName,
|
||||
DisplayName: userSession.DisplayName,
|
||||
AvatarKey: userSession.AvatarKey,
|
||||
AvatarURL: domain.AvatarURL(prefixAvatarURL, userSession.ResourceOwner, userSession.AvatarKey),
|
||||
SelectedIDPConfigID: userSession.SelectedIDPConfigID,
|
||||
PasswordVerification: userSession.PasswordVerification,
|
||||
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))
|
||||
for i, s := range userSessions {
|
||||
result[i] = UserSessionToModel(s)
|
||||
result[i] = UserSessionToModel(s, prefixAvatarURL)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ type UserGrantView struct {
|
||||
ProjectName string
|
||||
OrgName string
|
||||
OrgPrimaryDomain string
|
||||
AvatarURL string
|
||||
RoleKeys []string
|
||||
|
||||
CreationDate time.Time
|
||||
|
@ -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/usergrant/model"
|
||||
@ -32,21 +33,23 @@ const (
|
||||
)
|
||||
|
||||
type UserGrantView struct {
|
||||
ID string `json:"-" gorm:"column:id;primary_key"`
|
||||
ResourceOwner string `json:"-" gorm:"resource_owner"`
|
||||
UserID string `json:"userId" gorm:"user_id"`
|
||||
ProjectID string `json:"projectId" gorm:"column:project_id"`
|
||||
GrantID string `json:"grantId" gorm:"column:grant_id"`
|
||||
UserName string `json:"-" gorm:"column:user_name"`
|
||||
FirstName string `json:"-" gorm:"column:first_name"`
|
||||
LastName string `json:"-" gorm:"column:last_name"`
|
||||
DisplayName string `json:"-" gorm:"column:display_name"`
|
||||
Email string `json:"-" gorm:"column:email"`
|
||||
ProjectName string `json:"-" gorm:"column:project_name"`
|
||||
ProjectOwner string `json:"-" gorm:"column:project_owner"`
|
||||
OrgName string `json:"-" gorm:"column:org_name"`
|
||||
OrgPrimaryDomain string `json:"-" gorm:"column:org_primary_domain"`
|
||||
RoleKeys pq.StringArray `json:"roleKeys" gorm:"column:role_keys"`
|
||||
ID string `json:"-" gorm:"column:id;primary_key"`
|
||||
ResourceOwner string `json:"-" gorm:"resource_owner"`
|
||||
UserID string `json:"userId" gorm:"user_id"`
|
||||
ProjectID string `json:"projectId" gorm:"column:project_id"`
|
||||
GrantID string `json:"grantId" gorm:"column:grant_id"`
|
||||
UserName string `json:"-" gorm:"column:user_name"`
|
||||
FirstName string `json:"-" gorm:"column:first_name"`
|
||||
LastName string `json:"-" gorm:"column:last_name"`
|
||||
DisplayName string `json:"-" gorm:"column:display_name"`
|
||||
Email string `json:"-" gorm:"column:email"`
|
||||
ProjectName string `json:"-" gorm:"column:project_name"`
|
||||
ProjectOwner string `json:"-" gorm:"column:project_owner"`
|
||||
OrgName string `json:"-" gorm:"column:org_name"`
|
||||
OrgPrimaryDomain string `json:"-" gorm:"column:org_primary_domain"`
|
||||
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"`
|
||||
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
||||
@ -55,7 +58,7 @@ type UserGrantView struct {
|
||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||
}
|
||||
|
||||
func UserGrantToModel(grant *UserGrantView) *model.UserGrantView {
|
||||
func UserGrantToModel(grant *UserGrantView, prefixAvatarURL string) *model.UserGrantView {
|
||||
return &model.UserGrantView{
|
||||
ID: grant.ID,
|
||||
ResourceOwner: grant.ResourceOwner,
|
||||
@ -73,15 +76,16 @@ func UserGrantToModel(grant *UserGrantView) *model.UserGrantView {
|
||||
OrgName: grant.OrgName,
|
||||
OrgPrimaryDomain: grant.OrgPrimaryDomain,
|
||||
RoleKeys: grant.RoleKeys,
|
||||
AvatarURL: domain.AvatarURL(prefixAvatarURL, grant.ResourceOwner, grant.AvatarKey),
|
||||
Sequence: grant.Sequence,
|
||||
GrantID: grant.GrantID,
|
||||
}
|
||||
}
|
||||
|
||||
func UserGrantsToModel(grants []*UserGrantView) []*model.UserGrantView {
|
||||
func UserGrantsToModel(grants []*UserGrantView, prefixAvatarURL string) []*model.UserGrantView {
|
||||
result := make([]*model.UserGrantView, len(grants))
|
||||
for i, g := range grants {
|
||||
result[i] = UserGrantToModel(g)
|
||||
result[i] = UserGrantToModel(g, prefixAvatarURL)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
16
migrations/cockroach/V1.49__avatar.sql
Normal file
16
migrations/cockroach/V1.49__avatar.sql
Normal 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;
|
@ -51,6 +51,12 @@ message Member {
|
||||
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 {
|
||||
|
@ -113,6 +113,12 @@ message Profile {
|
||||
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 {
|
||||
@ -509,6 +515,12 @@ message Session {
|
||||
}
|
||||
];
|
||||
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 {
|
||||
@ -641,6 +653,12 @@ message UserGrant {
|
||||
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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user