Instrument tracing across accesscontrol (#91864)

Instrument tracing across accesscontrol 

---------

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>
This commit is contained in:
Jeff Levin 2024-08-16 14:08:19 -08:00 committed by GitHub
parent 68f545210d
commit 028e8ac59e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 301 additions and 51 deletions

View File

@ -211,49 +211,79 @@ var reqDatasourceByIdNotFound = `{
}`
func TestDataSourceQueryError(t *testing.T) {
type body struct {
Message string `json:"message"`
MessageId string `json:"messageId"`
StatusCode int `json:"statusCode"`
}
tcs := []struct {
request string
clientErr error
expectedStatus int
expectedBody string
expectedBody body
}{
{
request: reqValid,
clientErr: plugins.ErrPluginUnavailable,
expectedStatus: http.StatusInternalServerError,
expectedBody: `{"message":"Plugin unavailable","messageId":"plugin.unavailable","statusCode":500,"traceID":""}`,
expectedBody: body{
Message: "Plugin unavailable",
MessageId: "plugin.unavailable",
StatusCode: 500,
},
},
{
request: reqValid,
clientErr: plugins.ErrMethodNotImplemented,
expectedStatus: http.StatusNotFound,
expectedBody: `{"message":"Method not implemented","messageId":"plugin.notImplemented","statusCode":404,"traceID":""}`,
expectedBody: body{
Message: "Method not implemented",
MessageId: "plugin.notImplemented",
StatusCode: 404,
},
},
{
request: reqValid,
clientErr: errors.New("surprise surprise"),
expectedStatus: errutil.StatusInternal.HTTPStatus(),
expectedBody: `{"message":"An error occurred within the plugin","messageId":"plugin.downstreamError","statusCode":500,"traceID":""}`,
expectedBody: body{
Message: "An error occurred within the plugin",
MessageId: "plugin.downstreamError",
StatusCode: 500,
},
},
{
request: reqNoQueries,
expectedStatus: http.StatusBadRequest,
expectedBody: `{"message":"No queries found","messageId":"query.noQueries","statusCode":400,"traceID":""}`,
expectedBody: body{
Message: "No queries found",
MessageId: "query.noQueries",
StatusCode: 400,
},
},
{
request: reqQueryWithInvalidDatasourceID,
expectedStatus: http.StatusBadRequest,
expectedBody: `{"message":"Query does not contain a valid data source identifier","messageId":"query.invalidDatasourceId","statusCode":400,"traceID":""}`,
expectedBody: body{
Message: "Query does not contain a valid data source identifier",
MessageId: "query.invalidDatasourceId",
StatusCode: 400,
},
},
{
request: reqDatasourceByUidNotFound,
expectedStatus: http.StatusNotFound,
expectedBody: `{"message":"Data source not found","traceID":""}`,
expectedBody: body{
Message: "Data source not found",
},
},
{
request: reqDatasourceByIdNotFound,
expectedStatus: http.StatusNotFound,
expectedBody: `{"message":"Data source not found","traceID":""}`,
expectedBody: body{
Message: "Data source not found",
},
},
}
@ -291,14 +321,24 @@ func TestDataSourceQueryError(t *testing.T) {
hs.QuotaService = quotatest.New(false, nil)
})
req := srv.NewPostRequest("/api/ds/query", strings.NewReader(tc.request))
webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {datasources.ActionQuery: []string{datasources.ScopeAll}}}})
resp, err := srv.SendJSON(req)
require.NoError(t, err)
require.Equal(t, tc.expectedStatus, resp.StatusCode)
body, err := io.ReadAll(resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, tc.expectedBody, string(body))
var responseBody body
err = json.Unmarshal(bodyBytes, &responseBody)
require.NoError(t, err)
require.Equal(t, tc.expectedBody.Message, responseBody.Message)
require.Equal(t, tc.expectedBody.MessageId, responseBody.MessageId)
require.Equal(t, tc.expectedBody.StatusCode, responseBody.StatusCode)
require.NoError(t, resp.Body.Close())
})
}

View File

@ -199,12 +199,15 @@ func TestCallResource(t *testing.T) {
resp, err := srv.SendJSON(req)
require.NoError(t, err)
body := new(strings.Builder)
_, err = io.Copy(body, resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
require.NoError(t, err)
expectedBody := `{ "message": "Failed to call resource", "traceID": "" }`
require.JSONEq(t, expectedBody, body.String())
var responseBody struct {
Message string `json:"message"`
}
err = json.Unmarshal(bodyBytes, &responseBody)
require.NoError(t, err)
require.Equal(t, responseBody.Message, "Failed to call resource")
require.NoError(t, resp.Body.Close())
require.Equal(t, 500, resp.StatusCode)
})

View File

