mirror of
https://github.com/grafana/grafana.git
synced 2025-02-12 08:35:43 -06:00
Auth: Move Service Account service to SignedInUser Interface (#73142)
* move service account service to identity interface * Update pkg/services/auth/identity/requester.go
This commit is contained in:
parent
8c2f439cd7
commit
67de18ff06
@ -1,6 +1,12 @@
|
||||
package identity
|
||||
|
||||
import "github.com/grafana/grafana/pkg/models/roletype"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models/roletype"
|
||||
)
|
||||
|
||||
const (
|
||||
NamespaceUser = "user"
|
||||
@ -10,6 +16,9 @@ const (
|
||||
NamespaceRenderService = "render"
|
||||
)
|
||||
|
||||
var ErrNotIntIdentifier = errors.New("identifier is not an int64")
|
||||
var ErrIdentifierNotInitialized = errors.New("identifier is not initialized")
|
||||
|
||||
type Requester interface {
|
||||
// GetDisplayName returns the display name of the active entity.
|
||||
// The display name is the name if it is set, otherwise the login or email.
|
||||
@ -50,3 +59,24 @@ type Requester interface {
|
||||
// HasUniqueId returns true if the entity has a unique id
|
||||
HasUniqueId() bool
|
||||
}
|
||||
|
||||
// IntIdentifier converts a string identifier to an int64.
|
||||
// Applicable for users, service accounts, api keys and renderer service.
|
||||
// Errors if the identifier is not initialized or if namespace is not recognized.
|
||||
func IntIdentifier(namespace, identifier string) (int64, error) {
|
||||
switch namespace {
|
||||
case NamespaceUser, NamespaceAPIKey, NamespaceServiceAccount, NamespaceRenderService:
|
||||
id, err := strconv.ParseInt(identifier, 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unrecognized format for valid namespace %s: %w", namespace, err)
|
||||
}
|
||||
|
||||
if id < 1 {
|
||||
return 0, ErrIdentifierNotInitialized
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
return 0, ErrNotIntIdentifier
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apikey"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||
@ -101,17 +102,26 @@ func (api *ServiceAccountsAPI) CreateServiceAccount(c *contextmodel.ReqContext)
|
||||
return response.Error(http.StatusBadRequest, "Bad request data", err)
|
||||
}
|
||||
|
||||
if err := api.validateRole(cmd.Role, &c.OrgRole); err != nil {
|
||||
if err := api.validateRole(cmd.Role, c.SignedInUser.GetOrgRole()); err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "failed to create service account", err)
|
||||
}
|
||||
|
||||
serviceAccount, err := api.service.CreateServiceAccount(c.Req.Context(), c.OrgID, &cmd)
|
||||
serviceAccount, err := api.service.CreateServiceAccount(c.Req.Context(), c.SignedInUser.GetOrgID(), &cmd)
|
||||
if err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to create service account", err)
|
||||
}
|
||||
|
||||
if c.SignedInUser.IsRealUser() {
|
||||
if _, err := api.permissionService.SetUserPermission(c.Req.Context(), c.OrgID, accesscontrol.User{ID: c.SignedInUser.UserID}, strconv.FormatInt(serviceAccount.Id, 10), "Admin"); err != nil {
|
||||
namespace, identifier := c.SignedInUser.GetNamespacedID()
|
||||
|
||||
if namespace == identity.NamespaceUser {
|
||||
userID, err := identity.IntIdentifier(namespace, identifier)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to parse user id", err)
|
||||
}
|
||||
|
||||
if _, err := api.permissionService.SetUserPermission(c.Req.Context(),
|
||||
c.SignedInUser.GetOrgID(), accesscontrol.User{ID: userID},
|
||||
strconv.FormatInt(serviceAccount.Id, 10), "Admin"); err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to set permissions for service account creator", err)
|
||||
}
|
||||
}
|
||||
@ -143,7 +153,7 @@ func (api *ServiceAccountsAPI) RetrieveServiceAccount(ctx *contextmodel.ReqConte
|
||||
return response.Error(http.StatusBadRequest, "Service Account ID is invalid", err)
|
||||
}
|
||||
|
||||
serviceAccount, err := api.service.RetrieveServiceAccount(ctx.Req.Context(), ctx.OrgID, scopeID)
|
||||
serviceAccount, err := api.service.RetrieveServiceAccount(ctx.Req.Context(), ctx.SignedInUser.GetOrgID(), scopeID)
|
||||
if err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to retrieve service account", err)
|
||||
}
|
||||
@ -190,11 +200,11 @@ func (api *ServiceAccountsAPI) UpdateServiceAccount(c *contextmodel.ReqContext)
|
||||
return response.Error(http.StatusBadRequest, "Bad request data", err)
|
||||
}
|
||||
|
||||
if err := api.validateRole(cmd.Role, &c.OrgRole); err != nil {
|
||||
if err := api.validateRole(cmd.Role, c.SignedInUser.GetOrgRole()); err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "failed to update service account", err)
|
||||
}
|
||||
|
||||
resp, err := api.service.UpdateServiceAccount(c.Req.Context(), c.OrgID, scopeID, &cmd)
|
||||
resp, err := api.service.UpdateServiceAccount(c.Req.Context(), c.SignedInUser.GetOrgID(), scopeID, &cmd)
|
||||
if err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "Failed update service account", err)
|
||||
}
|
||||
@ -212,7 +222,7 @@ func (api *ServiceAccountsAPI) UpdateServiceAccount(c *contextmodel.ReqContext)
|
||||
})
|
||||
}
|
||||
|
||||
func (api *ServiceAccountsAPI) validateRole(r *org.RoleType, orgRole *org.RoleType) error {
|
||||
func (api *ServiceAccountsAPI) validateRole(r *org.RoleType, orgRole org.RoleType) error {
|
||||
if r != nil && !r.IsValid() {
|
||||
return serviceaccounts.ErrServiceAccountInvalidRole.Errorf("invalid role specified")
|
||||
}
|
||||
@ -240,7 +250,7 @@ func (api *ServiceAccountsAPI) DeleteServiceAccount(ctx *contextmodel.ReqContext
|
||||
if err != nil {
|
||||
return response.Error(http.StatusBadRequest, "Service account ID is invalid", err)
|
||||
}
|
||||
err = api.service.DeleteServiceAccount(ctx.Req.Context(), ctx.OrgID, scopeID)
|
||||
err = api.service.DeleteServiceAccount(ctx.Req.Context(), ctx.SignedInUser.GetOrgID(), scopeID)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Service account deletion error", err)
|
||||
}
|
||||
@ -280,7 +290,7 @@ func (api *ServiceAccountsAPI) SearchOrgServiceAccountsWithPaging(c *contextmode
|
||||
filter = serviceaccounts.FilterOnlyDisabled
|
||||
}
|
||||
q := serviceaccounts.SearchOrgServiceAccountsQuery{
|
||||
OrgID: c.OrgID,
|
||||
OrgID: c.SignedInUser.GetOrgID(),
|
||||
Query: c.Query("query"),
|
||||
Page: page,
|
||||
Limit: perPage,
|
||||
@ -315,7 +325,7 @@ func (api *ServiceAccountsAPI) SearchOrgServiceAccountsWithPaging(c *contextmode
|
||||
|
||||
// POST /api/serviceaccounts/migrate
|
||||
func (api *ServiceAccountsAPI) MigrateApiKeysToServiceAccounts(ctx *contextmodel.ReqContext) response.Response {
|
||||
results, err := api.service.MigrateApiKeysToServiceAccounts(ctx.Req.Context(), ctx.OrgID)
|
||||
results, err := api.service.MigrateApiKeysToServiceAccounts(ctx.Req.Context(), ctx.SignedInUser.GetOrgID())
|
||||
if err != nil {
|
||||
return response.JSON(http.StatusInternalServerError, results)
|
||||
}
|
||||
@ -330,7 +340,7 @@ func (api *ServiceAccountsAPI) ConvertToServiceAccount(ctx *contextmodel.ReqCont
|
||||
return response.Error(http.StatusBadRequest, "Key ID is invalid", err)
|
||||
}
|
||||
|
||||
if err := api.service.MigrateApiKey(ctx.Req.Context(), ctx.OrgID, keyId); err != nil {
|
||||
if err := api.service.MigrateApiKey(ctx.Req.Context(), ctx.SignedInUser.GetOrgID(), keyId); err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Error converting API key", err)
|
||||
}
|
||||
|
||||
@ -342,15 +352,11 @@ func (api *ServiceAccountsAPI) getAccessControlMetadata(c *contextmodel.ReqConte
|
||||
return map[string]accesscontrol.Metadata{}
|
||||
}
|
||||
|
||||
if c.SignedInUser.Permissions == nil {
|
||||
return map[string]accesscontrol.Metadata{}
|
||||
}
|
||||
|
||||
permissions, ok := c.SignedInUser.Permissions[c.OrgID]
|
||||
if !ok {
|
||||
if len(c.SignedInUser.GetPermissions()) == 0 {
|
||||
return map[string]accesscontrol.Metadata{}
|
||||
}
|
||||
|
||||
permissions := c.SignedInUser.GetPermissions()
|
||||
return accesscontrol.GetResourcesMetadata(c.Req.Context(), permissions, "serviceaccounts:id:", saIDs)
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,9 @@ func TestServiceAccountsAPI_CreateServiceAccount(t *testing.T) {
|
||||
a.service = &fakeServiceAccountService{ExpectedServiceAccount: tt.expectedSA, ExpectedErr: tt.expectedErr}
|
||||
})
|
||||
req := server.NewRequest(http.MethodPost, "/api/serviceaccounts/", strings.NewReader(tt.body))
|
||||
webtest.RequestWithSignedInUser(req, &user.SignedInUser{OrgRole: tt.basicRole, OrgID: 1, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}})
|
||||
webtest.RequestWithSignedInUser(req, &user.SignedInUser{
|
||||
OrgRole: tt.basicRole, OrgID: 1, IsAnonymous: true,
|
||||
Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}})
|
||||
res, err := server.SendJSON(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -69,8 +69,9 @@ func (api *ServiceAccountsAPI) ListTokens(ctx *contextmodel.ReqContext) response
|
||||
return response.Error(http.StatusBadRequest, "Service Account ID is invalid", err)
|
||||
}
|
||||
|
||||
orgID := ctx.SignedInUser.GetOrgID()
|
||||
saTokens, err := api.service.ListTokens(ctx.Req.Context(), &serviceaccounts.GetSATokensQuery{
|
||||
OrgID: &ctx.OrgID,
|
||||
OrgID: &orgID,
|
||||
ServiceAccountID: &saID,
|
||||
})
|
||||
if err != nil {
|
||||
@ -131,7 +132,7 @@ func (api *ServiceAccountsAPI) CreateToken(c *contextmodel.ReqContext) response.
|
||||
}
|
||||
|
||||
// confirm service account exists
|
||||
if _, err = api.service.RetrieveServiceAccount(c.Req.Context(), c.OrgID, saID); err != nil {
|
||||
if _, err = api.service.RetrieveServiceAccount(c.Req.Context(), c.SignedInUser.GetOrgID(), saID); err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to retrieve service account", err)
|
||||
}
|
||||
|
||||
@ -141,7 +142,7 @@ func (api *ServiceAccountsAPI) CreateToken(c *contextmodel.ReqContext) response.
|
||||
}
|
||||
|
||||
// Force affected service account to be the one referenced in the URL
|
||||
cmd.OrgId = c.OrgID
|
||||
cmd.OrgId = c.SignedInUser.GetOrgID()
|
||||
|
||||
if api.cfg.ApiKeyMaxSecondsToLive != -1 {
|
||||
if cmd.SecondsToLive == 0 {
|
||||
@ -204,7 +205,7 @@ func (api *ServiceAccountsAPI) DeleteToken(c *contextmodel.ReqContext) response.
|
||||
}
|
||||
|
||||
// confirm service account exists
|
||||
if _, err := api.service.RetrieveServiceAccount(c.Req.Context(), c.OrgID, saID); err != nil {
|
||||
if _, err := api.service.RetrieveServiceAccount(c.Req.Context(), c.SignedInUser.GetOrgID(), saID); err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to retrieve service account", err)
|
||||
}
|
||||
|
||||
@ -213,7 +214,7 @@ func (api *ServiceAccountsAPI) DeleteToken(c *contextmodel.ReqContext) response.
|
||||
return response.Error(http.StatusBadRequest, "Token ID is invalid", err)
|
||||
}
|
||||
|
||||
if err = api.service.DeleteServiceAccountToken(c.Req.Context(), c.OrgID, saID, tokenID); err != nil {
|
||||
if err = api.service.DeleteServiceAccountToken(c.Req.Context(), c.SignedInUser.GetOrgID(), saID, tokenID); err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, failedToDeleteMsg, err)
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
@ -106,7 +106,7 @@ type SearchOrgServiceAccountsQuery struct {
|
||||
Filter ServiceAccountFilter
|
||||
Page int
|
||||
Limit int
|
||||
SignedInUser *user.SignedInUser
|
||||
SignedInUser identity.Requester
|
||||
}
|
||||
|
||||
func (q *SearchOrgServiceAccountsQuery) SetDefaults() {
|
||||
|
Loading…
Reference in New Issue
Block a user