Access Control: move features to Enterprise (#32640)

* Move db package WIP

* Implement OSS access control

* Register OSS access control

* Fix linter error in tests

* Fix linter error in evaluator

* Simplify OSS tests

* Optimize builtin roles

* Chore: add comments to the exported functions

* Remove init from ossaccesscontrol package (moved to ext)

* Add access control as a dependency for http server

* Modify middleware to receive fallback function

* Middleware: refactor fallback function call

* Move unused models to enterprise

* Simplify AccessControl type

* Chore: use bool IsDisabled() method instead of CanBeDisabled interface
This commit is contained in:
Alexander Zobnin
2021-04-06 16:49:09 +03:00
committed by GitHub
parent 664268a498
commit 823f0bc460
21 changed files with 219 additions and 2256 deletions
@@ -0,0 +1,42 @@
package ossaccesscontrol
import (
"github.com/grafana/grafana/pkg/services/accesscontrol"
)
var builtInRolesMap = map[string]accesscontrol.RoleDTO{
"grafana:builtin:users:read:self": {
Name: "grafana:builtin:users:read:self",
Version: 1,
Permissions: []accesscontrol.Permission{
{
Permission: "users:read",
Scope: "users:self",
},
{
Permission: "users.tokens:list",
Scope: "users:self",
},
{
Permission: "users.teams:read",
Scope: "users:self",
},
},
},
}
var builtInRoleGrants = map[string][]string{
"Viewer": {
"grafana:builtin:users:read:self",
},
}
func getBuiltInRole(role string) *accesscontrol.RoleDTO {
var builtInRole accesscontrol.RoleDTO
if r, ok := builtInRolesMap[role]; ok {
// Do not modify builtInRoles
builtInRole = r
return &builtInRole
}
return nil
}
@@ -0,0 +1,59 @@
package ossaccesscontrol
import (
"context"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/evaluator"
"github.com/grafana/grafana/pkg/setting"
)
// OSSAccessControlService is the service implementing role based access control.
type OSSAccessControlService struct {
Cfg *setting.Cfg `inject:""`
Log log.Logger
}
// Init initializes the OSSAccessControlService.
func (ac *OSSAccessControlService) Init() error {
ac.Log = log.New("accesscontrol")
return nil
}
func (ac *OSSAccessControlService) IsDisabled() bool {
if ac.Cfg == nil {
return true
}
_, exists := ac.Cfg.FeatureToggles["accesscontrol"]
return !exists
}
// Evaluate evaluates access to the given resource
func (ac *OSSAccessControlService) Evaluate(ctx context.Context, user *models.SignedInUser, permission string, scope ...string) (bool, error) {
return evaluator.Evaluate(ctx, ac, user, permission, scope...)
}
// GetUserPermissions returns user permissions based on built-in roles
func (ac *OSSAccessControlService) GetUserPermissions(ctx context.Context, user *models.SignedInUser, roles []string) ([]*accesscontrol.Permission, error) {
permissions := make([]*accesscontrol.Permission, 0)
for _, legacyRole := range roles {
if builtInRoleNames, ok := builtInRoleGrants[legacyRole]; ok {
for _, builtInRoleName := range builtInRoleNames {
builtInRole := getBuiltInRole(builtInRoleName)
if builtInRole == nil {
continue
}
for _, p := range builtInRole.Permissions {
permission := p
permissions = append(permissions, &permission)
}
}
}
}
return permissions, nil
}
@@ -0,0 +1,98 @@
package ossaccesscontrol
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
)
func setupTestEnv(t testing.TB) *OSSAccessControlService {
t.Helper()
cfg := setting.NewCfg()
cfg.FeatureToggles = map[string]bool{"accesscontrol": true}
ac := OSSAccessControlService{
Cfg: cfg,
Log: log.New("accesscontrol-test"),
}
err := ac.Init()
require.NoError(t, err)
return &ac
}
type evaluatingPermissionsTestCase struct {
desc string
user userTestCase
endpoints []endpointTestCase
evalResult bool
}
type userTestCase struct {
name string
orgRole models.RoleType
isGrafanaAdmin bool
}
type endpointTestCase struct {
permission string
scope []string
}
func TestEvaluatingPermissions(t *testing.T) {
testCases := []evaluatingPermissionsTestCase{
{
desc: "should successfully evaluate access to the endpoint",
user: userTestCase{
name: "testuser",
orgRole: models.ROLE_EDITOR,
isGrafanaAdmin: false,
},
endpoints: []endpointTestCase{
{permission: "users.teams:read", scope: []string{"users:self"}},
{permission: "users:read", scope: []string{"users:self"}},
},
evalResult: true,
},
{
desc: "should restrict access to the unauthorized endpoints",
user: userTestCase{
name: "testuser",
orgRole: models.ROLE_VIEWER,
isGrafanaAdmin: false,
},
endpoints: []endpointTestCase{
{permission: "users:create", scope: []string{"users"}},
},
evalResult: false,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
ac := setupTestEnv(t)
t.Cleanup(registry.ClearOverrides)
user := &models.SignedInUser{
UserId: 1,
OrgId: 1,
Name: tc.user.name,
OrgRole: tc.user.orgRole,
IsGrafanaAdmin: tc.user.isGrafanaAdmin,
}
for _, endpoint := range tc.endpoints {
result, err := ac.Evaluate(context.Background(), user, endpoint.permission, endpoint.scope...)
require.NoError(t, err)
assert.Equal(t, tc.evalResult, result)
}
})
}
}