fix: return absolute url for avatar in user sessions (#3724)

* fix: return absolute url for avatar in user sessions

* fix: refresh token unique constraint
This commit is contained in:
Livio Amstutz 2022-05-30 13:27:52 +02:00 committed by GitHub
parent e79aab3671
commit 41d78ef523
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 43 additions and 85 deletions

View File

@ -156,7 +156,7 @@ CREATE TABLE auth.refresh_tokens (
instance_id STRING NOT NULL, instance_id STRING NOT NULL,
PRIMARY KEY (id, instance_id), PRIMARY KEY (id, instance_id),
UNIQUE INDEX unique_client_user_index (client_id ASC, user_agent_id ASC, user_id ASC, instance_id) UNIQUE INDEX unique_client_user_index (client_id, user_agent_id, user_id)
); );
CREATE TABLE auth.org_project_mapping ( CREATE TABLE auth.org_project_mapping (

View File

@ -153,7 +153,7 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
verifier := internal_authz.Start(repo) verifier := internal_authz.Start(repo)
authenticatedAPIs := api.New(config.Port, router, &repo, config.InternalAuthZ, config.ExternalSecure, config.HTTP2HostHeader) authenticatedAPIs := api.New(config.Port, router, &repo, config.InternalAuthZ, config.ExternalSecure, config.HTTP2HostHeader)
authRepo, err := auth_es.Start(config.Auth, config.SystemDefaults, commands, queries, dbClient, assets.HandlerPrefix, keys.OIDC, keys.User) authRepo, err := auth_es.Start(config.Auth, config.SystemDefaults, commands, queries, dbClient, keys.OIDC, keys.User)
if err != nil { if err != nil {
return fmt.Errorf("error starting auth repo: %w", err) return fmt.Errorf("error starting auth repo: %w", err)
} }

View File

@ -100,7 +100,7 @@ func (s *Server) ListMyUserSessions(ctx context.Context, req *auth_pb.ListMyUser
return nil, err return nil, err
} }
return &auth_pb.ListMyUserSessionsResponse{ return &auth_pb.ListMyUserSessionsResponse{
Result: user_grpc.UserSessionsToPb(userSessions), Result: user_grpc.UserSessionsToPb(userSessions, s.assetsAPIDomain(ctx)),
}, nil }, nil
} }

View File

