From 2cf421dfe35831bc2681fbb3d90040c0d65cb613 Mon Sep 17 00:00:00 2001 From: J Guerreiro Date: Tue, 8 Feb 2022 19:19:22 +0000 Subject: [PATCH] ServiceAccounts: Add access control metadata to service accounts (#45096) * add role to DTO * add access control metadata --- pkg/services/serviceaccounts/api/api.go | 36 +++++++++++++++++-- .../serviceaccounts/database/database.go | 9 +++-- pkg/services/serviceaccounts/models.go | 13 ++++--- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/pkg/services/serviceaccounts/api/api.go b/pkg/services/serviceaccounts/api/api.go index bde88d1bf39..4498a8d0d54 100644 --- a/pkg/services/serviceaccounts/api/api.go +++ b/pkg/services/serviceaccounts/api/api.go @@ -6,8 +6,10 @@ import ( "net/http" "strconv" + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/routing" + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" @@ -31,6 +33,7 @@ type ServiceAccountsAPI struct { RouterRegister routing.RouteRegister store serviceaccounts.Store apiKeyStore APIKeyStore + log log.Logger } type serviceAccountIdDTO struct { @@ -53,6 +56,7 @@ func NewServiceAccountsAPI( RouterRegister: routerRegister, store: store, apiKeyStore: apiKeyStore, + log: log.New("serviceaccounts.api"), } } @@ -132,14 +136,42 @@ func (api *ServiceAccountsAPI) ConvertToServiceAccount(ctx *models.ReqContext) r } } -func (api *ServiceAccountsAPI) ListServiceAccounts(ctx *models.ReqContext) response.Response { - serviceAccounts, err := api.store.ListServiceAccounts(ctx.Req.Context(), ctx.OrgId, -1) +func (api *ServiceAccountsAPI) ListServiceAccounts(c *models.ReqContext) response.Response { + serviceAccounts, err := api.store.ListServiceAccounts(c.Req.Context(), c.OrgId, -1) if err != nil { return response.Error(http.StatusInternalServerError, "Failed to list service accounts", err) } + + saIDs := map[string]bool{} + for i := range serviceAccounts { + serviceAccounts[i].AvatarUrl = dtos.GetGravatarUrlWithDefault("", serviceAccounts[i].Name) + saIDs[strconv.FormatInt(serviceAccounts[i].Id, 10)] = true + } + + metadata, err := api.getAccessControlMetadata(c, saIDs) + if err == nil && len(metadata) != 0 { + for i := range serviceAccounts { + serviceAccounts[i].AccessControl = metadata[strconv.FormatInt(serviceAccounts[i].Id, 10)] + } + } + return response.JSON(http.StatusOK, serviceAccounts) } +func (api *ServiceAccountsAPI) getAccessControlMetadata(c *models.ReqContext, saIDs map[string]bool) (map[string]accesscontrol.Metadata, error) { + if api.accesscontrol.IsDisabled() || !c.QueryBool("accesscontrol") { + return nil, nil + } + + userPermissions, err := api.accesscontrol.GetUserPermissions(c.Req.Context(), c.SignedInUser) + if err != nil || len(userPermissions) == 0 { + api.log.Warn("could not fetch accesscontrol metadata for teams", "error", err) + return nil, err + } + + return accesscontrol.GetResourcesMetadata(c.Req.Context(), userPermissions, "serviceaccounts", saIDs), nil +} + func (api *ServiceAccountsAPI) RetrieveServiceAccount(ctx *models.ReqContext) response.Response { scopeID, err := strconv.ParseInt(web.Params(ctx.Req)[":serviceAccountId"], 10, 64) if err != nil { diff --git a/pkg/services/serviceaccounts/database/database.go b/pkg/services/serviceaccounts/database/database.go index 4541e59e8a6..e6f1191a584 100644 --- a/pkg/services/serviceaccounts/database/database.go +++ b/pkg/services/serviceaccounts/database/database.go @@ -143,10 +143,11 @@ func (s *ServiceAccountsStoreImpl) ListServiceAccounts(ctx context.Context, orgI if serviceAccountID > 0 { query.UserID = serviceAccountID } - err := s.sqlStore.GetOrgUsers(ctx, &query) - if err != nil { + + if err := s.sqlStore.GetOrgUsers(ctx, &query); err != nil { return nil, err } + saDTOs := make([]*serviceaccounts.ServiceAccountDTO, len(query.Result)) for i, user := range query.Result { saDTOs[i] = &serviceaccounts.ServiceAccountDTO{ @@ -154,6 +155,7 @@ func (s *ServiceAccountsStoreImpl) ListServiceAccounts(ctx context.Context, orgI OrgId: user.OrgId, Name: user.Name, Login: user.Login, + Role: user.Role, } tokens, err := s.ListTokens(ctx, user.OrgId, user.UserId) if err != nil { @@ -161,7 +163,8 @@ func (s *ServiceAccountsStoreImpl) ListServiceAccounts(ctx context.Context, orgI } saDTOs[i].Tokens = int64(len(tokens)) } - return saDTOs, err + + return saDTOs, nil } // RetrieveServiceAccountByID returns a service account by its ID diff --git a/pkg/services/serviceaccounts/models.go b/pkg/services/serviceaccounts/models.go index 550b55be00b..d5577e2ecc7 100644 --- a/pkg/services/serviceaccounts/models.go +++ b/pkg/services/serviceaccounts/models.go @@ -28,11 +28,14 @@ type CreateServiceaccountForm struct { } type ServiceAccountDTO struct { - Id int64 `json:"id"` - Name string `json:"name"` - Login string `json:"login"` - OrgId int64 `json:"orgId"` - Tokens int64 `json:"tokens"` + Id int64 `json:"id"` + Name string `json:"name"` + Login string `json:"login"` + OrgId int64 `json:"orgId"` + Tokens int64 `json:"tokens"` + Role string `json:"role"` + AvatarUrl string `json:"avatarUrl"` + AccessControl map[string]bool `json:"accessControl,omitempty"` } type ServiceAccountProfileDTO struct {