mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
RBAC: Allow omitting default permissions when a new resource is created (#90720)
* Cfg: Move rbac settings to own struct * Cfg: Add setting to control if resource should generate managed permissions when created * Dashboards: Check if we should generate default permissions when dashboard is created * Folders: Check if we should generate default permissions when folder is created * Datasource: Check if we should generate default permissions when datasource is created * ServiceAccount: Check if we should generate default permissions when service account is created * Cfg: Add option to specify resources for wich we should default seed * ManagedPermissions: Move providers to their own files * Dashboards: Default seed all possible managed permissions if configured * Folders: Default seed all possible managed permissions if configured * Cfg: Remove service account from list * RBAC: Move utility function * remove managed permission settings from the config file examples, change the setting names * remove ini file changes from the PR * fix setting reading * fix linting errors * fix tests * fix wildcard role seeding --------- Co-authored-by: Karl Persson <kalle.persson@grafana.com> Co-authored-by: jguer <me@jguer.space>
This commit is contained in:
parent
82236976ae
commit
9bb2cf4968
@ -188,6 +188,9 @@ func (hs *HTTPServer) CreateFolder(c *contextmodel.ReqContext) response.Response
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (hs *HTTPServer) setDefaultFolderPermissions(ctx context.Context, orgID int64, user identity.Requester, folder *folder.Folder) error {
|
func (hs *HTTPServer) setDefaultFolderPermissions(ctx context.Context, orgID int64, user identity.Requester, folder *folder.Folder) error {
|
||||||
|
if !hs.Cfg.RBAC.PermissionsOnCreation("folder") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var permissions []accesscontrol.SetResourcePermissionCommand
|
var permissions []accesscontrol.SetResourcePermissionCommand
|
||||||
var userID int64
|
var userID int64
|
||||||
|
|
||||||
|
@ -550,7 +550,7 @@ func (hs *HTTPServer) hasPluginRequestedPermissions(c *contextmodel.ReqContext,
|
|||||||
hs.log.Debug("check installer's permissions, plugin wants to register an external service")
|
hs.log.Debug("check installer's permissions, plugin wants to register an external service")
|
||||||
evaluator := evalAllPermissions(plugin.JSONData.IAM.Permissions)
|
evaluator := evalAllPermissions(plugin.JSONData.IAM.Permissions)
|
||||||
hasAccess := ac.HasGlobalAccess(hs.AccessControl, hs.authnService, c)
|
hasAccess := ac.HasGlobalAccess(hs.AccessControl, hs.authnService, c)
|
||||||
if hs.Cfg.RBACSingleOrganization {
|
if hs.Cfg.RBAC.SingleOrganization {
|
||||||
// In a single organization setup, no need for a global check
|
// In a single organization setup, no need for a global check
|
||||||
hasAccess = ac.HasAccess(hs.AccessControl, c)
|
hasAccess = ac.HasAccess(hs.AccessControl, c)
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ func Test_PluginsInstallAndUninstall(t *testing.T) {
|
|||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.Cfg.PluginAdminEnabled = tc.pluginAdminEnabled
|
hs.Cfg.PluginAdminEnabled = tc.pluginAdminEnabled
|
||||||
hs.Cfg.PluginAdminExternalManageEnabled = tc.pluginAdminExternalManageEnabled
|
hs.Cfg.PluginAdminExternalManageEnabled = tc.pluginAdminExternalManageEnabled
|
||||||
hs.Cfg.RBACSingleOrganization = tc.singleOrganization
|
hs.Cfg.RBAC.SingleOrganization = tc.singleOrganization
|
||||||
|
|
||||||
hs.orgService = &orgtest.FakeOrgService{ExpectedOrg: &org.Org{}}
|
hs.orgService = &orgtest.FakeOrgService{ExpectedOrg: &org.Org{}}
|
||||||
hs.accesscontrolService = &actest.FakeService{}
|
hs.accesscontrolService = &actest.FakeService{}
|
||||||
@ -743,7 +743,7 @@ func TestHTTPServer_hasPluginRequestedPermissions(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.Cfg.RBACSingleOrganization = tt.singleOrg
|
hs.Cfg.RBAC.SingleOrganization = tt.singleOrg
|
||||||
hs.pluginStore = &pluginstore.FakePluginStore{
|
hs.pluginStore = &pluginstore.FakePluginStore{
|
||||||
PluginList: []pluginstore.Plugin{tt.plugin},
|
PluginList: []pluginstore.Plugin{tt.plugin},
|
||||||
}
|
}
|
||||||
|
@ -362,6 +362,20 @@ func GetOrgRoles(user identity.Requester) []string {
|
|||||||
return roles
|
return roles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PermissionsForActions generate Permissions for all actions provided scoped to provided scope.
|
||||||
|
func PermissionsForActions(actions []string, scope string) []Permission {
|
||||||
|
permissions := make([]Permission, len(actions))
|
||||||
|
|
||||||
|
for i, action := range actions {
|
||||||
|
permissions[i] = Permission{
|
||||||
|
Action: action,
|
||||||
|
Scope: scope,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return permissions
|
||||||
|
}
|
||||||
|
|
||||||
func BackgroundUser(name string, orgID int64, role org.RoleType, permissions []Permission) identity.Requester {
|
func BackgroundUser(name string, orgID int64, role org.RoleType, permissions []Permission) identity.Requester {
|
||||||
return &user.SignedInUser{
|
return &user.SignedInUser{
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
|
@ -119,7 +119,7 @@ func (s *Service) GetUserPermissions(ctx context.Context, user identity.Requeste
|
|||||||
timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary)
|
timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary)
|
||||||
defer timer.ObserveDuration()
|
defer timer.ObserveDuration()
|
||||||
|
|
||||||
if !s.cfg.RBACPermissionCache || !user.HasUniqueId() {
|
if !s.cfg.RBAC.PermissionCache || !user.HasUniqueId() {
|
||||||
return s.getUserPermissions(ctx, user, options)
|
return s.getUserPermissions(ctx, user, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ func UseGlobalOrg(c *contextmodel.ReqContext) (int64, error) {
|
|||||||
// UseGlobalOrSingleOrg returns the global organization or the current organization in a single organization setup
|
// UseGlobalOrSingleOrg returns the global organization or the current organization in a single organization setup
|
||||||
func UseGlobalOrSingleOrg(cfg *setting.Cfg) OrgIDGetter {
|
func UseGlobalOrSingleOrg(cfg *setting.Cfg) OrgIDGetter {
|
||||||
return func(c *contextmodel.ReqContext) (int64, error) {
|
return func(c *contextmodel.ReqContext) (int64, error) {
|
||||||
if cfg.RBACSingleOrganization {
|
if cfg.RBAC.SingleOrganization {
|
||||||
return c.GetOrgID(), nil
|
return c.GetOrgID(), nil
|
||||||
}
|
}
|
||||||
return GlobalOrgID, nil
|
return GlobalOrgID, nil
|
||||||
@ -271,7 +271,7 @@ func UseGlobalOrgFromRequestData(cfg *setting.Cfg) OrgIDGetter {
|
|||||||
|
|
||||||
// We only check permissions in the global organization if we are not running a SingleOrganization setup
|
// We only check permissions in the global organization if we are not running a SingleOrganization setup
|
||||||
// That allows Organization Admins to modify global roles and make global assignments.
|
// That allows Organization Admins to modify global roles and make global assignments.
|
||||||
if query.Global && !cfg.RBACSingleOrganization {
|
if query.Global && !cfg.RBAC.SingleOrganization {
|
||||||
return GlobalOrgID, nil
|
return GlobalOrgID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +284,7 @@ func UseGlobalOrgFromRequestParams(cfg *setting.Cfg) OrgIDGetter {
|
|||||||
return func(c *contextmodel.ReqContext) (int64, error) {
|
return func(c *contextmodel.ReqContext) (int64, error) {
|
||||||
// We only check permissions in the global organization if we are not running a SingleOrganization setup
|
// We only check permissions in the global organization if we are not running a SingleOrganization setup
|
||||||
// That allows Organization Admins to modify global roles and make global assignments, and is intended for use in hosted Grafana.
|
// That allows Organization Admins to modify global roles and make global assignments, and is intended for use in hosted Grafana.
|
||||||
if c.QueryBool("global") && !cfg.RBACSingleOrganization {
|
if c.QueryBool("global") && !cfg.RBAC.SingleOrganization {
|
||||||
return GlobalOrgID, nil
|
return GlobalOrgID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
177
pkg/services/accesscontrol/ossaccesscontrol/dashboard.go
Normal file
177
pkg/services/accesscontrol/ossaccesscontrol/dashboard.go
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
package ossaccesscontrol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
|
"github.com/grafana/grafana/pkg/services/licensing"
|
||||||
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DashboardPermissionsService struct {
|
||||||
|
*resourcepermissions.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
var DashboardViewActions = []string{dashboards.ActionDashboardsRead}
|
||||||
|
var DashboardEditActions = append(DashboardViewActions, []string{dashboards.ActionDashboardsWrite, dashboards.ActionDashboardsDelete}...)
|
||||||
|
var DashboardAdminActions = append(DashboardEditActions, []string{dashboards.ActionDashboardsPermissionsRead, dashboards.ActionDashboardsPermissionsWrite}...)
|
||||||
|
|
||||||
|
func getDashboardViewActions(features featuremgmt.FeatureToggles) []string {
|
||||||
|
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
|
||||||
|
return append(DashboardViewActions, accesscontrol.ActionAnnotationsRead)
|
||||||
|
}
|
||||||
|
return DashboardViewActions
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDashboardEditActions(features featuremgmt.FeatureToggles) []string {
|
||||||
|
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
|
||||||
|
return append(DashboardEditActions, []string{accesscontrol.ActionAnnotationsRead, accesscontrol.ActionAnnotationsWrite, accesscontrol.ActionAnnotationsDelete, accesscontrol.ActionAnnotationsCreate}...)
|
||||||
|
}
|
||||||
|
return DashboardEditActions
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDashboardAdminActions(features featuremgmt.FeatureToggles) []string {
|
||||||
|
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
|
||||||
|
return append(DashboardAdminActions, []string{accesscontrol.ActionAnnotationsRead, accesscontrol.ActionAnnotationsWrite, accesscontrol.ActionAnnotationsDelete, accesscontrol.ActionAnnotationsCreate}...)
|
||||||
|
}
|
||||||
|
return DashboardAdminActions
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerDashboardRoles(cfg *setting.Cfg, features featuremgmt.FeatureToggles, service accesscontrol.Service) error {
|
||||||
|
if !cfg.RBAC.PermissionsWildcardSeed("dashboard") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer := accesscontrol.RoleRegistration{
|
||||||
|
Role: accesscontrol.RoleDTO{
|
||||||
|
Name: "fixed:dashboards:viewer",
|
||||||
|
DisplayName: "Viewer",
|
||||||
|
Description: "View all dashboards",
|
||||||
|
Group: "Dashboards",
|
||||||
|
Permissions: accesscontrol.PermissionsForActions(getDashboardViewActions(features), dashboards.ScopeDashboardsAll),
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
|
Grants: []string{"Viewer"},
|
||||||
|
}
|
||||||
|
|
||||||
|
editor := accesscontrol.RoleRegistration{
|
||||||
|
Role: accesscontrol.RoleDTO{
|
||||||
|
Name: "fixed:dashboards:editor",
|
||||||
|
DisplayName: "Editor",
|
||||||
|
Description: "Edit all dashboards.",
|
||||||
|
Group: "Dashboards",
|
||||||
|
Permissions: accesscontrol.PermissionsForActions(getDashboardEditActions(features), dashboards.ScopeDashboardsAll),
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
|
Grants: []string{"Editor"},
|
||||||
|
}
|
||||||
|
|
||||||
|
admin := accesscontrol.RoleRegistration{
|
||||||
|
Role: accesscontrol.RoleDTO{
|
||||||
|
Name: "fixed:dashboards:admin",
|
||||||
|
DisplayName: "Admin",
|
||||||
|
Description: "Administer all dashboards.",
|
||||||
|
Group: "Dashboards",
|
||||||
|
Permissions: accesscontrol.PermissionsForActions(getDashboardAdminActions(features), dashboards.ScopeDashboardsAll),
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
|
Grants: []string{"Admin"},
|
||||||
|
}
|
||||||
|
|
||||||
|
return service.DeclareFixedRoles(viewer, editor, admin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideDashboardPermissions(
|
||||||
|
cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl,
|
||||||
|
license licensing.Licensing, dashboardStore dashboards.Store, folderService folder.Service, service accesscontrol.Service,
|
||||||
|
teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService,
|
||||||
|
) (*DashboardPermissionsService, error) {
|
||||||
|
getDashboard := func(ctx context.Context, orgID int64, resourceID string) (*dashboards.Dashboard, error) {
|
||||||
|
query := &dashboards.GetDashboardQuery{UID: resourceID, OrgID: orgID}
|
||||||
|
queryResult, err := dashboardStore.GetDashboard(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return queryResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := registerDashboardRoles(cfg, features, service); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
options := resourcepermissions.Options{
|
||||||
|
Resource: "dashboards",
|
||||||
|
ResourceAttribute: "uid",
|
||||||
|
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
|
||||||
|
dashboard, err := getDashboard(ctx, orgID, resourceID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if dashboard.IsFolder {
|
||||||
|
return errors.New("not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
InheritedScopesSolver: func(ctx context.Context, orgID int64, resourceID string) ([]string, error) {
|
||||||
|
wildcards := accesscontrol.WildcardsFromPrefix(dashboards.ScopeFoldersPrefix)
|
||||||
|
scopes := []string(wildcards)
|
||||||
|
|
||||||
|
dashboard, err := getDashboard(ctx, orgID, resourceID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.AccessControl).Inc()
|
||||||
|
// nolint:staticcheck
|
||||||
|
if dashboard.FolderUID != "" {
|
||||||
|
query := &dashboards.GetDashboardQuery{UID: dashboard.FolderUID, OrgID: orgID}
|
||||||
|
queryResult, err := dashboardStore.GetDashboard(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parentScope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(queryResult.UID)
|
||||||
|
|
||||||
|
nestedScopes, err := dashboards.GetInheritedScopes(ctx, orgID, queryResult.UID, folderService)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scopes = append(scopes, parentScope)
|
||||||
|
scopes = append(scopes, nestedScopes...)
|
||||||
|
return scopes, nil
|
||||||
|
}
|
||||||
|
return append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder.GeneralFolderUID)), nil
|
||||||
|
},
|
||||||
|
Assignments: resourcepermissions.Assignments{
|
||||||
|
Users: true,
|
||||||
|
Teams: true,
|
||||||
|
BuiltInRoles: true,
|
||||||
|
ServiceAccounts: true,
|
||||||
|
},
|
||||||
|
PermissionsToActions: map[string][]string{
|
||||||
|
"View": getDashboardViewActions(features),
|
||||||
|
"Edit": getDashboardEditActions(features),
|
||||||
|
"Admin": getDashboardAdminActions(features),
|
||||||
|
},
|
||||||
|
ReaderRoleName: "Dashboard permission reader",
|
||||||
|
WriterRoleName: "Dashboard permission writer",
|
||||||
|
RoleGroup: "Dashboards",
|
||||||
|
}
|
||||||
|
|
||||||
|
srv, err := resourcepermissions.New(cfg, options, features, router, license, ac, service, sql, teamService, userService, actionSetService)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &DashboardPermissionsService{srv}, nil
|
||||||
|
}
|
87
pkg/services/accesscontrol/ossaccesscontrol/datasource.go
Normal file
87
pkg/services/accesscontrol/ossaccesscontrol/datasource.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package ossaccesscontrol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
||||||
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DatasourceQueryActions contains permissions to read information
|
||||||
|
// about a data source and submit arbitrary queries to it.
|
||||||
|
var DatasourceQueryActions = []string{
|
||||||
|
datasources.ActionRead,
|
||||||
|
datasources.ActionQuery,
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideDatasourcePermissionsService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, db db.DB) *DatasourcePermissionsService {
|
||||||
|
return &DatasourcePermissionsService{
|
||||||
|
store: resourcepermissions.NewStore(cfg, db, features),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ accesscontrol.DatasourcePermissionsService = new(DatasourcePermissionsService)
|
||||||
|
|
||||||
|
type DatasourcePermissionsService struct {
|
||||||
|
store resourcepermissions.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DatasourcePermissionsService) GetPermissions(ctx context.Context, user identity.Requester, resourceID string) ([]accesscontrol.ResourcePermission, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DatasourcePermissionsService) SetUserPermission(ctx context.Context, orgID int64, user accesscontrol.User, resourceID, permission string) (*accesscontrol.ResourcePermission, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DatasourcePermissionsService) SetTeamPermission(ctx context.Context, orgID, teamID int64, resourceID, permission string) (*accesscontrol.ResourcePermission, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DatasourcePermissionsService) SetBuiltInRolePermission(ctx context.Context, orgID int64, builtInRole string, resourceID string, permission string) (*accesscontrol.ResourcePermission, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPermissions sets managed permissions for a datasource in OSS. This ensures that Viewers and Editors maintain query access to a data source
|
||||||
|
// 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) {
|
||||||
|
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
|
||||||
|
if cmd.Permission != "Query" || cmd.BuiltinRole == "" || resourceID == "*" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
actions := DatasourceQueryActions
|
||||||
|
|
||||||
|
dbCommands = append(dbCommands, resourcepermissions.SetResourcePermissionsCommand{
|
||||||
|
BuiltinRole: cmd.BuiltinRole,
|
||||||
|
SetResourcePermissionCommand: resourcepermissions.SetResourcePermissionCommand{
|
||||||
|
Actions: actions,
|
||||||
|
Resource: datasources.ScopeRoot,
|
||||||
|
ResourceID: resourceID,
|
||||||
|
ResourceAttribute: "uid",
|
||||||
|
Permission: cmd.Permission,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.store.SetResourcePermissions(ctx, orgID, dbCommands, resourcepermissions.ResourceHooks{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DatasourcePermissionsService) DeleteResourcePermissions(ctx context.Context, orgID int64, resourceID string) error {
|
||||||
|
return e.store.DeleteResourcePermissions(ctx, orgID, &resourcepermissions.DeleteResourcePermissionsCmd{
|
||||||
|
Resource: datasources.ScopeRoot,
|
||||||
|
ResourceAttribute: "uid",
|
||||||
|
ResourceID: resourceID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DatasourcePermissionsService) MapActions(permission accesscontrol.ResourcePermission) string {
|
||||||
|
return ""
|
||||||
|
}
|
133
pkg/services/accesscontrol/ossaccesscontrol/folder.go
Normal file
133
pkg/services/accesscontrol/ossaccesscontrol/folder.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package ossaccesscontrol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
|
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||||
|
"github.com/grafana/grafana/pkg/services/licensing"
|
||||||
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FolderPermissionsService struct {
|
||||||
|
*resourcepermissions.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
var FolderViewActions = []string{dashboards.ActionFoldersRead, accesscontrol.ActionAlertingRuleRead, libraryelements.ActionLibraryPanelsRead, accesscontrol.ActionAlertingSilencesRead}
|
||||||
|
var FolderEditActions = append(FolderViewActions, []string{
|
||||||
|
dashboards.ActionFoldersWrite,
|
||||||
|
dashboards.ActionFoldersDelete,
|
||||||
|
dashboards.ActionDashboardsCreate,
|
||||||
|
accesscontrol.ActionAlertingRuleCreate,
|
||||||
|
accesscontrol.ActionAlertingRuleUpdate,
|
||||||
|
accesscontrol.ActionAlertingRuleDelete,
|
||||||
|
accesscontrol.ActionAlertingSilencesCreate,
|
||||||
|
accesscontrol.ActionAlertingSilencesWrite,
|
||||||
|
libraryelements.ActionLibraryPanelsCreate,
|
||||||
|
libraryelements.ActionLibraryPanelsWrite,
|
||||||
|
libraryelements.ActionLibraryPanelsDelete,
|
||||||
|
}...)
|
||||||
|
var FolderAdminActions = append(FolderEditActions, []string{dashboards.ActionFoldersPermissionsRead, dashboards.ActionFoldersPermissionsWrite}...)
|
||||||
|
|
||||||
|
func registerFolderRoles(cfg *setting.Cfg, features featuremgmt.FeatureToggles, service accesscontrol.Service) error {
|
||||||
|
if !cfg.RBAC.PermissionsWildcardSeed("folder") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer := accesscontrol.RoleRegistration{
|
||||||
|
Role: accesscontrol.RoleDTO{
|
||||||
|
Name: "fixed:folders:viewer",
|
||||||
|
DisplayName: "Viewer",
|
||||||
|
Description: "View all folders and dashboards.",
|
||||||
|
Group: "Folders",
|
||||||
|
Permissions: accesscontrol.PermissionsForActions(append(getDashboardViewActions(features), FolderViewActions...), dashboards.ScopeFoldersAll),
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
|
Grants: []string{"Viewer"},
|
||||||
|
}
|
||||||
|
|
||||||
|
editor := accesscontrol.RoleRegistration{
|
||||||
|
Role: accesscontrol.RoleDTO{
|
||||||
|
Name: "fixed:folders:editor",
|
||||||
|
DisplayName: "Editor",
|
||||||
|
Description: "Edit all folders and dashboards.",
|
||||||
|
Group: "Folders",
|
||||||
|
Permissions: accesscontrol.PermissionsForActions(append(getDashboardEditActions(features), FolderEditActions...), dashboards.ScopeFoldersAll),
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
|
Grants: []string{"Editor"},
|
||||||
|
}
|
||||||
|
|
||||||
|
admin := accesscontrol.RoleRegistration{
|
||||||
|
Role: accesscontrol.RoleDTO{
|
||||||
|
Name: "fixed:folders:admin",
|
||||||
|
DisplayName: "Admin",
|
||||||
|
Description: "Administer all folders and dashboards",
|
||||||
|
Group: "folders",
|
||||||
|
Permissions: accesscontrol.PermissionsForActions(append(getDashboardAdminActions(features), FolderAdminActions...), dashboards.ScopeFoldersAll),
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
|
Grants: []string{"Admin"},
|
||||||
|
}
|
||||||
|
|
||||||
|
return service.DeclareFixedRoles(viewer, editor, admin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideFolderPermissions(
|
||||||
|
cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, accesscontrol accesscontrol.AccessControl,
|
||||||
|
license licensing.Licensing, dashboardStore dashboards.Store, folderService folder.Service, service accesscontrol.Service,
|
||||||
|
teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService,
|
||||||
|
) (*FolderPermissionsService, error) {
|
||||||
|
if err := registerFolderRoles(cfg, features, service); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
options := resourcepermissions.Options{
|
||||||
|
Resource: "folders",
|
||||||
|
ResourceAttribute: "uid",
|
||||||
|
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
|
||||||
|
query := &dashboards.GetDashboardQuery{UID: resourceID, OrgID: orgID}
|
||||||
|
queryResult, err := dashboardStore.GetDashboard(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !queryResult.IsFolder {
|
||||||
|
return errors.New("not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
InheritedScopesSolver: func(ctx context.Context, orgID int64, resourceID string) ([]string, error) {
|
||||||
|
return dashboards.GetInheritedScopes(ctx, orgID, resourceID, folderService)
|
||||||
|
},
|
||||||
|
Assignments: resourcepermissions.Assignments{
|
||||||
|
Users: true,
|
||||||
|
Teams: true,
|
||||||
|
BuiltInRoles: true,
|
||||||
|
ServiceAccounts: true,
|
||||||
|
},
|
||||||
|
PermissionsToActions: map[string][]string{
|
||||||
|
"View": append(getDashboardViewActions(features), FolderViewActions...),
|
||||||
|
"Edit": append(getDashboardEditActions(features), FolderEditActions...),
|
||||||
|
"Admin": append(getDashboardAdminActions(features), FolderAdminActions...),
|
||||||
|
},
|
||||||
|
ReaderRoleName: "Folder permission reader",
|
||||||
|
WriterRoleName: "Folder permission writer",
|
||||||
|
RoleGroup: "Folders",
|
||||||
|
}
|
||||||
|
srv, err := resourcepermissions.New(cfg, options, features, router, license, accesscontrol, service, sql, teamService, userService, actionSetService)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &FolderPermissionsService{srv}, nil
|
||||||
|
}
|
@ -1,417 +0,0 @@
|
|||||||
package ossaccesscontrol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
|
||||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
|
||||||
"github.com/grafana/grafana/pkg/services/licensing"
|
|
||||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
|
||||||
"github.com/grafana/grafana/pkg/services/serviceaccounts/retriever"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team/teamimpl"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TeamPermissionsService struct {
|
|
||||||
*resourcepermissions.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
TeamMemberActions = []string{
|
|
||||||
accesscontrol.ActionTeamsRead,
|
|
||||||
}
|
|
||||||
|
|
||||||
TeamAdminActions = []string{
|
|
||||||
accesscontrol.ActionTeamsRead,
|
|
||||||
accesscontrol.ActionTeamsDelete,
|
|
||||||
accesscontrol.ActionTeamsWrite,
|
|
||||||
accesscontrol.ActionTeamsPermissionsRead,
|
|
||||||
accesscontrol.ActionTeamsPermissionsWrite,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func ProvideTeamPermissions(
|
|
||||||
cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB,
|
|
||||||
ac accesscontrol.AccessControl, license licensing.Licensing, service accesscontrol.Service,
|
|
||||||
teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService,
|
|
||||||
) (*TeamPermissionsService, error) {
|
|
||||||
options := resourcepermissions.Options{
|
|
||||||
Resource: "teams",
|
|
||||||
ResourceAttribute: "id",
|
|
||||||
OnlyManaged: true,
|
|
||||||
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
|
|
||||||
id, err := strconv.ParseInt(resourceID, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = teamService.GetTeamByID(context.Background(), &team.GetTeamByIDQuery{
|
|
||||||
OrgID: orgID,
|
|
||||||
ID: id,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Assignments: resourcepermissions.Assignments{
|
|
||||||
Users: true,
|
|
||||||
Teams: false,
|
|
||||||
BuiltInRoles: false,
|
|
||||||
},
|
|
||||||
PermissionsToActions: map[string][]string{
|
|
||||||
"Member": TeamMemberActions,
|
|
||||||
"Admin": TeamAdminActions,
|
|
||||||
},
|
|
||||||
ReaderRoleName: "Team permission reader",
|
|
||||||
WriterRoleName: "Team permission writer",
|
|
||||||
RoleGroup: "Teams",
|
|
||||||
OnSetUser: func(session *db.Session, orgID int64, user accesscontrol.User, resourceID, permission string) error {
|
|
||||||
teamId, err := strconv.ParseInt(resourceID, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch permission {
|
|
||||||
case "Member":
|
|
||||||
return teamimpl.AddOrUpdateTeamMemberHook(session, user.ID, orgID, teamId, user.IsExternal, 0)
|
|
||||||
case "Admin":
|
|
||||||
return teamimpl.AddOrUpdateTeamMemberHook(session, user.ID, orgID, teamId, user.IsExternal, dashboardaccess.PERMISSION_ADMIN)
|
|
||||||
case "":
|
|
||||||
return teamimpl.RemoveTeamMemberHook(session, &team.RemoveTeamMemberCommand{
|
|
||||||
OrgID: orgID,
|
|
||||||
UserID: user.ID,
|
|
||||||
TeamID: teamId,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("invalid team permission type %s", permission)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
srv, err := resourcepermissions.New(cfg, options, features, router, license, ac, service, sql, teamService, userService, actionSetService)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &TeamPermissionsService{srv}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type DashboardPermissionsService struct {
|
|
||||||
*resourcepermissions.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
var DashboardViewActions = []string{dashboards.ActionDashboardsRead}
|
|
||||||
var DashboardEditActions = append(DashboardViewActions, []string{dashboards.ActionDashboardsWrite, dashboards.ActionDashboardsDelete}...)
|
|
||||||
var DashboardAdminActions = append(DashboardEditActions, []string{dashboards.ActionDashboardsPermissionsRead, dashboards.ActionDashboardsPermissionsWrite}...)
|
|
||||||
|
|
||||||
func getDashboardViewActions(features featuremgmt.FeatureToggles) []string {
|
|
||||||
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
|
|
||||||
return append(DashboardViewActions, accesscontrol.ActionAnnotationsRead)
|
|
||||||
}
|
|
||||||
return DashboardViewActions
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDashboardEditActions(features featuremgmt.FeatureToggles) []string {
|
|
||||||
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
|
|
||||||
return append(DashboardEditActions, []string{accesscontrol.ActionAnnotationsRead, accesscontrol.ActionAnnotationsWrite, accesscontrol.ActionAnnotationsDelete, accesscontrol.ActionAnnotationsCreate}...)
|
|
||||||
}
|
|
||||||
return DashboardEditActions
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDashboardAdminActions(features featuremgmt.FeatureToggles) []string {
|
|
||||||
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
|
|
||||||
return append(DashboardAdminActions, []string{accesscontrol.ActionAnnotationsRead, accesscontrol.ActionAnnotationsWrite, accesscontrol.ActionAnnotationsDelete, accesscontrol.ActionAnnotationsCreate}...)
|
|
||||||
}
|
|
||||||
return DashboardAdminActions
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProvideDashboardPermissions(
|
|
||||||
cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl,
|
|
||||||
license licensing.Licensing, dashboardStore dashboards.Store, folderService folder.Service, service accesscontrol.Service,
|
|
||||||
teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService,
|
|
||||||
) (*DashboardPermissionsService, error) {
|
|
||||||
getDashboard := func(ctx context.Context, orgID int64, resourceID string) (*dashboards.Dashboard, error) {
|
|
||||||
query := &dashboards.GetDashboardQuery{UID: resourceID, OrgID: orgID}
|
|
||||||
queryResult, err := dashboardStore.GetDashboard(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return queryResult, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
options := resourcepermissions.Options{
|
|
||||||
Resource: "dashboards",
|
|
||||||
ResourceAttribute: "uid",
|
|
||||||
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
|
|
||||||
dashboard, err := getDashboard(ctx, orgID, resourceID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if dashboard.IsFolder {
|
|
||||||
return errors.New("not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
InheritedScopesSolver: func(ctx context.Context, orgID int64, resourceID string) ([]string, error) {
|
|
||||||
wildcards := accesscontrol.WildcardsFromPrefix(dashboards.ScopeFoldersPrefix)
|
|
||||||
scopes := []string(wildcards)
|
|
||||||
|
|
||||||
dashboard, err := getDashboard(ctx, orgID, resourceID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.AccessControl).Inc()
|
|
||||||
// nolint:staticcheck
|
|
||||||
if dashboard.FolderUID != "" {
|
|
||||||
query := &dashboards.GetDashboardQuery{UID: dashboard.FolderUID, OrgID: orgID}
|
|
||||||
queryResult, err := dashboardStore.GetDashboard(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
parentScope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(queryResult.UID)
|
|
||||||
|
|
||||||
nestedScopes, err := dashboards.GetInheritedScopes(ctx, orgID, queryResult.UID, folderService)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
scopes = append(scopes, parentScope)
|
|
||||||
scopes = append(scopes, nestedScopes...)
|
|
||||||
return scopes, nil
|
|
||||||
}
|
|
||||||
return append(scopes, dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder.GeneralFolderUID)), nil
|
|
||||||
},
|
|
||||||
Assignments: resourcepermissions.Assignments{
|
|
||||||
Users: true,
|
|
||||||
Teams: true,
|
|
||||||
BuiltInRoles: true,
|
|
||||||
ServiceAccounts: true,
|
|
||||||
},
|
|
||||||
PermissionsToActions: map[string][]string{
|
|
||||||
"View": getDashboardViewActions(features),
|
|
||||||
"Edit": getDashboardEditActions(features),
|
|
||||||
"Admin": getDashboardAdminActions(features),
|
|
||||||
},
|
|
||||||
ReaderRoleName: "Dashboard permission reader",
|
|
||||||
WriterRoleName: "Dashboard permission writer",
|
|
||||||
RoleGroup: "Dashboards",
|
|
||||||
}
|
|
||||||
|
|
||||||
srv, err := resourcepermissions.New(cfg, options, features, router, license, ac, service, sql, teamService, userService, actionSetService)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &DashboardPermissionsService{srv}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type FolderPermissionsService struct {
|
|
||||||
*resourcepermissions.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
var FolderViewActions = []string{dashboards.ActionFoldersRead, accesscontrol.ActionAlertingRuleRead, libraryelements.ActionLibraryPanelsRead, accesscontrol.ActionAlertingSilencesRead}
|
|
||||||
var FolderEditActions = append(FolderViewActions, []string{
|
|
||||||
dashboards.ActionFoldersWrite,
|
|
||||||
dashboards.ActionFoldersDelete,
|
|
||||||
dashboards.ActionDashboardsCreate,
|
|
||||||
accesscontrol.ActionAlertingRuleCreate,
|
|
||||||
accesscontrol.ActionAlertingRuleUpdate,
|
|
||||||
accesscontrol.ActionAlertingRuleDelete,
|
|
||||||
accesscontrol.ActionAlertingSilencesCreate,
|
|
||||||
accesscontrol.ActionAlertingSilencesWrite,
|
|
||||||
libraryelements.ActionLibraryPanelsCreate,
|
|
||||||
libraryelements.ActionLibraryPanelsWrite,
|
|
||||||
libraryelements.ActionLibraryPanelsDelete,
|
|
||||||
}...)
|
|
||||||
var FolderAdminActions = append(FolderEditActions, []string{dashboards.ActionFoldersPermissionsRead, dashboards.ActionFoldersPermissionsWrite}...)
|
|
||||||
|
|
||||||
func ProvideFolderPermissions(
|
|
||||||
cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, accesscontrol accesscontrol.AccessControl,
|
|
||||||
license licensing.Licensing, dashboardStore dashboards.Store, folderService folder.Service, service accesscontrol.Service,
|
|
||||||
teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService,
|
|
||||||
) (*FolderPermissionsService, error) {
|
|
||||||
options := resourcepermissions.Options{
|
|
||||||
Resource: "folders",
|
|
||||||
ResourceAttribute: "uid",
|
|
||||||
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
|
|
||||||
query := &dashboards.GetDashboardQuery{UID: resourceID, OrgID: orgID}
|
|
||||||
queryResult, err := dashboardStore.GetDashboard(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !queryResult.IsFolder {
|
|
||||||
return errors.New("not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
InheritedScopesSolver: func(ctx context.Context, orgID int64, resourceID string) ([]string, error) {
|
|
||||||
return dashboards.GetInheritedScopes(ctx, orgID, resourceID, folderService)
|
|
||||||
},
|
|
||||||
Assignments: resourcepermissions.Assignments{
|
|
||||||
Users: true,
|
|
||||||
Teams: true,
|
|
||||||
BuiltInRoles: true,
|
|
||||||
ServiceAccounts: true,
|
|
||||||
},
|
|
||||||
PermissionsToActions: map[string][]string{
|
|
||||||
"View": append(getDashboardViewActions(features), FolderViewActions...),
|
|
||||||
"Edit": append(getDashboardEditActions(features), FolderEditActions...),
|
|
||||||
"Admin": append(getDashboardAdminActions(features), FolderAdminActions...),
|
|
||||||
},
|
|
||||||
ReaderRoleName: "Folder permission reader",
|
|
||||||
WriterRoleName: "Folder permission writer",
|
|
||||||
RoleGroup: "Folders",
|
|
||||||
}
|
|
||||||
srv, err := resourcepermissions.New(cfg, options, features, router, license, accesscontrol, service, sql, teamService, userService, actionSetService)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &FolderPermissionsService{srv}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DatasourceQueryActions contains permissions to read information
|
|
||||||
// about a data source and submit arbitrary queries to it.
|
|
||||||
var DatasourceQueryActions = []string{
|
|
||||||
datasources.ActionRead,
|
|
||||||
datasources.ActionQuery,
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProvideDatasourcePermissionsService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, db db.DB) *DatasourcePermissionsService {
|
|
||||||
return &DatasourcePermissionsService{
|
|
||||||
store: resourcepermissions.NewStore(cfg, db, features),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ accesscontrol.DatasourcePermissionsService = new(DatasourcePermissionsService)
|
|
||||||
|
|
||||||
type DatasourcePermissionsService struct {
|
|
||||||
store resourcepermissions.Store
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DatasourcePermissionsService) GetPermissions(ctx context.Context, user identity.Requester, resourceID string) ([]accesscontrol.ResourcePermission, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DatasourcePermissionsService) SetUserPermission(ctx context.Context, orgID int64, user accesscontrol.User, resourceID, permission string) (*accesscontrol.ResourcePermission, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DatasourcePermissionsService) SetTeamPermission(ctx context.Context, orgID, teamID int64, resourceID, permission string) (*accesscontrol.ResourcePermission, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DatasourcePermissionsService) SetBuiltInRolePermission(ctx context.Context, orgID int64, builtInRole string, resourceID string, permission string) (*accesscontrol.ResourcePermission, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPermissions sets managed permissions for a datasource in OSS. This ensures that Viewers and Editors maintain query access to a data source
|
|
||||||
// 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) {
|
|
||||||
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
|
|
||||||
if cmd.Permission != "Query" || cmd.BuiltinRole == "" || resourceID == "*" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
actions := DatasourceQueryActions
|
|
||||||
|
|
||||||
dbCommands = append(dbCommands, resourcepermissions.SetResourcePermissionsCommand{
|
|
||||||
BuiltinRole: cmd.BuiltinRole,
|
|
||||||
SetResourcePermissionCommand: resourcepermissions.SetResourcePermissionCommand{
|
|
||||||
Actions: actions,
|
|
||||||
Resource: datasources.ScopeRoot,
|
|
||||||
ResourceID: resourceID,
|
|
||||||
ResourceAttribute: "uid",
|
|
||||||
Permission: cmd.Permission,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.store.SetResourcePermissions(ctx, orgID, dbCommands, resourcepermissions.ResourceHooks{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DatasourcePermissionsService) DeleteResourcePermissions(ctx context.Context, orgID int64, resourceID string) error {
|
|
||||||
return e.store.DeleteResourcePermissions(ctx, orgID, &resourcepermissions.DeleteResourcePermissionsCmd{
|
|
||||||
Resource: datasources.ScopeRoot,
|
|
||||||
ResourceAttribute: "uid",
|
|
||||||
ResourceID: resourceID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DatasourcePermissionsService) MapActions(permission accesscontrol.ResourcePermission) string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ServiceAccountEditActions = []string{
|
|
||||||
serviceaccounts.ActionRead,
|
|
||||||
serviceaccounts.ActionWrite,
|
|
||||||
}
|
|
||||||
ServiceAccountAdminActions = []string{
|
|
||||||
serviceaccounts.ActionRead,
|
|
||||||
serviceaccounts.ActionWrite,
|
|
||||||
serviceaccounts.ActionDelete,
|
|
||||||
serviceaccounts.ActionPermissionsRead,
|
|
||||||
serviceaccounts.ActionPermissionsWrite,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type ServiceAccountPermissionsService struct {
|
|
||||||
*resourcepermissions.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProvideServiceAccountPermissions(
|
|
||||||
cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl,
|
|
||||||
license licensing.Licensing, serviceAccountRetrieverService *retriever.Service, service accesscontrol.Service,
|
|
||||||
teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService,
|
|
||||||
) (*ServiceAccountPermissionsService, error) {
|
|
||||||
options := resourcepermissions.Options{
|
|
||||||
Resource: "serviceaccounts",
|
|
||||||
ResourceAttribute: "id",
|
|
||||||
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
|
|
||||||
id, err := strconv.ParseInt(resourceID, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = serviceAccountRetrieverService.RetrieveServiceAccount(ctx, orgID, id)
|
|
||||||
return err
|
|
||||||
},
|
|
||||||
Assignments: resourcepermissions.Assignments{
|
|
||||||
Users: true,
|
|
||||||
Teams: true,
|
|
||||||
BuiltInRoles: false,
|
|
||||||
},
|
|
||||||
PermissionsToActions: map[string][]string{
|
|
||||||
"Edit": ServiceAccountEditActions,
|
|
||||||
"Admin": ServiceAccountAdminActions,
|
|
||||||
},
|
|
||||||
ReaderRoleName: "Service account permission reader",
|
|
||||||
WriterRoleName: "Service account permission writer",
|
|
||||||
RoleGroup: "Service accounts",
|
|
||||||
}
|
|
||||||
|
|
||||||
srv, err := resourcepermissions.New(cfg, options, features, router, license, ac, service, sql, teamService, userService, actionSetService)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &ServiceAccountPermissionsService{srv}, nil
|
|
||||||
}
|
|
@ -0,0 +1,73 @@
|
|||||||
|
package ossaccesscontrol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
||||||
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
|
"github.com/grafana/grafana/pkg/services/licensing"
|
||||||
|
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||||
|
"github.com/grafana/grafana/pkg/services/serviceaccounts/retriever"
|
||||||
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ServiceAccountEditActions = []string{
|
||||||
|
serviceaccounts.ActionRead,
|
||||||
|
serviceaccounts.ActionWrite,
|
||||||
|
}
|
||||||
|
ServiceAccountAdminActions = []string{
|
||||||
|
serviceaccounts.ActionRead,
|
||||||
|
serviceaccounts.ActionWrite,
|
||||||
|
serviceaccounts.ActionDelete,
|
||||||
|
serviceaccounts.ActionPermissionsRead,
|
||||||
|
serviceaccounts.ActionPermissionsWrite,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServiceAccountPermissionsService struct {
|
||||||
|
*resourcepermissions.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideServiceAccountPermissions(
|
||||||
|
cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl,
|
||||||
|
license licensing.Licensing, serviceAccountRetrieverService *retriever.Service, service accesscontrol.Service,
|
||||||
|
teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService,
|
||||||
|
) (*ServiceAccountPermissionsService, error) {
|
||||||
|
options := resourcepermissions.Options{
|
||||||
|
Resource: "serviceaccounts",
|
||||||
|
ResourceAttribute: "id",
|
||||||
|
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
|
||||||
|
id, err := strconv.ParseInt(resourceID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = serviceAccountRetrieverService.RetrieveServiceAccount(ctx, orgID, id)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
Assignments: resourcepermissions.Assignments{
|
||||||
|
Users: true,
|
||||||
|
Teams: true,
|
||||||
|
BuiltInRoles: false,
|
||||||
|
},
|
||||||
|
PermissionsToActions: map[string][]string{
|
||||||
|
"Edit": ServiceAccountEditActions,
|
||||||
|
"Admin": ServiceAccountAdminActions,
|
||||||
|
},
|
||||||
|
ReaderRoleName: "Service account permission reader",
|
||||||
|
WriterRoleName: "Service account permission writer",
|
||||||
|
RoleGroup: "Service accounts",
|
||||||
|
}
|
||||||
|
|
||||||
|
srv, err := resourcepermissions.New(cfg, options, features, router, license, ac, service, sql, teamService, userService, actionSetService)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ServiceAccountPermissionsService{srv}, nil
|
||||||
|
}
|
103
pkg/services/accesscontrol/ossaccesscontrol/team.go
Normal file
103
pkg/services/accesscontrol/ossaccesscontrol/team.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package ossaccesscontrol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
||||||
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
|
"github.com/grafana/grafana/pkg/services/licensing"
|
||||||
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
|
"github.com/grafana/grafana/pkg/services/team/teamimpl"
|
||||||
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TeamPermissionsService struct {
|
||||||
|
*resourcepermissions.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
TeamMemberActions = []string{
|
||||||
|
accesscontrol.ActionTeamsRead,
|
||||||
|
}
|
||||||
|
|
||||||
|
TeamAdminActions = []string{
|
||||||
|
accesscontrol.ActionTeamsRead,
|
||||||
|
accesscontrol.ActionTeamsDelete,
|
||||||
|
accesscontrol.ActionTeamsWrite,
|
||||||
|
accesscontrol.ActionTeamsPermissionsRead,
|
||||||
|
accesscontrol.ActionTeamsPermissionsWrite,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProvideTeamPermissions(
|
||||||
|
cfg *setting.Cfg, features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB,
|
||||||
|
ac accesscontrol.AccessControl, license licensing.Licensing, service accesscontrol.Service,
|
||||||
|
teamService team.Service, userService user.Service, actionSetService resourcepermissions.ActionSetService,
|
||||||
|
) (*TeamPermissionsService, error) {
|
||||||
|
options := resourcepermissions.Options{
|
||||||
|
Resource: "teams",
|
||||||
|
ResourceAttribute: "id",
|
||||||
|
OnlyManaged: true,
|
||||||
|
ResourceValidator: func(ctx context.Context, orgID int64, resourceID string) error {
|
||||||
|
id, err := strconv.ParseInt(resourceID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = teamService.GetTeamByID(context.Background(), &team.GetTeamByIDQuery{
|
||||||
|
OrgID: orgID,
|
||||||
|
ID: id,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Assignments: resourcepermissions.Assignments{
|
||||||
|
Users: true,
|
||||||
|
Teams: false,
|
||||||
|
BuiltInRoles: false,
|
||||||
|
},
|
||||||
|
PermissionsToActions: map[string][]string{
|
||||||
|
"Member": TeamMemberActions,
|
||||||
|
"Admin": TeamAdminActions,
|
||||||
|
},
|
||||||
|
ReaderRoleName: "Team permission reader",
|
||||||
|
WriterRoleName: "Team permission writer",
|
||||||
|
RoleGroup: "Teams",
|
||||||
|
OnSetUser: func(session *db.Session, orgID int64, user accesscontrol.User, resourceID, permission string) error {
|
||||||
|
teamId, err := strconv.ParseInt(resourceID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch permission {
|
||||||
|
case "Member":
|
||||||
|
return teamimpl.AddOrUpdateTeamMemberHook(session, user.ID, orgID, teamId, user.IsExternal, 0)
|
||||||
|
case "Admin":
|
||||||
|
return teamimpl.AddOrUpdateTeamMemberHook(session, user.ID, orgID, teamId, user.IsExternal, dashboardaccess.PERMISSION_ADMIN)
|
||||||
|
case "":
|
||||||
|
return teamimpl.RemoveTeamMemberHook(session, &team.RemoveTeamMemberCommand{
|
||||||
|
OrgID: orgID,
|
||||||
|
UserID: user.ID,
|
||||||
|
TeamID: teamId,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid team permission type %s", permission)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
srv, err := resourcepermissions.New(cfg, options, features, router, license, ac, service, sql, teamService, userService, actionSetService)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &TeamPermissionsService{srv}, nil
|
||||||
|
}
|
@ -691,7 +691,7 @@ func (s *store) createPermissions(sess *db.Session, roleID int64, cmd SetResourc
|
|||||||
|
|
||||||
// if we have actionset feature enabled and are only working with action sets
|
// if we have actionset feature enabled and are only working with action sets
|
||||||
// skip adding the missing actions to the permissions table
|
// skip adding the missing actions to the permissions table
|
||||||
if !(s.shouldStoreActionSet(resource, permission) && s.cfg.OnlyStoreAccessActionSets) {
|
if !(s.shouldStoreActionSet(resource, permission) && s.cfg.RBAC.OnlyStoreAccessActionSets) {
|
||||||
for action := range missingActions {
|
for action := range missingActions {
|
||||||
p := managedPermission(action, resource, resourceID, resourceAttribute)
|
p := managedPermission(action, resource, resourceID, resourceAttribute)
|
||||||
p.RoleID = roleID
|
p.RoleID = roleID
|
||||||
|
@ -488,6 +488,15 @@ func (dr *DashboardServiceImpl) GetDashboardsByPluginID(ctx context.Context, que
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dr *DashboardServiceImpl) setDefaultPermissions(ctx context.Context, dto *dashboards.SaveDashboardDTO, dash *dashboards.Dashboard, provisioned bool) {
|
func (dr *DashboardServiceImpl) setDefaultPermissions(ctx context.Context, dto *dashboards.SaveDashboardDTO, dash *dashboards.Dashboard, provisioned bool) {
|
||||||
|
resource := "dashboard"
|
||||||
|
if dash.IsFolder {
|
||||||
|
resource = "folder"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !dr.cfg.RBAC.PermissionsOnCreation(resource) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Dashboard).Inc()
|
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Dashboard).Inc()
|
||||||
// nolint:staticcheck
|
// nolint:staticcheck
|
||||||
inFolder := dash.FolderID > 0
|
inFolder := dash.FolderID > 0
|
||||||
@ -524,6 +533,10 @@ func (dr *DashboardServiceImpl) setDefaultPermissions(ctx context.Context, dto *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dr *DashboardServiceImpl) setDefaultFolderPermissions(ctx context.Context, cmd *folder.CreateFolderCommand, f *folder.Folder, provisioned bool) {
|
func (dr *DashboardServiceImpl) setDefaultFolderPermissions(ctx context.Context, cmd *folder.CreateFolderCommand, f *folder.Folder, provisioned bool) {
|
||||||
|
if !dr.cfg.RBAC.PermissionsOnCreation("folder") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
inFolder := f.ParentUID != ""
|
inFolder := f.ParentUID != ""
|
||||||
var permissions []accesscontrol.SetResourcePermissionCommand
|
var permissions []accesscontrol.SetResourcePermissionCommand
|
||||||
|
|
||||||
|
@ -280,20 +280,24 @@ func (s *Service) AddDataSource(ctx context.Context, cmd *datasources.AddDataSou
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// This belongs in Data source permissions, and we probably want
|
if s.cfg.RBAC.PermissionsOnCreation("datasource") {
|
||||||
// to do this with a hook in the store and rollback on fail.
|
// This belongs in Data source permissions, and we probably want
|
||||||
// We can't use events, because there's no way to communicate
|
// to do this with a hook in the store and rollback on fail.
|
||||||
// failure, and we want "not being able to set default perms"
|
// We can't use events, because there's no way to communicate
|
||||||
// to fail the creation.
|
// failure, and we want "not being able to set default perms"
|
||||||
permissions := []accesscontrol.SetResourcePermissionCommand{
|
// to fail the creation.
|
||||||
{BuiltinRole: "Viewer", Permission: "Query"},
|
permissions := []accesscontrol.SetResourcePermissionCommand{
|
||||||
{BuiltinRole: "Editor", Permission: "Query"},
|
{BuiltinRole: "Viewer", Permission: "Query"},
|
||||||
|
{BuiltinRole: "Editor", Permission: "Query"},
|
||||||
|
}
|
||||||
|
if cmd.UserID != 0 {
|
||||||
|
permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{UserID: cmd.UserID, Permission: "Admin"})
|
||||||
|
}
|
||||||
|
_, err = s.permissionsService.SetPermissions(ctx, cmd.OrgID, dataSource.UID, permissions...)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if cmd.UserID != 0 {
|
|
||||||
permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{UserID: cmd.UserID, Permission: "Admin"})
|
return nil
|
||||||
}
|
|
||||||
_, err = s.permissionsService.SetPermissions(ctx, cmd.OrgID, dataSource.UID, permissions...)
|
|
||||||
return err
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
@ -570,7 +571,11 @@ func TestService_DeleteDataSource(t *testing.T) {
|
|||||||
permissionSvc.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil).Once()
|
permissionSvc.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil).Once()
|
||||||
permissionSvc.On("DeleteResourcePermissions", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
permissionSvc.On("DeleteResourcePermissions", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||||
|
|
||||||
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, &setting.Cfg{}, featuremgmt.WithFeatures(), acmock.New(), permissionSvc, quotaService, &pluginstore.FakePluginStore{}, &pluginfakes.FakePluginClient{}, nil)
|
f := ini.Empty()
|
||||||
|
f.Section("rbac").Key("resources_with_managed_permissions_on_creation").SetValue("datasource")
|
||||||
|
cfg, err := setting.NewCfgFromINIFile(f)
|
||||||
|
require.NoError(t, err)
|
||||||
|
dsService, err := ProvideService(sqlStore, secretsService, secretsStore, cfg, featuremgmt.WithFeatures(), acmock.New(), permissionSvc, quotaService, &pluginstore.FakePluginStore{}, &pluginfakes.FakePluginClient{}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// First add the datasource
|
// First add the datasource
|
||||||
|
@ -7,10 +7,10 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/middleware/requestmeta"
|
"github.com/grafana/grafana/pkg/middleware/requestmeta"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
@ -98,25 +98,25 @@ func (api *ServiceAccountsAPI) CreateServiceAccount(c *contextmodel.ReqContext)
|
|||||||
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to create service account", err)
|
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to create service account", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace, identifier := c.SignedInUser.GetNamespacedID()
|
if api.cfg.RBAC.PermissionsOnCreation("service-account") {
|
||||||
|
if c.SignedInUser.GetID().IsNamespace(authn.NamespaceUser) {
|
||||||
|
userID, err := c.SignedInUser.GetID().ParseInt()
|
||||||
|
if err != nil {
|
||||||
|
return response.Error(http.StatusInternalServerError, "Failed to parse user id", err)
|
||||||
|
}
|
||||||
|
|
||||||
if namespace == identity.NamespaceUser {
|
if _, err := api.permissionService.SetUserPermission(c.Req.Context(),
|
||||||
userID, err := identity.IntIdentifier(namespace, identifier)
|
c.SignedInUser.GetOrgID(), accesscontrol.User{ID: userID},
|
||||||
if err != nil {
|
strconv.FormatInt(serviceAccount.Id, 10), "Admin"); err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "Failed to parse user id", err)
|
return response.Error(http.StatusInternalServerError, "Failed to set permissions for service account creator", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.permissionService.SetUserPermission(c.Req.Context(),
|
// Clear permission cache for the user who's created the service account, so that new permissions are fetched for their next call
|
||||||
c.SignedInUser.GetOrgID(), accesscontrol.User{ID: userID},
|
// Required for cases when caller wants to immediately interact with the newly created object
|
||||||
strconv.FormatInt(serviceAccount.Id, 10), "Admin"); err != nil {
|
api.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
|
||||||
return response.Error(http.StatusInternalServerError, "Failed to set permissions for service account creator", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear permission cache for the user who's created the service account, so that new permissions are fetched for their next call
|
|
||||||
// Required for cases when caller wants to immediately interact with the newly created object
|
|
||||||
api.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
|
|
||||||
|
|
||||||
return response.JSON(http.StatusCreated, serviceAccount)
|
return response.JSON(http.StatusCreated, serviceAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,9 +322,6 @@ type Cfg struct {
|
|||||||
// GrafanaJavascriptAgent config
|
// GrafanaJavascriptAgent config
|
||||||
GrafanaJavascriptAgent GrafanaJavascriptAgent
|
GrafanaJavascriptAgent GrafanaJavascriptAgent
|
||||||
|
|
||||||
// accessactionsets
|
|
||||||
OnlyStoreAccessActionSets bool
|
|
||||||
|
|
||||||
// Data sources
|
// Data sources
|
||||||
DataSourceLimit int
|
DataSourceLimit int
|
||||||
// Number of queries to be executed concurrently. Only for the datasource supports concurrency.
|
// Number of queries to be executed concurrently. Only for the datasource supports concurrency.
|
||||||
@ -467,14 +464,7 @@ type Cfg struct {
|
|||||||
OAuth2ServerGeneratedKeyTypeForClient string
|
OAuth2ServerGeneratedKeyTypeForClient string
|
||||||
OAuth2ServerAccessTokenLifespan time.Duration
|
OAuth2ServerAccessTokenLifespan time.Duration
|
||||||
|
|
||||||
// Access Control
|
RBAC RBACSettings
|
||||||
RBACPermissionCache bool
|
|
||||||
// Enable Permission validation during role creation and provisioning
|
|
||||||
RBACPermissionValidationEnabled bool
|
|
||||||
// Reset basic roles permissions on start-up
|
|
||||||
RBACResetBasicRoles bool
|
|
||||||
// RBAC single organization. This configuration option is subject to change.
|
|
||||||
RBACSingleOrganization bool
|
|
||||||
|
|
||||||
Zanzana ZanzanaSettings
|
Zanzana ZanzanaSettings
|
||||||
|
|
||||||
@ -1116,7 +1106,7 @@ func (cfg *Cfg) parseINIFile(iniFile *ini.File) error {
|
|||||||
|
|
||||||
readOAuth2ServerSettings(cfg)
|
readOAuth2ServerSettings(cfg)
|
||||||
|
|
||||||
readAccessControlSettings(iniFile, cfg)
|
cfg.readRBACSettings()
|
||||||
|
|
||||||
cfg.readZanzanaSettings()
|
cfg.readZanzanaSettings()
|
||||||
|
|
||||||
@ -1657,15 +1647,6 @@ func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readAccessControlSettings(iniFile *ini.File, cfg *Cfg) {
|
|
||||||
rbac := iniFile.Section("rbac")
|
|
||||||
cfg.RBACPermissionCache = rbac.Key("permission_cache").MustBool(true)
|
|
||||||
cfg.RBACPermissionValidationEnabled = rbac.Key("permission_validation_enabled").MustBool(false)
|
|
||||||
cfg.RBACResetBasicRoles = rbac.Key("reset_basic_roles").MustBool(false)
|
|
||||||
cfg.RBACSingleOrganization = rbac.Key("single_organization").MustBool(false)
|
|
||||||
cfg.OnlyStoreAccessActionSets = rbac.Key("only_store_access_action_sets").MustBool(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func readOAuth2ServerSettings(cfg *Cfg) {
|
func readOAuth2ServerSettings(cfg *Cfg) {
|
||||||
oauth2Srv := cfg.SectionWithEnvOverrides("oauth2_server")
|
oauth2Srv := cfg.SectionWithEnvOverrides("oauth2_server")
|
||||||
cfg.OAuth2ServerEnabled = oauth2Srv.Key("enabled").MustBool(false)
|
cfg.OAuth2ServerEnabled = oauth2Srv.Key("enabled").MustBool(false)
|
||||||
|
61
pkg/setting/settings_rbac.go
Normal file
61
pkg/setting/settings_rbac.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RBACSettings struct {
|
||||||
|
// Enable permission cache
|
||||||
|
PermissionCache bool
|
||||||
|
// Enable Permission validation during role creation and provisioning
|
||||||
|
PermissionValidationEnabled bool
|
||||||
|
// Reset basic roles permissions on start-up
|
||||||
|
ResetBasicRoles bool
|
||||||
|
// RBAC single organization. This configuration option is subject to change.
|
||||||
|
SingleOrganization bool
|
||||||
|
|
||||||
|
OnlyStoreAccessActionSets bool
|
||||||
|
|
||||||
|
// set of resources that should generate managed permissions when created
|
||||||
|
resourcesWithPermissionsOnCreation map[string]struct{}
|
||||||
|
|
||||||
|
// set of resources that should we should seed wildcard scopes for
|
||||||
|
resourcesWithWildcardSeed map[string]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Cfg) readRBACSettings() {
|
||||||
|
s := RBACSettings{}
|
||||||
|
|
||||||
|
rbac := cfg.Raw.Section("rbac")
|
||||||
|
s.PermissionCache = rbac.Key("permission_cache").MustBool(true)
|
||||||
|
s.PermissionValidationEnabled = rbac.Key("permission_validation_enabled").MustBool(false)
|
||||||
|
s.ResetBasicRoles = rbac.Key("reset_basic_roles").MustBool(false)
|
||||||
|
s.SingleOrganization = rbac.Key("single_organization").MustBool(false)
|
||||||
|
s.OnlyStoreAccessActionSets = rbac.Key("only_store_access_action_sets").MustBool(false)
|
||||||
|
|
||||||
|
// List of resources to generate managed permissions for upon resource creation (dashboard, folder, service-account, datasource)
|
||||||
|
resources := util.SplitString(rbac.Key("resources_with_managed_permissions_on_creation").MustString("dashboard, folder, service-account, datasource"))
|
||||||
|
s.resourcesWithPermissionsOnCreation = map[string]struct{}{}
|
||||||
|
for _, resource := range resources {
|
||||||
|
s.resourcesWithPermissionsOnCreation[resource] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of resources to seed managed permission wildcards for (dashboard, folder, datasource)
|
||||||
|
resources = util.SplitString(rbac.Key("resources_with_seeded_wildcard_access").MustString(""))
|
||||||
|
s.resourcesWithWildcardSeed = map[string]struct{}{}
|
||||||
|
for _, resource := range resources {
|
||||||
|
s.resourcesWithWildcardSeed[resource] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.RBAC = s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RBACSettings) PermissionsOnCreation(resource string) bool {
|
||||||
|
_, ok := r.resourcesWithPermissionsOnCreation[resource]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RBACSettings) PermissionsWildcardSeed(resource string) bool {
|
||||||
|
_, ok := r.resourcesWithWildcardSeed[resource]
|
||||||
|
return ok
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user