@ -7,6 +7,7 @@ import (
openfgav1 "github.com/openfga/api/proto/openfga/v1"
"github.com/prometheus/client_golang/prometheus"
"go.opentelemetry.io/otel"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/log"
@ -18,6 +19,7 @@ import (
var (
errAccessNotImplemented = errors.New("access control not implemented for resource")
tracer = otel.Tracer("github.com/grafana/grafana/pkg/services/accesscontrol/acimpl")
)
var _ accesscontrol.AccessControl = new(AccessControl)
@ -52,6 +54,9 @@ type AccessControl struct {
}
func (a *AccessControl) Evaluate(ctx context.Context, user identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.Evaluate")
defer span.End()
if a.features.IsEnabledGlobally(featuremgmt.FlagZanzana) {
return a.evaluateCompare(ctx, user, evaluator)
}
@ -60,6 +65,9 @@ func (a *AccessControl) Evaluate(ctx context.Context, user identity.Requester, e
}
func (a *AccessControl) evaluate(ctx context.Context, user identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.evaluate")
defer span.End()
timer := prometheus.NewTimer(metrics.MAccessEvaluationsSummary)
defer timer.ObserveDuration()
metrics.MAccessEvaluationCount.Inc()
@ -98,6 +106,9 @@ func (a *AccessControl) evaluate(ctx context.Context, user identity.Requester, e
}
func (a *AccessControl) evaluateZanzana(ctx context.Context, user identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.evaluateZanzana")
defer span.End()
eval, err := evaluator.MutateScopes(ctx, a.resolvers.GetScopeAttributeMutator(user.GetOrgID()))
if err != nil {
if !errors.Is(err, accesscontrol.ErrResolverNotFound) {
@ -140,6 +151,9 @@ type evalResult struct {
// evaluateCompare run RBAC and zanzana checks in parallel and then compare result
func (a *AccessControl) evaluateCompare(ctx context.Context, user identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.evaluateCompare")
defer span.End()
res := make(chan evalResult, 2)
go func() {
timer := prometheus.NewTimer(a.metrics.mAccessEngineEvaluationsSeconds.WithLabelValues("zanzana"))
@ -192,5 +206,8 @@ func (a *AccessControl) RegisterScopeAttributeResolver(prefix string, resolver a
}
func (a *AccessControl) debug(ctx context.Context, ident identity.Requester, msg string, eval accesscontrol.Evaluator) {
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.debug")
defer span.End()
a.log.FromContext(ctx).Debug(msg, "id", ident.GetID(), "orgID", ident.GetOrgID(), "permissions", eval.GoString())
}

View File

@ -85,7 +85,6 @@ func ProvideOSSService(
log: log.New("accesscontrol.service"),
roles: accesscontrol.BuildBasicRoleDefinitions(),
store: store,
tracer: tracer,
sync: migrator.NewZanzanaSynchroniser(zclient, db),
permRegistry: permRegistry,
}
@ -103,7 +102,6 @@ type Service struct {
registrations accesscontrol.RegistrationList
roles map[string]*accesscontrol.RoleDTO
store accesscontrol.Store
tracer tracing.Tracer
sync *migrator.ZanzanaSynchroniser
permRegistry permreg.PermissionRegistry
}
@ -116,7 +114,7 @@ func (s *Service) GetUsageStats(_ context.Context) map[string]any {
// GetUserPermissions returns user permissions based on built-in roles
func (s *Service) GetUserPermissions(ctx context.Context, user identity.Requester, options accesscontrol.Options) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.GetUserPermissionsOSS")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.GetUserPermissions")
defer span.End()
timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary)
@ -130,7 +128,7 @@ func (s *Service) GetUserPermissions(ctx context.Context, user identity.Requeste
}
func (s *Service) getUserPermissions(ctx context.Context, user identity.Requester, _ accesscontrol.Options) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.getUserPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.getUserPermissions")
defer span.End()
permissions := make([]accesscontrol.Permission, 0)
@ -166,7 +164,7 @@ func (s *Service) getUserPermissions(ctx context.Context, user identity.Requeste
}
func (s *Service) getBasicRolePermissions(ctx context.Context, role string, orgID int64) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.getBasicRolePermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.getBasicRolePermissions")
defer span.End()
var permissions []accesscontrol.Permission
@ -188,7 +186,7 @@ func (s *Service) getBasicRolePermissions(ctx context.Context, role string, orgI
}
func (s *Service) getTeamsPermissions(ctx context.Context, teamIDs []int64, orgID int64) (map[int64][]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.getTeamsPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.getTeamsPermissions")
defer span.End()
teamPermissions, err := s.store.GetTeamsPermissions(ctx, accesscontrol.GetUserPermissionsQuery{
@ -208,7 +206,7 @@ func (s *Service) getTeamsPermissions(ctx context.Context, teamIDs []int64, orgI
// Returns only permissions directly assigned to user, without basic role and team permissions
func (s *Service) getUserDirectPermissions(ctx context.Context, user identity.Requester) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.getUserDirectPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.getUserDirectPermissions")
defer span.End()
var userID int64
@ -240,7 +238,7 @@ func (s *Service) getUserDirectPermissions(ctx context.Context, user identity.Re
}
func (s *Service) getCachedUserPermissions(ctx context.Context, user identity.Requester, options accesscontrol.Options) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.getCachedUserPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.getCachedUserPermissions")
defer span.End()
permissions := []accesscontrol.Permission{}
@ -266,7 +264,7 @@ func (s *Service) getCachedUserPermissions(ctx context.Context, user identity.Re
}
func (s *Service) getCachedBasicRolesPermissions(ctx context.Context, user identity.Requester, options accesscontrol.Options, permissions []accesscontrol.Permission) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.getCachedBasicRolesPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.getCachedBasicRolesPermissions")
defer span.End()
basicRoles := accesscontrol.GetOrgRoles(user)
@ -283,7 +281,7 @@ func (s *Service) getCachedBasicRolesPermissions(ctx context.Context, user ident
}
func (s *Service) getCachedBasicRolePermissions(ctx context.Context, role string, orgID int64, options accesscontrol.Options) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.getCachedBasicRolePermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.getCachedBasicRolePermissions")
defer span.End()
key := accesscontrol.GetBasicRolePermissionCacheKey(role, orgID)
@ -294,7 +292,7 @@ func (s *Service) getCachedBasicRolePermissions(ctx context.Context, role string
}
func (s *Service) getCachedUserDirectPermissions(ctx context.Context, user identity.Requester, options accesscontrol.Options) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.getCachedUserDirectPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.getCachedUserDirectPermissions")
defer span.End()
key := accesscontrol.GetUserDirectPermissionCacheKey(user)
@ -308,7 +306,7 @@ type getPermissionsFunc = func(ctx context.Context) ([]accesscontrol.Permission,
// Generic method for getting various permissions from cache
func (s *Service) getCachedPermissions(ctx context.Context, key string, getPermissionsFn getPermissionsFunc, options accesscontrol.Options) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.getCachedPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.getCachedPermissions")
defer span.End()
if !options.ReloadCache {
@ -333,7 +331,7 @@ func (s *Service) getCachedPermissions(ctx context.Context, key string, getPermi
}
func (s *Service) getCachedTeamsPermissions(ctx context.Context, user identity.Requester, options accesscontrol.Options, permissions []accesscontrol.Permission) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.getCachedTeamsPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.getCachedTeamsPermissions")
defer span.End()
teams := user.GetTeams()
@ -380,14 +378,14 @@ func (s *Service) ClearUserPermissionCache(user identity.Requester) {
}
func (s *Service) DeleteUserPermissions(ctx context.Context, orgID int64, userID int64) error {
ctx, span := s.tracer.Start(ctx, "authz.DeleteUserPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.DeleteUserPermissions")
defer span.End()
return s.store.DeleteUserPermissions(ctx, orgID, userID)
}
func (s *Service) DeleteTeamPermissions(ctx context.Context, orgID int64, teamID int64) error {
ctx, span := s.tracer.Start(ctx, "authz.DeleteTeamPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.DeleteTeamPermissions")
defer span.End()
return s.store.DeleteTeamPermissions(ctx, orgID, teamID)
@ -419,7 +417,7 @@ func (s *Service) DeclareFixedRoles(registrations ...accesscontrol.RoleRegistrat
// RegisterFixedRoles registers all declared roles in RAM
func (s *Service) RegisterFixedRoles(ctx context.Context) error {
_, span := s.tracer.Start(ctx, "authz.RegisterFixedRoles")
_, span := tracer.Start(ctx, "accesscontrol.acimpl.RegisterFixedRoles")
defer span.End()
s.registrations.Range(func(registration accesscontrol.RoleRegistration) bool {
@ -445,7 +443,7 @@ func (s *Service) RegisterFixedRoles(ctx context.Context) error {
// DeclarePluginRoles allow the caller to declare, to the service, plugin roles and their assignments
// to organization roles ("Viewer", "Editor", "Admin") or "Grafana Admin"
func (s *Service) DeclarePluginRoles(ctx context.Context, ID, name string, regs []plugins.RoleRegistration) error {
ctx, span := s.tracer.Start(ctx, "authz.DeclarePluginRoles")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.DeclarePluginRoles")
defer span.End()
// Protect behind feature toggle
@ -488,7 +486,7 @@ func GetActionFilter(options accesscontrol.SearchOptions) func(action string) bo
// SearchUsersPermissions returns all users' permissions filtered by action prefixes
func (s *Service) SearchUsersPermissions(ctx context.Context, usr identity.Requester, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.SearchUsersPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.SearchUsersPermissions")
defer span.End()
// Limit roles to available in OSS
@ -602,7 +600,7 @@ func (s *Service) SearchUsersPermissions(ctx context.Context, usr identity.Reque
}
func (s *Service) SearchUserPermissions(ctx context.Context, orgID int64, searchOptions accesscontrol.SearchOptions) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.SearchUserPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.SearchUserPermissions")
defer span.End()
timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary)
@ -619,7 +617,7 @@ func (s *Service) SearchUserPermissions(ctx context.Context, orgID int64, search
}
func (s *Service) searchUserPermissions(ctx context.Context, orgID int64, searchOptions accesscontrol.SearchOptions) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.searchUserPermissions")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.searchUserPermissions")
defer span.End()
userID, err := searchOptions.ComputeUserID()
@ -672,7 +670,7 @@ func (s *Service) searchUserPermissions(ctx context.Context, orgID int64, search
}
func (s *Service) searchUserPermissionsFromCache(ctx context.Context, orgID int64, searchOptions accesscontrol.SearchOptions) ([]accesscontrol.Permission, bool) {
_, span := s.tracer.Start(ctx, "authz.searchUserPermissionsFromCache")
_, span := tracer.Start(ctx, "accesscontrol.acimpl.searchUserPermissionsFromCache")
defer span.End()
userID, err := searchOptions.ComputeUserID()
@ -714,7 +712,7 @@ func PermissionMatchesSearchOptions(permission accesscontrol.Permission, searchO
}
func (s *Service) SaveExternalServiceRole(ctx context.Context, cmd accesscontrol.SaveExternalServiceRoleCommand) error {
ctx, span := s.tracer.Start(ctx, "authz.SaveExternalServiceRole")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.SaveExternalServiceRole")
defer span.End()
if !s.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAccounts) {
@ -730,7 +728,7 @@ func (s *Service) SaveExternalServiceRole(ctx context.Context, cmd accesscontrol
}
func (s *Service) DeleteExternalServiceRole(ctx context.Context, externalServiceID string) error {
ctx, span := s.tracer.Start(ctx, "authz.DeleteExternalServiceRole")
ctx, span := tracer.Start(ctx, "accesscontrol.acimpl.DeleteExternalServiceRole")
defer span.End()
if !s.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAccounts) {
@ -748,7 +746,7 @@ func (*Service) SyncUserRoles(ctx context.Context, orgID int64, cmd accesscontro
}
func (s *Service) GetRoleByName(ctx context.Context, orgID int64, roleName string) (*accesscontrol.RoleDTO, error) {
_, span := s.tracer.Start(ctx, "authz.GetRoleByName")
_, span := tracer.Start(ctx, "accesscontrol.acimpl.GetRoleByName")
defer span.End()
err := accesscontrol.ErrRoleNotFound

View File

@ -42,7 +42,6 @@ func setupTestEnv(t testing.TB) *Service {
log: log.New("accesscontrol"),
registrations: accesscontrol.RegistrationList{},
roles: accesscontrol.BuildBasicRoleDefinitions(),
tracer: tracing.InitializeTracerForTest(),
store: database.ProvideService(db.InitTestReplDB(t)),
permRegistry: permreg.ProvidePermissionRegistry(),
}

View File

@ -10,8 +10,11 @@ import (
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"go.opentelemetry.io/otel"
)
var tracer = otel.Tracer("github.com/grafana/grafana/pkg/services/accesscontrol/api")
func NewAccessControlAPI(router routing.RouteRegister, accesscontrol ac.AccessControl, service ac.Service,
features featuremgmt.FeatureToggles) *AccessControlAPI {
return &AccessControlAPI{
@ -43,8 +46,11 @@ func (api *AccessControlAPI) RegisterAPIEndpoints() {
// GET /api/access-control/user/actions
func (api *AccessControlAPI) getUserActions(c *contextmodel.ReqContext) response.Response {
ctx, span := tracer.Start(c.Req.Context(), "accesscontrol.api.getUserActions")
defer span.End()
reloadCache := c.QueryBool("reloadcache")
permissions, err := api.Service.GetUserPermissions(c.Req.Context(),
permissions, err := api.Service.GetUserPermissions(ctx,
c.SignedInUser, ac.Options{ReloadCache: reloadCache})
if err != nil {
return response.JSON(http.StatusInternalServerError, err)
@ -55,18 +61,24 @@ func (api *AccessControlAPI) getUserActions(c *contextmodel.ReqContext) response
// GET /api/access-control/user/permissions
func (api *AccessControlAPI) getUserPermissions(c *contextmodel.ReqContext) response.Response {
ctx, span := tracer.Start(c.Req.Context(), "accesscontrol.api.getUserPermissions")
defer span.End()
reloadCache := c.QueryBool("reloadcache")
permissions, err := api.Service.GetUserPermissions(c.Req.Context(),
permissions, err := api.Service.GetUserPermissions(ctx,
c.SignedInUser, ac.Options{ReloadCache: reloadCache})
if err != nil {
return response.JSON(http.StatusInternalServerError, err)
}
return response.JSON(http.StatusOK, ac.GroupScopesByActionContext(c.Req.Context(), permissions))
return response.JSON(http.StatusOK, ac.GroupScopesByActionContext(ctx, permissions))
}
// GET /api/access-control/users/permissions/search
func (api *AccessControlAPI) searchUsersPermissions(c *contextmodel.ReqContext) response.Response {
ctx, span := tracer.Start(c.Req.Context(), "accesscontrol.api.searchUsersPermissions")
defer span.End()
searchOptions := ac.SearchOptions{
ActionPrefix: c.Query("actionPrefix"),
Action: c.Query("action"),
@ -84,7 +96,7 @@ func (api *AccessControlAPI) searchUsersPermissions(c *contextmodel.ReqContext)
}
// Compute metadata
permissions, err := api.Service.SearchUsersPermissions(c.Req.Context(), c.SignedInUser, searchOptions)
permissions, err := api.Service.SearchUsersPermissions(ctx, c.SignedInUser, searchOptions)
if err != nil {
return response.Error(http.StatusInternalServerError, "could not get org user permissions", err)
}

View File

@ -8,8 +8,11 @@ import (
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"go.opentelemetry.io/otel"
)
var tracer = otel.Tracer("github.com/grafana/grafana/pkg/services/accesscontrol/database")
const (
// userAssignsSQL is a query to select all users assignments.
userAssignsSQL = `SELECT ur.user_id, ur.org_id, ur.role_id
@ -45,6 +48,9 @@ type AccessControlStore struct {
}
func (s *AccessControlStore) GetUserPermissions(ctx context.Context, query accesscontrol.GetUserPermissionsQuery) ([]accesscontrol.Permission, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.database.GetUserPermissions")
defer span.End()
result := make([]accesscontrol.Permission, 0)
err := s.sql.ReadReplica().WithDbSession(ctx, func(sess *db.Session) error {
if query.UserID == 0 && len(query.TeamIDs) == 0 && len(query.Roles) == 0 {
@ -100,6 +106,9 @@ func (p teamPermission) Permission() accesscontrol.Permission {
}
func (s *AccessControlStore) GetTeamsPermissions(ctx context.Context, query accesscontrol.GetUserPermissionsQuery) (map[int64][]accesscontrol.Permission, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.database.GetTeamsPermissions")
defer span.End()
teams := query.TeamIDs
orgID := query.OrgID
rolePrefixes := query.RolePrefixes
@ -156,6 +165,9 @@ func (s *AccessControlStore) GetTeamsPermissions(ctx context.Context, query acce
// SearchUsersPermissions returns the list of user permissions in specific organization indexed by UserID
func (s *AccessControlStore) SearchUsersPermissions(ctx context.Context, orgID int64, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.database.SearchUsersPermissions")
defer span.End()
type UserRBACPermission struct {
UserID int64 `xorm:"user_id"`
Action string `xorm:"action"`
@ -278,6 +290,9 @@ func (s *AccessControlStore) SearchUsersPermissions(ctx context.Context, orgID i
// GetUsersBasicRoles returns the list of user basic roles (Admin, Editor, Viewer, Grafana Admin) indexed by UserID
func (s *AccessControlStore) GetUsersBasicRoles(ctx context.Context, userFilter []int64, orgID int64) (map[int64][]string, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.database.GetUsersBasicRoles")
defer span.End()
type UserOrgRole struct {
UserID int64 `xorm:"id"`
OrgRole string `xorm:"role"`
@ -318,6 +333,9 @@ func (s *AccessControlStore) GetUsersBasicRoles(ctx context.Context, userFilter
}
func (s *AccessControlStore) DeleteUserPermissions(ctx context.Context, orgID, userID int64) error {
ctx, span := tracer.Start(ctx, "accesscontrol.database.DeleteUserPermissions")
defer span.End()
err := s.sql.DB().WithDbSession(ctx, func(sess *db.Session) error {
roleDeleteQuery := "DELETE FROM user_role WHERE user_id = ?"
roleDeleteParams := []any{roleDeleteQuery, userID}
@ -383,6 +401,9 @@ func (s *AccessControlStore) DeleteUserPermissions(ctx context.Context, orgID, u
}
func (s *AccessControlStore) DeleteTeamPermissions(ctx context.Context, orgID, teamID int64) error {
ctx, span := tracer.Start(ctx, "accesscontrol.database.DeleteTeamPermissions")
defer span.End()
err := s.sql.DB().WithDbSession(ctx, func(sess *db.Session) error {
roleDeleteQuery := "DELETE FROM team_role WHERE team_id = ? AND org_id = ?"
roleDeleteParams := []any{roleDeleteQuery, teamID, orgID}

View File

@ -17,6 +17,9 @@ func extServiceRoleName(externalServiceID string) string {
}
func (s *AccessControlStore) DeleteExternalServiceRole(ctx context.Context, externalServiceID string) error {
ctx, span := tracer.Start(ctx, "accesscontrol.database.DeleteExternalServiceRole")
defer span.End()
uid := accesscontrol.PrefixedRoleUID(extServiceRoleName(externalServiceID))
return s.sql.DB().WithDbSession(ctx, func(sess *db.Session) error {
stored, errGet := getRoleByUID(ctx, sess, uid)
@ -52,6 +55,9 @@ func (s *AccessControlStore) DeleteExternalServiceRole(ctx context.Context, exte
}
func (s *AccessControlStore) SaveExternalServiceRole(ctx context.Context, cmd accesscontrol.SaveExternalServiceRoleCommand) error {
ctx, span := tracer.Start(ctx, "accesscontrol.database.SaveExternalServiceRole")
defer span.End()
role := genExternalServiceRole(cmd)
assignment := genExternalServiceAssignment(cmd)
@ -103,6 +109,9 @@ func genExternalServiceAssignment(cmd accesscontrol.SaveExternalServiceRoleComma
}
func getRoleByUID(ctx context.Context, sess *db.Session, uid string) (*accesscontrol.Role, error) {
_, span := tracer.Start(ctx, "accesscontrol.database.getRoleByUID")
defer span.End()
var role accesscontrol.Role
has, err := sess.Where("uid = ?", uid).Get(&role)
if err != nil {
@ -115,6 +124,9 @@ func getRoleByUID(ctx context.Context, sess *db.Session, uid string) (*accesscon
}
func getRoleAssignments(ctx context.Context, sess *db.Session, roleID int64) ([]accesscontrol.UserRole, error) {
_, span := tracer.Start(ctx, "accesscontrol.database.GgetRoleAssignments")
defer span.End()
var assignements []accesscontrol.UserRole
if err := sess.Where("role_id = ?", roleID).Find(&assignements); err != nil {
return nil, err
@ -123,6 +135,9 @@ func getRoleAssignments(ctx context.Context, sess *db.Session, roleID int64) ([]
}
func getRolePermissions(ctx context.Context, sess *db.Session, id int64) ([]accesscontrol.Permission, error) {
_, span := tracer.Start(ctx, "accesscontrol.database.getRolePermissions")
defer span.End()
var permissions []accesscontrol.Permission
if err := sess.Where("role_id = ?", id).Find(&permissions); err != nil {
return nil, err
@ -157,6 +172,9 @@ func permissionDiff(previous, new []accesscontrol.Permission) (added, removed []
}
func (*AccessControlStore) saveRole(ctx context.Context, sess *db.Session, role *accesscontrol.Role) (*accesscontrol.Role, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.database.saveRole")
defer span.End()
existingRole, err := getRoleByUID(ctx, sess, role.UID)
if err != nil && !errors.Is(err, accesscontrol.ErrRoleNotFound) {
return nil, err
@ -177,6 +195,9 @@ func (*AccessControlStore) saveRole(ctx context.Context, sess *db.Session, role
}
func (*AccessControlStore) savePermissions(ctx context.Context, sess *db.Session, roleID int64, permissions []accesscontrol.Permission) error {
ctx, span := tracer.Start(ctx, "accesscontrol.database.savePermissions")
defer span.End()
now := time.Now()
storedPermissions, err := getRolePermissions(ctx, sess, roleID)
if err != nil {
@ -210,6 +231,9 @@ func (*AccessControlStore) savePermissions(ctx context.Context, sess *db.Session
}
func (*AccessControlStore) saveUserAssignment(ctx context.Context, sess *db.Session, assignment accesscontrol.UserRole) error {
ctx, span := tracer.Start(ctx, "accesscontrol.database.saveUserAssignment")
defer span.End()
// alreadyAssigned checks if the assignment already exists without accounting for the organization
assignments, errGetAssigns := getRoleAssignments(ctx, sess, assignment.RoleID)
if errGetAssigns != nil {

View File

@ -13,6 +13,9 @@ import (
)
func GetAccessPolicies(ctx context.Context, orgID int64, sql *session.SessionDB, resolver accesscontrol.ScopeAttributeResolverFunc) ([]accesspolicy.Resource, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.database.GetAccessPolicies")
defer span.End()
type permissionInfo struct {
RoleUID string
RoleName string

View File

@ -104,6 +104,9 @@ func (p permissionEvaluator) EvaluateCustom(fn CheckerFn) (bool, error) {
}
func (p permissionEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttributeMutator) (Evaluator, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.permissionEvaluatorMutateScopes")
defer span.End()
if p.Scopes == nil {
return EvalPermission(p.Action), nil
}
@ -172,6 +175,9 @@ func (a allEvaluator) EvaluateCustom(fn CheckerFn) (bool, error) {
}
func (a allEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttributeMutator) (Evaluator, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.allEvaluator.MutateScopes")
defer span.End()
resolved := false
modified := make([]Evaluator, 0, len(a.allOf))
for _, e := range a.allOf {
@ -245,6 +251,9 @@ func (a anyEvaluator) EvaluateCustom(fn CheckerFn) (bool, error) {
}
func (a anyEvaluator) MutateScopes(ctx context.Context, mutate ScopeAttributeMutator) (Evaluator, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.anyEvaluator.MutateScopes")
defer span.End()
resolved := false
modified := make([]Evaluator, 0, len(a.anyOf))
for _, e := range a.anyOf {

View File

@ -11,6 +11,9 @@ type Metadata map[string]bool
// GetResourcesMetadata returns a map of accesscontrol metadata, listing for each resource, users available actions
func GetResourcesMetadata(ctx context.Context, permissions map[string][]string, prefix string, resourceIDs map[string]bool) map[string]Metadata {
_, span := tracer.Start(ctx, "accesscontrol.GetResourcesMetadata")
defer span.End()
wildcards := WildcardsFromPrefix(prefix)
// index of the prefix in the scope

View File

@ -29,6 +29,10 @@ import (
func Middleware(ac AccessControl) func(Evaluator) web.Handler {
return func(evaluator Evaluator) web.Handler {
return func(c *contextmodel.ReqContext) {
ctx, span := tracer.Start(c.Req.Context(), "accesscontrol.Middleware")
defer span.End()
c.Req = c.Req.WithContext(ctx)
if c.AllowAnonymous {
forceLogin, _ := strconv.ParseBool(c.Req.URL.Query().Get("forceLogin")) // ignoring error, assuming false for non-true values is ok.
orgID, err := strconv.ParseInt(c.Req.URL.Query().Get("orgId"), 10, 64)
@ -59,7 +63,11 @@ func Middleware(ac AccessControl) func(Evaluator) web.Handler {
}
func authorize(c *contextmodel.ReqContext, ac AccessControl, user identity.Requester, evaluator Evaluator) {
injected, err := evaluator.MutateScopes(c.Req.Context(), scopeInjector(scopeParams{
ctx, span := tracer.Start(c.Req.Context(), "accesscontrol.authorize")
defer span.End()
c.Req = c.Req.WithContext(ctx)
injected, err := evaluator.MutateScopes(ctx, scopeInjector(scopeParams{
OrgID: user.GetOrgID(),
URLParams: web.Params(c.Req),
}))
@ -68,7 +76,7 @@ func authorize(c *contextmodel.ReqContext, ac AccessControl, user identity.Reque
return
}
hasAccess, err := ac.Evaluate(c.Req.Context(), user, injected)
hasAccess, err := ac.Evaluate(ctx, user, injected)
if !hasAccess || err != nil {
deny(c, injected, err)
return
@ -177,6 +185,10 @@ type OrgIDGetter func(c *contextmodel.ReqContext) (int64, error)
func AuthorizeInOrgMiddleware(ac AccessControl, authnService authn.Service) func(OrgIDGetter, Evaluator) web.Handler {
return func(getTargetOrg OrgIDGetter, evaluator Evaluator) web.Handler {
return func(c *contextmodel.ReqContext) {
ctx, span := tracer.Start(c.Req.Context(), "accesscontrol.AuthorizeInOrgMiddleware")
defer span.End()
c.Req = c.Req.WithContext(ctx)
targetOrgID, err := getTargetOrg(c)
if err != nil {
if errors.Is(err, ErrInvalidRequestBody) {

View File

@ -8,12 +8,15 @@ import (
"strings"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
"go.opentelemetry.io/otel"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/authz/zanzana"
)
var tracer = otel.Tracer("github.com/grafana/grafana/pkg/accesscontrol/migrator")
// A TupleCollector is responsible to build and store [openfgav1.TupleKey] into provided tuple map.
// They key used should be a unique group key for the collector so we can skip over an already synced group.
type TupleCollector func(ctx context.Context, tuples map[string][]*openfgav1.TupleKey) error
@ -54,6 +57,9 @@ func NewZanzanaSynchroniser(client zanzana.Client, store db.DB, collectors ...Tu
// Sync runs all collectors and tries to write all collected tuples.
// It will skip over any "sync group" that has already been written.
func (z *ZanzanaSynchroniser) Sync(ctx context.Context) error {
ctx, span := tracer.Start(ctx, "accesscontrol.migrator.Sync")
defer span.End()
tuplesMap := make(map[string][]*openfgav1.TupleKey)
for _, c := range z.collectors {
@ -145,6 +151,9 @@ func managedPermissionsCollector(store db.DB) TupleCollector {
func teamMembershipCollector(store db.DB) TupleCollector {
return func(ctx context.Context, tuples map[string][]*openfgav1.TupleKey) error {
ctx, span := tracer.Start(ctx, "accesscontrol.migrator.teamMembershipCollector")
defer span.End()
const collectorID = "team_membership"
const query = `
SELECT t.uid as team_uid, u.uid as user_uid, tm.permission
@ -191,6 +200,9 @@ func teamMembershipCollector(store db.DB) TupleCollector {
// folderTreeCollector collects folder tree structure and writes it as relation tuples
func folderTreeCollector(store db.DB) TupleCollector {
return func(ctx context.Context, tuples map[string][]*openfgav1.TupleKey) error {
ctx, span := tracer.Start(ctx, "accesscontrol.migrator.folderTreeCollector")
defer span.End()
const collectorID = "folder"
const query = `
SELECT uid, parent_uid, org_id FROM folder
@ -236,6 +248,9 @@ func folderTreeCollector(store db.DB) TupleCollector {
// dashboardFolderCollector collects information about dashboards parent folders
func dashboardFolderCollector(store db.DB) TupleCollector {
return func(ctx context.Context, tuples map[string][]*openfgav1.TupleKey) error {
ctx, span := tracer.Start(ctx, "accesscontrol.migrator.dashboardFolderCollector")
defer span.End()
const collectorID = "folder"
const query = `
SELECT org_id, uid, folder_uid, is_folder FROM dashboard WHERE is_folder = 0 AND folder_uid IS NOT NULL

View File

@ -113,6 +113,9 @@ func ProvideDashboardPermissions(
Resource: "dashboards",
ResourceAttribute: "uid",
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
ctx, span := tracer.Start(ctx, "accesscontrol.ossaccesscontrol.ProvideDashboardPermissions.ResourceValidator")
defer span.End()
dashboard, err := getDashboard(ctx, orgID, resourceID)
if err != nil {
return err

View File

@ -10,8 +10,11 @@ import (
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
"go.opentelemetry.io/otel"
)
var tracer = otel.Tracer("github.com/grafana/grafana/pkg/accesscontrol/ossaccesscontrol")
// DatasourceQueryActions contains permissions to read information
// about a data source and submit arbitrary queries to it.
var DatasourceQueryActions = []string{
@ -51,6 +54,9 @@ func (e DatasourcePermissionsService) SetBuiltInRolePermission(ctx context.Conte
// if an OSS/unlicensed instance is upgraded to Enterprise/licensed.
// https://github.com/grafana/identity-access-team/issues/672
func (e DatasourcePermissionsService) SetPermissions(ctx context.Context, orgID int64, resourceID string, commands ...accesscontrol.SetResourcePermissionCommand) ([]accesscontrol.ResourcePermission, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.ossaccesscontrol.SetPermissions")
defer span.End()
dbCommands := make([]resourcepermissions.SetResourcePermissionsCommand, 0, len(commands))
for _, cmd := range commands {
// Only set query permissions for built-in roles; do not set permissions for data sources with * as UID, as this would grant wildcard permissions
@ -75,6 +81,9 @@ func (e DatasourcePermissionsService) SetPermissions(ctx context.Context, orgID
}
func (e DatasourcePermissionsService) DeleteResourcePermissions(ctx context.Context, orgID int64, resourceID string) error {
ctx, span := tracer.Start(ctx, "accesscontrol.ossaccesscontrol.DeleteResourcePermissions")
defer span.End()
return e.store.DeleteResourcePermissions(ctx, orgID, &resourcepermissions.DeleteResourcePermissionsCmd{
Resource: datasources.ScopeRoot,
ResourceAttribute: "uid",

View File

@ -95,6 +95,9 @@ func ProvideFolderPermissions(
Resource: "folders",
ResourceAttribute: "uid",
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
ctx, span := tracer.Start(ctx, "accesscontrol.ossaccesscontrol.ProvideFolderPermissions.ResourceValidator")
defer span.End()
query := &dashboards.GetDashboardQuery{UID: resourceID, OrgID: orgID}
queryResult, err := dashboardStore.GetDashboard(ctx, query)
if err != nil {

View File

@ -44,6 +44,9 @@ func ProvideServiceAccountPermissions(
Resource: "serviceaccounts",
ResourceAttribute: "id",
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
ctx, span := tracer.Start(ctx, "accesscontrol.ossaccesscontrol.ProvideServiceAccountPermissions.ResourceValidator")
defer span.End()
id, err := strconv.ParseInt(resourceID, 10, 64)
if err != nil {
return err

View File

@ -46,12 +46,15 @@ func ProvideTeamPermissions(
ResourceAttribute: "id",
OnlyManaged: true,
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
ctx, span := tracer.Start(ctx, "accesscontrol.ossaccesscontrol.ProvideTeamerPermissions.ResourceValidator")
defer span.End()
id, err := strconv.ParseInt(resourceID, 10, 64)
if err != nil {
return err
}
_, err = teamService.GetTeamByID(context.Background(), &team.GetTeamByIDQuery{
_, err = teamService.GetTeamByID(ctx, &team.GetTeamByIDQuery{
OrgID: orgID,
ID: id,
})

View File

@ -62,6 +62,9 @@ func (s *Resolvers) AddScopeAttributeResolver(prefix string, resolver ScopeAttri
func (s *Resolvers) GetScopeAttributeMutator(orgID int64) ScopeAttributeMutator {
return func(ctx context.Context, scope string) ([]string, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.GetScopeAttributeMutator")
defer span.End()
key := getScopeCacheKey(orgID, scope)
// Check cache before computing the scope
if cachedScope, ok := s.cache.Get(key); ok {

View File

@ -13,8 +13,11 @@ import (
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
"go.opentelemetry.io/otel"
)
var tracer = otel.Tracer("github.com/grafana/grafana/pkg/accesscontrol/resourcepermissions")
type api struct {
cfg *setting.Cfg
ac accesscontrol.AccessControl
@ -140,6 +143,10 @@ type getResourcePermissionsResponse []resourcePermissionDTO
// 404: notFoundError
// 500: internalServerError
func (a *api) getPermissions(c *contextmodel.ReqContext) response.Response {
ctx, span := tracer.Start(c.Req.Context(), "accesscontrol.resourcepermissions.getPermissions")
defer span.End()
c.Req = c.Req.WithContext(ctx)
resourceID := web.Params(c.Req)[":resourceID"]
permissions, err := a.service.GetPermissions(c.Req.Context(), c.SignedInUser, resourceID)
@ -227,6 +234,10 @@ type SetResourcePermissionsForUserParams struct {
// 404: notFoundError
// 500: internalServerError
func (a *api) setUserPermission(c *contextmodel.ReqContext) response.Response {
ctx, span := tracer.Start(c.Req.Context(), "accesscontrol.resourcepermissions.setUserPermission")
defer span.End()
c.Req = c.Req.WithContext(ctx)
userID, err := strconv.ParseInt(web.Params(c.Req)[":userID"], 10, 64)
if err != nil {
return response.Err(ErrInvalidParam.Build(ErrInvalidParamData("userID", err)))
@ -280,6 +291,10 @@ type SetResourcePermissionsForTeamParams struct {
// 404: notFoundError
// 500: internalServerError
func (a *api) setTeamPermission(c *contextmodel.ReqContext) response.Response {
ctx, span := tracer.Start(c.Req.Context(), "accesscontrol.resourcepermissions.setTeamPermission")
defer span.End()
c.Req = c.Req.WithContext(ctx)
teamID, err := strconv.ParseInt(web.Params(c.Req)[":teamID"], 10, 64)
if err != nil {
return response.Err(ErrInvalidParam.Build(ErrInvalidParamData("teamID", err)))
@ -333,6 +348,10 @@ type SetResourcePermissionsForBuiltInRoleParams struct {
// 404: notFoundError
// 500: internalServerError
func (a *api) setBuiltinRolePermission(c *contextmodel.ReqContext) response.Response {
ctx, span := tracer.Start(c.Req.Context(), "accesscontrol.resourcepermissions.setBuiltinRolePermission")
defer span.End()
c.Req = c.Req.WithContext(ctx)
builtInRole := web.Params(c.Req)[":builtInRole"]
resourceID := web.Params(c.Req)[":resourceID"]
@ -379,6 +398,9 @@ type SetResourcePermissionsParams struct {
// 404: notFoundError
// 500: internalServerError
func (a *api) setPermissions(c *contextmodel.ReqContext) response.Response {
ctx, span := tracer.Start(c.Req.Context(), "accesscontrol.resourcepermissions.setPermissions")
defer span.End()
resourceID := web.Params(c.Req)[":resourceID"]
cmd := setPermissionsCommand{}
@ -386,7 +408,7 @@ func (a *api) setPermissions(c *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "Bad request data: "+err.Error(), err)
}
_, err := a.service.SetPermissions(c.Req.Context(), c.SignedInUser.GetOrgID(), resourceID, cmd.Permissions...)
_, err := a.service.SetPermissions(ctx, c.SignedInUser.GetOrgID(), resourceID, cmd.Permissions...)
if err != nil {
return response.Err(err)
}

View File

@ -138,6 +138,9 @@ type Service struct {
}
func (s *Service) GetPermissions(ctx context.Context, user identity.Requester, resourceID string) ([]accesscontrol.ResourcePermission, error) {
ctx, span := tracer.Start(ctx, "accesscontroll.resourcepermissions.GetPermissions")
defer span.End()
var inheritedScopes []string
if s.options.InheritedScopesSolver != nil {
var err error
@ -206,6 +209,9 @@ func (s *Service) GetPermissions(ctx context.Context, user identity.Requester, r
}
func (s *Service) SetUserPermission(ctx context.Context, orgID int64, user accesscontrol.User, resourceID, permission string) (*accesscontrol.ResourcePermission, error) {
ctx, span := tracer.Start(ctx, "accesscontroll.resourcepermissions.SetUserPermission")
defer span.End()
actions, err := s.mapPermission(permission)
if err != nil {
return nil, err
@ -229,6 +235,9 @@ func (s *Service) SetUserPermission(ctx context.Context, orgID int64, user acces
}
func (s *Service) SetTeamPermission(ctx context.Context, orgID, teamID int64, resourceID, permission string) (*accesscontrol.ResourcePermission, error) {
ctx, span := tracer.Start(ctx, "accesscontroll.resourcepermissions.SetTeamPermission")
defer span.End()
actions, err := s.mapPermission(permission)
if err != nil {
return nil, err
@ -252,6 +261,9 @@ func (s *Service) SetTeamPermission(ctx context.Context, orgID, teamID int64, re
}
func (s *Service) SetBuiltInRolePermission(ctx context.Context, orgID int64, builtInRole, resourceID, permission string) (*accesscontrol.ResourcePermission, error) {
ctx, span := tracer.Start(ctx, "accesscontroll.resourcepermissions.SetBuiltInRolePermission")
defer span.End()
actions, err := s.mapPermission(permission)
if err != nil {
return nil, err
@ -278,6 +290,9 @@ func (s *Service) SetPermissions(
ctx context.Context, orgID int64, resourceID string,
commands ...accesscontrol.SetResourcePermissionCommand,
) ([]accesscontrol.ResourcePermission, error) {
ctx, span := tracer.Start(ctx, "accesscontroll.resourcepermissions.SetPermissions")
defer span.End()
if err := s.validateResource(ctx, orgID, resourceID); err != nil {
return nil, err
}
@ -355,6 +370,9 @@ func (s *Service) mapPermission(permission string) ([]string, error) {
}
func (s *Service) validateResource(ctx context.Context, orgID int64, resourceID string) error {
ctx, span := tracer.Start(ctx, "accesscontroll.resourcepermissions.validateResource")
defer span.End()
if s.options.ResourceValidator != nil {
return s.options.ResourceValidator(ctx, orgID, resourceID)
}
@ -362,6 +380,9 @@ func (s *Service) validateResource(ctx context.Context, orgID int64, resourceID
}
func (s *Service) validateUser(ctx context.Context, orgID, userID int64) error {
ctx, span := tracer.Start(ctx, "accesscontroll.resourcepermissions.validateUser")
defer span.End()
if !s.options.Assignments.Users {
return ErrInvalidAssignment.Build(ErrInvalidAssignmentData("users"))
}
@ -376,6 +397,9 @@ func (s *Service) validateUser(ctx context.Context, orgID, userID int64) error {
}
func (s *Service) validateTeam(ctx context.Context, orgID, teamID int64) error {
ctx, span := tracer.Start(ctx, "accesscontroll.resourcepermissions.validateTeam")
defer span.End()
if !s.options.Assignments.Teams {
return ErrInvalidAssignment.Build(ErrInvalidAssignmentData("teams"))
}
@ -391,7 +415,10 @@ func (s *Service) validateTeam(ctx context.Context, orgID, teamID int64) error {
return nil
}
func (s *Service) validateBuiltinRole(_ context.Context, builtinRole string) error {
func (s *Service) validateBuiltinRole(ctx context.Context, builtinRole string) error {
_, span := tracer.Start(ctx, "accesscontroll.resourcepermissions.validateBuiltinRole")
defer span.End()
if !s.options.Assignments.BuiltInRoles {
return ErrInvalidAssignment.Build(ErrInvalidAssignmentData("builtInRoles"))
}
@ -553,6 +580,9 @@ func (a *ActionSetSvc) ExpandActionSetsWithFilter(permissions []accesscontrol.Pe
// RegisterActionSets allow the caller to expand the existing action sets with additional permissions
// This is intended to be used by plugins, and currently supports extending folder and dashboard action sets
func (a *ActionSetSvc) RegisterActionSets(ctx context.Context, pluginID string, registrations []plugins.ActionSet) error {
ctx, span := tracer.Start(ctx, "accesscontroll.resourcepermissions.RegisterActionSets")
defer span.End()
if !a.features.IsEnabled(ctx, featuremgmt.FlagAccessActionSets) || !a.features.IsEnabled(ctx, featuremgmt.FlagAccessControlOnCall) {
return nil
}

View File

@ -63,6 +63,9 @@ type DeleteResourcePermissionsCmd struct {
}
func (s *store) DeleteResourcePermissions(ctx context.Context, orgID int64, cmd *DeleteResourcePermissionsCmd) error {
ctx, span := tracer.Start(ctx, "accesscontrol.resourcepermissions.DeleteResourcePermissions")
defer span.End()
scope := accesscontrol.Scope(cmd.Resource, cmd.ResourceAttribute, cmd.ResourceID)
err := s.sql.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
@ -88,6 +91,9 @@ func (s *store) SetUserResourcePermission(
cmd SetResourcePermissionCommand,
hook UserResourceHookFunc,
) (*accesscontrol.ResourcePermission, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.resourcepermissions.SetUserResourcePermission")
defer span.End()
if usr.ID == 0 {
return nil, user.ErrUserNotFound
}
@ -125,6 +131,9 @@ func (s *store) SetTeamResourcePermission(
cmd SetResourcePermissionCommand,
hook TeamResourceHookFunc,
) (*accesscontrol.ResourcePermission, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.resourcepermissions.SetTeamResourcePermission")
defer span.End()
if teamID == 0 {
return nil, team.ErrTeamNotFound
}
@ -164,6 +173,9 @@ func (s *store) SetBuiltInResourcePermission(
cmd SetResourcePermissionCommand,
hook BuiltinResourceHookFunc,
) (*accesscontrol.ResourcePermission, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.resourcepermissions.SetBuiltInResourcePermission")
defer span.End()
if !org.RoleType(builtInRole).IsValid() || builtInRole == accesscontrol.RoleGrafanaAdmin {
return nil, fmt.Errorf("invalid role: %s", builtInRole)
}
@ -207,6 +219,9 @@ func (s *store) SetResourcePermissions(
commands []SetResourcePermissionsCommand,
hooks ResourceHooks,
) ([]accesscontrol.ResourcePermission, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.resourcepermissions.SetResourcePermissions")
defer span.End()
var err error
var permissions []accesscontrol.ResourcePermission
@ -288,6 +303,9 @@ func (s *store) setResourcePermission(
}
func (s *store) GetResourcePermissions(ctx context.Context, orgID int64, query GetResourcePermissionsQuery) ([]accesscontrol.ResourcePermission, error) {
ctx, span := tracer.Start(ctx, "accesscontrol.resourcepermissions.GetResourcePermissions")
defer span.End()
var result []accesscontrol.ResourcePermission
err := s.sql.WithDbSession(ctx, func(sess *db.Session) error {