UserDisplay: Handle both service accounts and user names when resolving "createdBy" (#98719)

* Handle both user and service accounts when resolving identity name for dashboards and folders

Co-authored-by: Misi <mgyongyosi@users.noreply.github.com>
This commit is contained in:
Karl Persson 2025-01-10 10:06:59 +01:00 committed by GitHub
parent 557820df56
commit bdb4725768
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 34 additions and 21 deletions

View File

@ -150,10 +150,10 @@ func (hs *HTTPServer) GetDashboard(c *contextmodel.ReqContext) response.Response
// Finding creator and last updater of the dashboard
updater, creator := anonString, anonString
if dash.UpdatedBy > 0 {
updater = hs.getUserLogin(ctx, dash.UpdatedBy)
updater = hs.getIdentityName(ctx, dash.OrgID, dash.UpdatedBy)
}
if dash.CreatedBy > 0 {
creator = hs.getUserLogin(ctx, dash.CreatedBy)
creator = hs.getIdentityName(ctx, dash.OrgID, dash.CreatedBy)
}
annotationPermissions := &dashboardsV0.AnnotationPermission{}
@ -265,16 +265,24 @@ func (hs *HTTPServer) getAnnotationPermissionsByScope(c *contextmodel.ReqContext
}
}
func (hs *HTTPServer) getUserLogin(ctx context.Context, userID int64) string {
ctx, span := tracer.Start(ctx, "api.getUserLogin")
// getIdentityName returns name of either user or service account
func (hs *HTTPServer) getIdentityName(ctx context.Context, orgID, id int64) string {
ctx, span := tracer.Start(ctx, "api.getIdentityName")
defer span.End()
query := user.GetUserByIDQuery{ID: userID}
user, err := hs.userService.GetByID(ctx, &query)
// We use GetSignedInUser here instead of GetByID so both user and service accounts are resolved.
ident, err := hs.userService.GetSignedInUser(ctx, &user.GetSignedInUserQuery{
UserID: id,
OrgID: orgID,
})
if err != nil {
return anonString
}
return user.Login
if ident.IsIdentityType(claims.TypeServiceAccount) {
return ident.GetName()
}
return ident.GetLogin()
}
func (hs *HTTPServer) getDashboardHelper(ctx context.Context, orgID int64, id int64, uid string) (*dashboards.Dashboard, response.Response) {
@ -845,7 +853,7 @@ func (hs *HTTPServer) GetDashboardVersions(c *contextmodel.ReqContext) response.
if found {
creator = login
} else {
creator = hs.getUserLogin(c.Req.Context(), version.CreatedBy)
creator = hs.getIdentityName(c.Req.Context(), c.SignedInUser.GetOrgID(), version.CreatedBy)
if creator != anonString {
loginMem[version.CreatedBy] = creator
}
@ -941,7 +949,7 @@ func (hs *HTTPServer) GetDashboardVersion(c *contextmodel.ReqContext) response.R
creator := anonString
if res.CreatedBy > 0 {
creator = hs.getUserLogin(c.Req.Context(), res.CreatedBy)
creator = hs.getIdentityName(c.Req.Context(), dash.OrgID, res.CreatedBy)
}
dashVersionMeta := &dashver.DashboardVersionMeta{

View File

@ -742,7 +742,7 @@ func TestDashboardVersionsAPIEndpoint(t *testing.T) {
},
}
getHS(&usertest.FakeUserService{
ExpectedUser: &user.User{ID: 1, Login: "test-user"},
ExpectedSignedInUser: &user.SignedInUser{Login: "test-user"},
}).callGetDashboardVersions(sc)
assert.Equal(t, http.StatusOK, sc.resp.Code)

View File

@ -427,10 +427,10 @@ func (hs *HTTPServer) newToFolderDto(c *contextmodel.ReqContext, f *folder.Folde
// Finding creator and last updater of the folder
updater, creator := anonString, anonString
if f.CreatedBy > 0 {
creator = hs.getUserLogin(ctx, f.CreatedBy)
creator = hs.getIdentityName(ctx, f.OrgID, f.CreatedBy)
}
if f.UpdatedBy > 0 {
updater = hs.getUserLogin(ctx, f.UpdatedBy)
updater = hs.getIdentityName(ctx, f.OrgID, f.UpdatedBy)
}
acMetadata, _ := hs.getFolderACMetadata(c, f)
@ -1079,10 +1079,10 @@ func (fk8s *folderK8sHandler) toDTO(c *contextmodel.ReqContext, fold *folder.Fol
// #TODO refactor the various conversions of the folder so that we either set created by in folder.Folder or
// we convert from unstructured to folder DTO without an intermediate conversion to folder.Folder
if len(createdBy) > 0 {
creator = fk8s.getUserLogin(ctx, toUID(createdBy))
creator = fk8s.getIdentityName(ctx, toUID(createdBy))
}
if len(createdBy) > 0 {
updater = fk8s.getUserLogin(ctx, toUID(createdBy))
updater = fk8s.getIdentityName(ctx, toUID(createdBy))
}
acMetadata, _ := fk8s.getFolderACMetadata(c, fold)
@ -1119,18 +1119,21 @@ func (fk8s *folderK8sHandler) toDTO(c *contextmodel.ReqContext, fold *folder.Fol
}, nil
}
func (fk8s *folderK8sHandler) getUserLogin(ctx context.Context, userUID string) string {
func (fk8s *folderK8sHandler) getIdentityName(ctx context.Context, uid string) string {
ctx, span := tracer.Start(ctx, "api.getUserLogin")
defer span.End()
query := user.GetUserByUIDQuery{
UID: userUID,
}
user, err := fk8s.userService.GetByUID(ctx, &query)
ident, err := fk8s.userService.GetByUID(ctx, &user.GetUserByUIDQuery{
UID: uid,
})
if err != nil {
return anonString
}
return user.Login
if ident.IsServiceAccount {
return ident.Name
}
return ident.Login
}
func (fk8s *folderK8sHandler) getFolderACMetadata(c *contextmodel.ReqContext, f *folder.Folder) (accesscontrol.Metadata, error) {

View File

@ -534,6 +534,7 @@ func (m mockClientConfigProvider) DirectlyServeHTTP(w http.ResponseWriter, r *ht
func TestUpdateFolderLegacyAndUnifiedStorage(t *testing.T) {
testuser := &user.User{ID: 99, UID: "fdxsqt7t5ryf4a", Login: "testuser"}
testSignedInUser := &user.SignedInUser{UserID: 99, UserUID: "fdxsqt7t5ryf4a", Login: "testuser"}
legacyFolder := folder.Folder{
UID: "ady4yobv315a8e",
@ -718,7 +719,8 @@ func TestUpdateFolderLegacyAndUnifiedStorage(t *testing.T) {
ExpectedResult: model.HitList{},
}
hs.userService = &usertest.FakeUserService{
ExpectedUser: testuser,
ExpectedUser: testuser,
ExpectedSignedInUser: testSignedInUser,
}
hs.Features = featuremgmt.WithFeatures(
featuresArr...,