DataSourcePermissions: Handle licensing properly for ds permissions (#59694)

* RBAC: add viewer grand if dspermissions enforcement is not enabled

* RBAC: Change permissions based on role prefix

* RBAC: Add option to for permission service to add a license middleware

* RBAC: Remove actions from query struct
This commit is contained in:
Karl Persson 2022-12-02 13:19:14 +01:00 committed by GitHub
parent a7d4bbf024
commit 6d1bcd9f40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 29 additions and 42 deletions

View File

@ -114,7 +114,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
}
// when running oss or enterprise without a license all users should be able to query data sources
if !hs.License.FeatureEnabled("accesscontrol.enforcement") {
if !hs.License.FeatureEnabled("dspermissions.enforcement") {
datasourcesReaderRole.Grants = []string{string(org.RoleViewer)}
}

View File

@ -18,7 +18,6 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/api"
"github.com/grafana/grafana/pkg/services/accesscontrol/database"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/pluginutils"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/user"
@ -87,10 +86,6 @@ func (s *Service) GetUsageStats(_ context.Context) map[string]interface{} {
}
}
var actionsToFetch = append(
ossaccesscontrol.TeamAdminActions, append(ossaccesscontrol.DashboardAdminActions, append(ossaccesscontrol.FolderAdminActions, ossaccesscontrol.ServiceAccountAdminActions...)...)...,
)
// GetUserPermissions returns user permissions based on built-in roles
func (s *Service) GetUserPermissions(ctx context.Context, user *user.SignedInUser, options accesscontrol.Options) ([]accesscontrol.Permission, error) {
timer := prometheus.NewTimer(metrics.MAccessPermissionsSummary)
@ -112,11 +107,11 @@ func (s *Service) getUserPermissions(ctx context.Context, user *user.SignedInUse
}
dbPermissions, err := s.store.GetUserPermissions(ctx, accesscontrol.GetUserPermissionsQuery{
OrgID: user.OrgID,
UserID: user.UserID,
Roles: accesscontrol.GetOrgRoles(user),
TeamIDs: user.Teams,
Actions: actionsToFetch,
OrgID: user.OrgID,
UserID: user.UserID,
Roles: accesscontrol.GetOrgRoles(user),
TeamIDs: user.Teams,
RolePrefix: accesscontrol.ManagedRolePrefix,
})
if err != nil {
return nil, err

View File

@ -35,16 +35,11 @@ func (s *AccessControlStore) GetUserPermissions(ctx context.Context, query acces
INNER JOIN role ON role.id = permission.role_id
` + filter
if len(query.Actions) > 0 {
q += " WHERE permission.action IN("
if len(query.Actions) > 0 {
q += "?" + strings.Repeat(",?", len(query.Actions)-1)
}
q += ")"
for _, a := range query.Actions {
params = append(params, a)
}
if query.RolePrefix != "" {
q += " WHERE role.name LIKE ?"
params = append(params, query.RolePrefix+"%")
}
if err := sess.SQL(q, params...).Find(&result); err != nil {
return err
}

View File

@ -30,7 +30,6 @@ type getUserPermissionsTestCase struct {
userPermissions []string
teamPermissions []string
builtinPermissions []string
actions []string
expected int
}
@ -63,16 +62,6 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
builtinPermissions: []string{"5", "6"},
expected: 5,
},
{
desc: "Should filter on actions",
orgID: 1,
role: "",
userPermissions: []string{"1", "2", "10"},
teamPermissions: []string{"100", "2"},
builtinPermissions: []string{"5", "6"},
expected: 3,
actions: []string{"dashboards:write"},
},
{
desc: "should only get br permissions for anonymous user",
anonymousUser: true,
@ -137,7 +126,6 @@ func TestAccessControlStore_GetUserPermissions(t *testing.T) {
OrgID: tt.orgID,
UserID: userID,
Roles: roles,
Actions: tt.actions,
TeamIDs: teamIDs,
})

View File

@ -215,11 +215,11 @@ func (p Permission) OSSPermission() Permission {
}
type GetUserPermissionsQuery struct {
OrgID int64 `json:"-"`
UserID int64 `json:"userId"`
Roles []string
Actions []string
TeamIDs []int64
OrgID int64
UserID int64
Roles []string
TeamIDs []int64
RolePrefix string
}
// ResourcePermission is structure that holds all actions that either a team / user / builtin-role

View File

@ -33,6 +33,10 @@ func newApi(ac accesscontrol.AccessControl, router routing.RouteRegister, manage
func (a *api) registerEndpoints() {
auth := accesscontrol.Middleware(a.ac)
disable := disableMiddleware(a.ac.IsDisabled())
licenseMW := a.service.options.LicenseMW
if licenseMW == nil {
licenseMW = nopMiddleware
}
a.router.Group(fmt.Sprintf("/api/access-control/%s", a.service.options.Resource), func(r routing.RouteRegister) {
actionRead := fmt.Sprintf("%s.permissions:read", a.service.options.Resource)
@ -40,15 +44,15 @@ func (a *api) registerEndpoints() {
scope := accesscontrol.Scope(a.service.options.Resource, a.service.options.ResourceAttribute, accesscontrol.Parameter(":resourceID"))
r.Get("/description", auth(disable, accesscontrol.EvalPermission(actionRead)), routing.Wrap(a.getDescription))
r.Get("/:resourceID", auth(disable, accesscontrol.EvalPermission(actionRead, scope)), routing.Wrap(a.getPermissions))
r.Post("/:resourceID", auth(disable, accesscontrol.EvalPermission(actionWrite, scope)), routing.Wrap(a.setPermissions))
r.Post("/:resourceID", licenseMW, auth(disable, accesscontrol.EvalPermission(actionWrite, scope)), routing.Wrap(a.setPermissions))
if a.service.options.Assignments.Users {
r.Post("/:resourceID/users/:userID", auth(disable, accesscontrol.EvalPermission(actionWrite, scope)), routing.Wrap(a.setUserPermission))
r.Post("/:resourceID/users/:userID", licenseMW, auth(disable, accesscontrol.EvalPermission(actionWrite, scope)), routing.Wrap(a.setUserPermission))
}
if a.service.options.Assignments.Teams {
r.Post("/:resourceID/teams/:teamID", auth(disable, accesscontrol.EvalPermission(actionWrite, scope)), routing.Wrap(a.setTeamPermission))
r.Post("/:resourceID/teams/:teamID", licenseMW, auth(disable, accesscontrol.EvalPermission(actionWrite, scope)), routing.Wrap(a.setTeamPermission))
}
if a.service.options.Assignments.BuiltInRoles {
r.Post("/:resourceID/builtInRoles/:builtInRole", auth(disable, accesscontrol.EvalPermission(actionWrite, scope)), routing.Wrap(a.setBuiltinRolePermission))
r.Post("/:resourceID/builtInRoles/:builtInRole", licenseMW, auth(disable, accesscontrol.EvalPermission(actionWrite, scope)), routing.Wrap(a.setBuiltinRolePermission))
}
})
}

View File

@ -15,3 +15,5 @@ func disableMiddleware(shouldDisable bool) web.Handler {
}
}
}
func nopMiddleware(c *models.ReqContext) {}

View File

@ -5,6 +5,7 @@ import (
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/web"
)
type ResourceValidator func(ctx context.Context, orgID int64, resourceID string) error
@ -39,4 +40,6 @@ type Options struct {
OnSetBuiltInRole func(session *db.Session, orgID int64, builtInRole, resourceID, permission string) error
// InheritedScopesSolver if configured can generate additional scopes that will be used when fetching permissions for a resource
InheritedScopesSolver InheritedScopesSolver
// LicenseMV if configured is applied to endpoints that can modify permissions
LicenseMW web.Handler
}

View File

@ -68,7 +68,7 @@ export function buildNavModel(dataSource: DataSourceSettings, plugin: GenericDat
dsPermissions.tabSuffix = () => ProBadge({ experimentId: permissionsExperimentId, eventVariant: 'trial' });
}
if (featureEnabled('dspermissions')) {
if (featureEnabled('dspermissions.enforcement')) {
if (contextSrv.hasPermission(AccessControlAction.DataSourcesPermissionsRead)) {
navModel.children!.push(dsPermissions);
}