@ -7,15 +7,15 @@ import (
"github.com/zitadel/zitadel/pkg/grpc/user" "github.com/zitadel/zitadel/pkg/grpc/user"
) )
func UserSessionsToPb(sessions []*user_model.UserSessionView) []*user.Session { func UserSessionsToPb(sessions []*user_model.UserSessionView, avatarPrefix string) []*user.Session {
s := make([]*user.Session, len(sessions)) s := make([]*user.Session, len(sessions))
for i, session := range sessions { for i, session := range sessions {
s[i] = UserSessionToPb(session) s[i] = UserSessionToPb(session, avatarPrefix)
} }
return s return s
} }
func UserSessionToPb(session *user_model.UserSessionView) *user.Session { func UserSessionToPb(session *user_model.UserSessionView, avatarPrefix string) *user.Session {
return &user.Session{ return &user.Session{
// SessionId: session.,//TOOD: not return from be // SessionId: session.,//TOOD: not return from be
AgentId: session.UserAgentID, AgentId: session.UserAgentID,
@ -24,7 +24,7 @@ func UserSessionToPb(session *user_model.UserSessionView) *user.Session {
LoginName: session.LoginName, LoginName: session.LoginName,
DisplayName: session.DisplayName, DisplayName: session.DisplayName,
AuthState: SessionStateToPb(session.State), AuthState: SessionStateToPb(session.State),
AvatarUrl: session.AvatarURL, AvatarUrl: domain.AvatarURL(avatarPrefix, session.ResourceOwner, session.AvatarKey),
Details: object.ToViewDetailsPb( Details: object.ToViewDetailsPb(
session.Sequence, session.Sequence,
session.CreationDate, session.CreationDate,

View File

@ -65,11 +65,9 @@ type privacyPolicyProvider interface {
type userSessionViewProvider interface { type userSessionViewProvider interface {
UserSessionByIDs(string, string, string) (*user_view_model.UserSessionView, error) UserSessionByIDs(string, string, string) (*user_view_model.UserSessionView, error)
UserSessionsByAgentID(string, string) ([]*user_view_model.UserSessionView, error) UserSessionsByAgentID(string, string) ([]*user_view_model.UserSessionView, error)
PrefixAvatarURL() string
} }
type userViewProvider interface { type userViewProvider interface {
UserByID(string, string) (*user_view_model.UserView, error) UserByID(string, string) (*user_view_model.UserView, error)
PrefixAvatarURL() string
} }
type loginPolicyViewProvider interface { type loginPolicyViewProvider interface {
@ -1105,7 +1103,7 @@ func userSessionsByUserAgentID(provider userSessionViewProvider, agentID, instan
if err != nil { if err != nil {
return nil, err return nil, err
} }
return user_view_model.UserSessionsToModel(session, provider.PrefixAvatarURL()), nil return user_view_model.UserSessionsToModel(session), nil
} }
func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eventProvider userEventProvider, agentID string, user *user_model.UserView) (*user_model.UserSessionView, error) { func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eventProvider userEventProvider, agentID string, user *user_model.UserView) (*user_model.UserSessionView, error) {
@ -1119,7 +1117,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
events, err := eventProvider.UserEventsByID(ctx, user.ID, session.Sequence) events, err := eventProvider.UserEventsByID(ctx, user.ID, session.Sequence)
if err != nil { if err != nil {
logging.Log("EVENT-Hse6s").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events") logging.Log("EVENT-Hse6s").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events")
return user_view_model.UserSessionToModel(session, provider.PrefixAvatarURL()), nil return user_view_model.UserSessionToModel(session), nil
} }
sessionCopy := *session sessionCopy := *session
for _, event := range events { for _, event := range events {
@ -1144,7 +1142,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
eventData, err := user_view_model.UserSessionFromEvent(event) eventData, err := user_view_model.UserSessionFromEvent(event)
if err != nil { if err != nil {
logging.Log("EVENT-sdgT3").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error getting event data") logging.Log("EVENT-sdgT3").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error getting event data")
return user_view_model.UserSessionToModel(session, provider.PrefixAvatarURL()), nil return user_view_model.UserSessionToModel(session), nil
} }
if eventData.UserAgentID != agentID { if eventData.UserAgentID != agentID {
continue continue
@ -1155,7 +1153,7 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
err := sessionCopy.AppendEvent(event) err := sessionCopy.AppendEvent(event)
logging.Log("EVENT-qbhj3").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("error appending event") logging.Log("EVENT-qbhj3").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("error appending event")
} }
return user_view_model.UserSessionToModel(&sessionCopy, provider.PrefixAvatarURL()), nil return user_view_model.UserSessionToModel(&sessionCopy), nil
} }
func activeUserByID(ctx context.Context, userViewProvider userViewProvider, userEventProvider userEventProvider, queries orgViewProvider, lockoutPolicyProvider lockoutPolicyViewProvider, userID string, ignoreUnknownUsernames bool) (user *user_model.UserView, err error) { func activeUserByID(ctx context.Context, userViewProvider userViewProvider, userEventProvider userEventProvider, queries orgViewProvider, lockoutPolicyProvider lockoutPolicyViewProvider, userID string, ignoreUnknownUsernames bool) (user *user_model.UserView, err error) {
@ -1200,24 +1198,24 @@ func userByID(ctx context.Context, viewProvider userViewProvider, eventProvider
events, err := eventProvider.UserEventsByID(ctx, userID, user.Sequence) events, err := eventProvider.UserEventsByID(ctx, userID, user.Sequence)
if err != nil { if err != nil {
logging.Log("EVENT-dfg42").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events") logging.Log("EVENT-dfg42").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("error retrieving new events")
return user_view_model.UserToModel(user, viewProvider.PrefixAvatarURL()), nil return user_view_model.UserToModel(user), nil
} }
if len(events) == 0 { if len(events) == 0 {
if viewErr != nil { if viewErr != nil {
return nil, viewErr return nil, viewErr
} }
return user_view_model.UserToModel(user, viewProvider.PrefixAvatarURL()), viewErr return user_view_model.UserToModel(user), viewErr
} }
userCopy := *user userCopy := *user
for _, event := range events { for _, event := range events {
if err := userCopy.AppendEvent(event); err != nil { if err := userCopy.AppendEvent(event); err != nil {
return user_view_model.UserToModel(user, viewProvider.PrefixAvatarURL()), nil return user_view_model.UserToModel(user), nil
} }
} }
if userCopy.State == int32(user_model.UserStateDeleted) { if userCopy.State == int32(user_model.UserStateDeleted) {
return nil, errors.ThrowNotFound(nil, "EVENT-3F9so", "Errors.User.NotFound") return nil, errors.ThrowNotFound(nil, "EVENT-3F9so", "Errors.User.NotFound")
} }
return user_view_model.UserToModel(&userCopy, viewProvider.PrefixAvatarURL()), nil return user_view_model.UserToModel(&userCopy), nil
} }
func linkExternalIDPs(ctx context.Context, userCommandProvider userCommandProvider, request *domain.AuthRequest) error { func linkExternalIDPs(ctx context.Context, userCommandProvider userCommandProvider, request *domain.AuthRequest) error {

View File

@ -32,10 +32,6 @@ func (m *mockViewNoUserSession) UserSessionsByAgentID(string, string) ([]*user_v
return nil, nil return nil, nil
} }
func (m *mockViewNoUserSession) PrefixAvatarURL() string {
return ""
}
type mockViewErrUserSession struct{} type mockViewErrUserSession struct{}
func (m *mockViewErrUserSession) UserSessionByIDs(string, string, string) (*user_view_model.UserSessionView, error) { func (m *mockViewErrUserSession) UserSessionByIDs(string, string, string) (*user_view_model.UserSessionView, error) {
@ -46,10 +42,6 @@ func (m *mockViewErrUserSession) UserSessionsByAgentID(string, string) ([]*user_
return nil, errors.ThrowInternal(nil, "id", "internal error") return nil, errors.ThrowInternal(nil, "id", "internal error")
} }
func (m *mockViewErrUserSession) PrefixAvatarURL() string {
return ""
}
type mockViewUserSession struct { type mockViewUserSession struct {
ExternalLoginVerification time.Time ExternalLoginVerification time.Time
PasswordlessVerification time.Time PasswordlessVerification time.Time
@ -87,20 +79,12 @@ func (m *mockViewUserSession) UserSessionsByAgentID(string, string) ([]*user_vie
return sessions, nil return sessions, nil
} }
func (m *mockViewUserSession) PrefixAvatarURL() string {
return "prefix/"
}
type mockViewNoUser struct{} type mockViewNoUser struct{}
func (m *mockViewNoUser) UserByID(string, string) (*user_view_model.UserView, error) { func (m *mockViewNoUser) UserByID(string, string) (*user_view_model.UserView, error) {
return nil, errors.ThrowNotFound(nil, "id", "user not found") return nil, errors.ThrowNotFound(nil, "id", "user not found")
} }
func (m *mockViewNoUser) PrefixAvatarURL() string {
return ""
}
type mockEventUser struct { type mockEventUser struct {
Event *es_models.Event Event *es_models.Event
} }
@ -176,10 +160,6 @@ func (m *mockViewUser) UserByID(string, string) (*user_view_model.UserView, erro
}, nil }, nil
} }
func (m *mockViewUser) PrefixAvatarURL() string {
return ""
}
type mockViewOrg struct { type mockViewOrg struct {
State domain.OrgState State domain.OrgState
} }

View File

@ -14,12 +14,11 @@ import (
) )
type UserRepo struct { type UserRepo struct {
SearchLimit uint64 SearchLimit uint64
Eventstore v1.Eventstore Eventstore v1.Eventstore
View *view.View View *view.View
Query *query.Queries Query *query.Queries
SystemDefaults systemdefaults.SystemDefaults SystemDefaults systemdefaults.SystemDefaults
PrefixAvatarURL string
} }
func (repo *UserRepo) Health(ctx context.Context) error { func (repo *UserRepo) Health(ctx context.Context) error {

View File

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

View File

@ -33,14 +33,14 @@ type EsRepository struct {
eventstore.OrgRepository eventstore.OrgRepository
} }
func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string, oidcEncryption crypto.EncryptionAlgorithm, userEncryption crypto.EncryptionAlgorithm) (*EsRepository, error) { func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, oidcEncryption crypto.EncryptionAlgorithm, userEncryption crypto.EncryptionAlgorithm) (*EsRepository, error) {
es, err := v1.Start(dbClient) es, err := v1.Start(dbClient)
if err != nil { if err != nil {
return nil, err return nil, err
} }
idGenerator := id.SonyFlakeGenerator() idGenerator := id.SonyFlakeGenerator()
view, err := auth_view.StartView(dbClient, oidcEncryption, queries, idGenerator, assetsPrefix) view, err := auth_view.StartView(dbClient, oidcEncryption, queries, idGenerator)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -50,12 +50,11 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Comma
spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, systemDefaults, queries) spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, systemDefaults, queries)
userRepo := eventstore.UserRepo{ userRepo := eventstore.UserRepo{
SearchLimit: conf.SearchLimit, SearchLimit: conf.SearchLimit,
Eventstore: es, Eventstore: es,
View: view, View: view,
Query: queries, Query: queries,
SystemDefaults: systemDefaults, SystemDefaults: systemDefaults,
PrefixAvatarURL: assetsPrefix,
} }
//TODO: remove as soon as possible //TODO: remove as soon as possible
queryView := queryViewWrapper{ queryView := queryViewWrapper{

View File

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

View File

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

View File

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

View File

@ -37,7 +37,6 @@ type HumanView struct {
NickName string NickName string
DisplayName string DisplayName string
AvatarKey string AvatarKey string
AvatarURL string
PreferredLanguage string PreferredLanguage string
Gender Gender Gender Gender
Email string Email string
@ -249,7 +248,7 @@ func (u *UserView) GetProfile() (*Profile, error) {
Gender: u.Gender, Gender: u.Gender,
PreferredLoginName: u.PreferredLoginName, PreferredLoginName: u.PreferredLoginName,
LoginNames: u.LoginNames, LoginNames: u.LoginNames,
AvatarURL: u.AvatarURL, AvatarKey: u.AvatarKey,
}, nil }, nil
} }

View File

@ -139,7 +139,7 @@ func (m *MachineView) IsZero() bool {
return m == nil || m.Name == "" return m == nil || m.Name == ""
} }
func UserToModel(user *UserView, prefixAvatarURL string) *model.UserView { func UserToModel(user *UserView) *model.UserView {
userView := &model.UserView{ userView := &model.UserView{
ID: user.ID, ID: user.ID,
UserName: user.UserName, UserName: user.UserName,
@ -165,7 +165,6 @@ func UserToModel(user *UserView, prefixAvatarURL string) *model.UserView {
NickName: user.NickName, NickName: user.NickName,
DisplayName: user.DisplayName, DisplayName: user.DisplayName,
AvatarKey: user.AvatarKey, AvatarKey: user.AvatarKey,
AvatarURL: domain.AvatarURL(prefixAvatarURL, user.ResourceOwner, user.AvatarKey),
PreferredLanguage: user.PreferredLanguage, PreferredLanguage: user.PreferredLanguage,
Gender: model.Gender(user.Gender), Gender: model.Gender(user.Gender),
Email: user.Email, Email: user.Email,
@ -194,14 +193,6 @@ func UserToModel(user *UserView, prefixAvatarURL string) *model.UserView {
return userView return userView
} }
func UsersToModel(users []*UserView, prefixAvatarURL string) []*model.UserView {
result := make([]*model.UserView, len(users))
for i, p := range users {
result[i] = UserToModel(p, prefixAvatarURL)
}
return result
}
func WebauthnTokensToModel(tokens []*WebAuthNView) []*model.WebAuthNView { func WebauthnTokensToModel(tokens []*WebAuthNView) []*model.WebAuthNView {
if tokens == nil { if tokens == nil {
return nil return nil

View File

@ -55,7 +55,7 @@ func UserSessionFromEvent(event *models.Event) (*UserSessionView, error) {
return v, nil return v, nil
} }
func UserSessionToModel(userSession *UserSessionView, prefixAvatarURL string) *model.UserSessionView { func UserSessionToModel(userSession *UserSessionView) *model.UserSessionView {
return &model.UserSessionView{ return &model.UserSessionView{
ChangeDate: userSession.ChangeDate, ChangeDate: userSession.ChangeDate,
CreationDate: userSession.CreationDate, CreationDate: userSession.CreationDate,
@ -67,7 +67,6 @@ func UserSessionToModel(userSession *UserSessionView, prefixAvatarURL string) *m
LoginName: userSession.LoginName, LoginName: userSession.LoginName,
DisplayName: userSession.DisplayName, DisplayName: userSession.DisplayName,
AvatarKey: userSession.AvatarKey, AvatarKey: userSession.AvatarKey,
AvatarURL: domain.AvatarURL(prefixAvatarURL, userSession.ResourceOwner, userSession.AvatarKey),
SelectedIDPConfigID: userSession.SelectedIDPConfigID, SelectedIDPConfigID: userSession.SelectedIDPConfigID,
PasswordVerification: userSession.PasswordVerification, PasswordVerification: userSession.PasswordVerification,
PasswordlessVerification: userSession.PasswordlessVerification, PasswordlessVerification: userSession.PasswordlessVerification,
@ -80,10 +79,10 @@ func UserSessionToModel(userSession *UserSessionView, prefixAvatarURL string) *m
} }
} }
func UserSessionsToModel(userSessions []*UserSessionView, prefixAvatarURL string) []*model.UserSessionView { func UserSessionsToModel(userSessions []*UserSessionView) []*model.UserSessionView {
result := make([]*model.UserSessionView, len(userSessions)) result := make([]*model.UserSessionView, len(userSessions))
for i, s := range userSessions { for i, s := range userSessions {
result[i] = UserSessionToModel(s, prefixAvatarURL) result[i] = UserSessionToModel(s)
} }
return result return result
} }