merge (#45335)
Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
@@ -7,12 +7,16 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
ReloadCache bool
|
||||
}
|
||||
|
||||
type AccessControl interface {
|
||||
// Evaluate evaluates access to the given resources.
|
||||
Evaluate(ctx context.Context, user *models.SignedInUser, evaluator Evaluator) (bool, error)
|
||||
|
||||
// GetUserPermissions returns user permissions.
|
||||
GetUserPermissions(ctx context.Context, user *models.SignedInUser) ([]*Permission, error)
|
||||
GetUserPermissions(ctx context.Context, user *models.SignedInUser, options Options) ([]*Permission, error)
|
||||
|
||||
// GetUserRoles returns user roles.
|
||||
GetUserRoles(ctx context.Context, user *models.SignedInUser) ([]*RoleDTO, error)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
)
|
||||
|
||||
type AccessControlAPI struct {
|
||||
RouteRegister routing.RouteRegister
|
||||
AccessControl ac.AccessControl
|
||||
}
|
||||
|
||||
func (api *AccessControlAPI) RegisterAPIEndpoints() {
|
||||
// Users
|
||||
api.RouteRegister.Get("/api/access-control/user/permissions",
|
||||
middleware.ReqSignedIn, routing.Wrap(api.getUsersPermissions))
|
||||
}
|
||||
|
||||
// GET /api/access-control/user/permissions
|
||||
func (api *AccessControlAPI) getUsersPermissions(c *models.ReqContext) response.Response {
|
||||
reloadCache := c.QueryBool("reloadcache")
|
||||
permissions, err := api.AccessControl.GetUserPermissions(c.Req.Context(),
|
||||
c.SignedInUser, ac.Options{ReloadCache: reloadCache})
|
||||
if err != nil {
|
||||
response.JSON(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, ac.BuildPermissionsMap(permissions))
|
||||
}
|
||||
@@ -156,7 +156,8 @@ func LoadPermissionsMiddleware(ac accesscontrol.AccessControl) web.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
permissions, err := ac.GetUserPermissions(c.Req.Context(), c.SignedInUser)
|
||||
permissions, err := ac.GetUserPermissions(c.Req.Context(), c.SignedInUser,
|
||||
accesscontrol.Options{ReloadCache: false})
|
||||
if err != nil {
|
||||
c.JsonApiErr(http.StatusForbidden, "", err)
|
||||
return
|
||||
|
||||
@@ -39,7 +39,7 @@ type Mock struct {
|
||||
|
||||
// Override functions
|
||||
EvaluateFunc func(context.Context, *models.SignedInUser, accesscontrol.Evaluator) (bool, error)
|
||||
GetUserPermissionsFunc func(context.Context, *models.SignedInUser) ([]*accesscontrol.Permission, error)
|
||||
GetUserPermissionsFunc func(context.Context, *models.SignedInUser, accesscontrol.Options) ([]*accesscontrol.Permission, error)
|
||||
GetUserRolesFunc func(context.Context, *models.SignedInUser) ([]*accesscontrol.RoleDTO, error)
|
||||
IsDisabledFunc func() bool
|
||||
DeclareFixedRolesFunc func(...accesscontrol.RoleRegistration) error
|
||||
@@ -86,7 +86,7 @@ func (m *Mock) Evaluate(ctx context.Context, user *models.SignedInUser, evaluato
|
||||
return m.EvaluateFunc(ctx, user, evaluator)
|
||||
}
|
||||
// Otherwise perform an actual evaluation of the permissions
|
||||
permissions, err := m.GetUserPermissions(ctx, user)
|
||||
permissions, err := m.GetUserPermissions(ctx, user, accesscontrol.Options{ReloadCache: false})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -95,11 +95,12 @@ func (m *Mock) Evaluate(ctx context.Context, user *models.SignedInUser, evaluato
|
||||
|
||||
// GetUserPermissions returns user permissions.
|
||||
// This mock return m.permissions unless an override is provided.
|
||||
func (m *Mock) GetUserPermissions(ctx context.Context, user *models.SignedInUser) ([]*accesscontrol.Permission, error) {
|
||||
m.Calls.GetUserPermissions = append(m.Calls.GetUserPermissions, []interface{}{ctx, user})
|
||||
func (m *Mock) GetUserPermissions(ctx context.Context, user *models.SignedInUser,
|
||||
opts accesscontrol.Options) ([]*accesscontrol.Permission, error) {
|
||||
m.Calls.GetUserPermissions = append(m.Calls.GetUserPermissions, []interface{}{ctx, user, opts})
|
||||
// Use override if provided
|
||||
if m.GetUserPermissionsFunc != nil {
|
||||
return m.GetUserPermissionsFunc(ctx, user)
|
||||
return m.GetUserPermissionsFunc(ctx, user, opts)
|
||||
}
|
||||
// Otherwise return the Permissions list
|
||||
return m.permissions, nil
|
||||
|
||||
@@ -4,19 +4,29 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/api"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/resourceservices"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func ProvideService(features featuremgmt.FeatureToggles, usageStats usagestats.Service, provider accesscontrol.PermissionsProvider) *OSSAccessControlService {
|
||||
func ProvideService(features featuremgmt.FeatureToggles, usageStats usagestats.Service,
|
||||
provider accesscontrol.PermissionsProvider, routeRegister routing.RouteRegister) *OSSAccessControlService {
|
||||
s := ProvideOSSAccessControl(features, usageStats, provider)
|
||||
s.registerUsageMetrics()
|
||||
if !s.IsDisabled() {
|
||||
api := api.AccessControlAPI{
|
||||
RouteRegister: routeRegister,
|
||||
AccessControl: s,
|
||||
}
|
||||
api.RegisterAPIEndpoints()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -75,7 +85,7 @@ func (ac *OSSAccessControlService) Evaluate(ctx context.Context, user *models.Si
|
||||
}
|
||||
|
||||
if _, ok := user.Permissions[user.OrgId]; !ok {
|
||||
permissions, err := ac.GetUserPermissions(ctx, user)
|
||||
permissions, err := ac.GetUserPermissions(ctx, user, accesscontrol.Options{ReloadCache: true})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -96,7 +106,7 @@ func (ac *OSSAccessControlService) GetUserRoles(ctx context.Context, user *model
|
||||
}
|
||||
|
||||
// GetUserPermissions returns user permissions based on built-in roles
|
||||
func (ac *OSSAccessControlService) GetUserPermissions(ctx context.Context, user *models.SignedInUser) ([]*accesscontrol.Permission, error) {
|
||||
func (ac *OSSAccessControlService) GetUserPermissions(ctx context.Context, user *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) {
|
||||
timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary)
|
||||
defer timer.ObserveDuration()
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
@@ -152,6 +153,7 @@ func TestUsageMetrics(t *testing.T) {
|
||||
featuremgmt.WithFeatures("accesscontrol", tt.enabled),
|
||||
&usagestats.UsageStatsMock{T: t},
|
||||
database.ProvideService(sqlstore.InitTestDB(t)),
|
||||
routing.NewRouteRegister(),
|
||||
)
|
||||
report, err := s.usageStats.GetUsageReport(context.Background())
|
||||
assert.Nil(t, err)
|
||||
@@ -543,7 +545,7 @@ func TestOSSAccessControlService_GetUserPermissions(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test
|
||||
userPerms, err := ac.GetUserPermissions(context.Background(), &tt.user)
|
||||
userPerms, err := ac.GetUserPermissions(context.Background(), &tt.user, accesscontrol.Options{})
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err, "Expected an error with GetUserPermissions.")
|
||||
return
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestServiceAccountsAPI_DeleteServiceAccount(t *testing.T) {
|
||||
user: tests.TestUser{Login: "servicetest1@admin", IsServiceAccount: true},
|
||||
acmock: tests.SetupMockAccesscontrol(
|
||||
t,
|
||||
func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) {
|
||||
func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) {
|
||||
return []*accesscontrol.Permission{{Action: serviceaccounts.ActionDelete, Scope: serviceaccounts.ScopeAll}}, nil
|
||||
},
|
||||
false,
|
||||
@@ -74,7 +74,7 @@ func TestServiceAccountsAPI_DeleteServiceAccount(t *testing.T) {
|
||||
user: tests.TestUser{Login: "servicetest2@admin", IsServiceAccount: true},
|
||||
acmock: tests.SetupMockAccesscontrol(
|
||||
t,
|
||||
func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) {
|
||||
func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) {
|
||||
return []*accesscontrol.Permission{}, nil
|
||||
},
|
||||
false,
|
||||
@@ -134,7 +134,7 @@ func TestServiceAccountsAPI_RetrieveServiceAccount(t *testing.T) {
|
||||
user: &tests.TestUser{Login: "servicetest1@admin", IsServiceAccount: true},
|
||||
acmock: tests.SetupMockAccesscontrol(
|
||||
t,
|
||||
func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) {
|
||||
func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) {
|
||||
return []*accesscontrol.Permission{{Action: serviceaccounts.ActionRead, Scope: serviceaccounts.ScopeAll}}, nil
|
||||
},
|
||||
false,
|
||||
@@ -146,7 +146,7 @@ func TestServiceAccountsAPI_RetrieveServiceAccount(t *testing.T) {
|
||||
user: &tests.TestUser{Login: "servicetest2@admin", IsServiceAccount: true},
|
||||
acmock: tests.SetupMockAccesscontrol(
|
||||
t,
|
||||
func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) {
|
||||
func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) {
|
||||
return []*accesscontrol.Permission{}, nil
|
||||
},
|
||||
false,
|
||||
@@ -159,7 +159,7 @@ func TestServiceAccountsAPI_RetrieveServiceAccount(t *testing.T) {
|
||||
userID: 12,
|
||||
acmock: tests.SetupMockAccesscontrol(
|
||||
t,
|
||||
func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error) {
|
||||
func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) {
|
||||
return []*accesscontrol.Permission{{Action: serviceaccounts.ActionRead, Scope: serviceaccounts.ScopeAll}}, nil
|
||||
},
|
||||
false,
|
||||
|
||||
@@ -41,7 +41,9 @@ func (s *ServiceAccountMock) Migrated(ctx context.Context, orgID int64) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func SetupMockAccesscontrol(t *testing.T, userpermissionsfunc func(c context.Context, siu *models.SignedInUser) ([]*accesscontrol.Permission, error), disableAccessControl bool) *accesscontrolmock.Mock {
|
||||
func SetupMockAccesscontrol(t *testing.T,
|
||||
userpermissionsfunc func(c context.Context, siu *models.SignedInUser, opt accesscontrol.Options) ([]*accesscontrol.Permission, error),
|
||||
disableAccessControl bool) *accesscontrolmock.Mock {
|
||||
t.Helper()
|
||||
acmock := accesscontrolmock.New()
|
||||
if disableAccessControl {
|
||||
|
||||
Reference in New Issue
Block a user