App platform: Implement perm check with direct db access (#97579)

* implement perm check with direct db access

* add tests

* more tests

* Update pkg/services/authz/rbac/service.go

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>

* Update pkg/services/authz/rbac/service.go

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>

* allow fetching permissions for a user who is not a member of the org

* linting

* fix typo

---------

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
pull/97151/head
Ieva 6 months ago committed by GitHub
parent 3c78fb1aa4
commit ded90fa28d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 13
      pkg/services/authz/rbac/models.go
  2. 194
      pkg/services/authz/rbac/service.go
  3. 151
      pkg/services/authz/rbac/service_test.go
  4. 4
      pkg/services/authz/rbac/store/basic_role_query.sql
  5. 30
      pkg/services/authz/rbac/store/models.go
  6. 13
      pkg/services/authz/rbac/store/permission_query.sql
  7. 92
      pkg/services/authz/rbac/store/queries.go
  8. 97
      pkg/services/authz/rbac/store/sql_test.go
  9. 115
      pkg/services/authz/rbac/store/store.go
  10. 4
      pkg/services/authz/rbac/store/testdata/mysql--basic_role_query-basic_roles.sql
  11. 7
      pkg/services/authz/rbac/store/testdata/mysql--permission_query-admin_user.sql
  12. 8
      pkg/services/authz/rbac/store/testdata/mysql--permission_query-user_with_teams.sql
  13. 6
      pkg/services/authz/rbac/store/testdata/mysql--permission_query-viewer_user.sql
  14. 3
      pkg/services/authz/rbac/store/testdata/mysql--user_identifier_query-id_specified.sql
  15. 3
      pkg/services/authz/rbac/store/testdata/mysql--user_identifier_query-uid_specified.sql
  16. 4
      pkg/services/authz/rbac/store/testdata/postgres--basic_role_query-basic_roles.sql
  17. 7
      pkg/services/authz/rbac/store/testdata/postgres--permission_query-admin_user.sql
  18. 8
      pkg/services/authz/rbac/store/testdata/postgres--permission_query-user_with_teams.sql
  19. 6
      pkg/services/authz/rbac/store/testdata/postgres--permission_query-viewer_user.sql
  20. 3
      pkg/services/authz/rbac/store/testdata/postgres--user_identifier_query-id_specified.sql
  21. 3
      pkg/services/authz/rbac/store/testdata/postgres--user_identifier_query-uid_specified.sql
  22. 4
      pkg/services/authz/rbac/store/testdata/sqlite--basic_role_query-basic_roles.sql
  23. 7
      pkg/services/authz/rbac/store/testdata/sqlite--permission_query-admin_user.sql
  24. 8
      pkg/services/authz/rbac/store/testdata/sqlite--permission_query-user_with_teams.sql
  25. 6
      pkg/services/authz/rbac/store/testdata/sqlite--permission_query-viewer_user.sql
  26. 3
      pkg/services/authz/rbac/store/testdata/sqlite--user_identifier_query-id_specified.sql
  27. 3
      pkg/services/authz/rbac/store/testdata/sqlite--user_identifier_query-uid_specified.sql
  28. 7
      pkg/services/authz/rbac/store/user_identifier_query.sql
  29. 3
      pkg/services/authz/server.go

@ -0,0 +1,13 @@
package rbac
import "github.com/grafana/authlib/claims"
type CheckRequest struct {
Namespace claims.NamespaceInfo
UserUID string
Action string
Group string
Resource string
Verb string
Name string
}

@ -1,11 +1,23 @@
package rbac
import (
"context"
"fmt"
"strconv"
authzv1 "github.com/grafana/authlib/authz/proto/v1"
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
"github.com/grafana/authlib/claims"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"k8s.io/apiserver/pkg/endpoints/request"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/registry/apis/iam/common"
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/authz/mappers"
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
"github.com/grafana/grafana/pkg/services/authz/rbac/store"
"github.com/grafana/grafana/pkg/storage/legacysql"
)
@ -14,24 +26,180 @@ type Service struct {
authzv1.UnimplementedAuthzServiceServer
authzextv1.UnimplementedAuthzExtentionServiceServer
store *store.Store
store *store.Store
identityStore legacy.LegacyIdentityStore
actionMapper *mappers.K8sRbacMapper
logger log.Logger
tracer tracing.Tracer
}
func NewService(sql legacysql.LegacyDatabaseProvider, logger log.Logger, tracer tracing.Tracer) *Service {
func NewService(sql legacysql.LegacyDatabaseProvider, identityStore legacy.LegacyIdentityStore, logger log.Logger, tracer tracing.Tracer) *Service {
return &Service{
store: store.NewStore(sql),
logger: logger,
tracer: tracer,
store: store.NewStore(sql),
identityStore: identityStore,
actionMapper: mappers.NewK8sRbacMapper(),
logger: logger,
tracer: tracer,
}
}
func (s *Service) Check(ctx context.Context, req *authzv1.CheckRequest) (*authzv1.CheckResponse, error) {
ctx, span := s.tracer.Start(ctx, "authz_direct_db.Check")
defer span.End()
ctxLogger := s.logger.FromContext(ctx)
deny := &authzv1.CheckResponse{Allowed: false}
checkReq, err := s.validateRequest(ctx, req)
if err != nil {
ctxLogger.Error("invalid request", "error", err)
return deny, err
}
ctx = request.WithNamespace(ctx, req.GetNamespace())
permissions, err := s.getUserPermissions(ctx, checkReq)
if err != nil {
ctxLogger.Error("could not get user permissions", "subject", req.GetSubject(), "error", err)
return deny, err
}
allowed, err := s.checkPermission(ctx, permissions, checkReq)
if err != nil {
ctxLogger.Error("could not check permission", "error", err)
return deny, err
}
return &authzv1.CheckResponse{Allowed: allowed}, nil
}
func (s *Service) validateRequest(ctx context.Context, req *authzv1.CheckRequest) (*CheckRequest, error) {
ctxLogger := s.logger.FromContext(ctx)
if req.GetNamespace() == "" {
return nil, status.Error(codes.InvalidArgument, "namespace is required")
}
namespace := req.GetNamespace()
ns, err := claims.ParseNamespace(namespace)
if err != nil {
ctxLogger.Error("could not parse namespace", "namespace", namespace, "error", err)
return nil, err
}
if req.GetSubject() == "" {
return nil, status.Error(codes.InvalidArgument, "subject is required")
}
user := req.GetSubject()
identityType, userUID, err := claims.ParseTypeID(user)
if err != nil {
ctxLogger.Error("could not parse subject", "subject", user, "error", err)
return nil, err
}
// Permission check currently only checks user and service account permissions, so might return a false negative for other types
if !(identityType == claims.TypeUser || identityType == claims.TypeServiceAccount) {
ctxLogger.Warn("unsupported identity type", "type", identityType)
}
if req.GetGroup() == "" || req.GetResource() == "" || req.GetVerb() == "" {
return nil, status.Error(codes.InvalidArgument, "group, resource and verb are required")
}
action, ok := s.actionMapper.Action(req.GetGroup(), req.GetResource(), req.GetVerb())
if !ok {
ctxLogger.Error("could not find associated rbac action", "group", req.GetGroup(), "resource", req.GetResource(), "verb", req.GetVerb())
return nil, status.Error(codes.NotFound, "could not find associated rbac action")
}
checkReq := &CheckRequest{
Namespace: ns,
UserUID: userUID,
Action: action,
Group: req.GetGroup(),
Resource: req.GetResource(),
Verb: req.GetVerb(),
Name: req.GetName(),
}
return checkReq, nil
}
// TODO: Implement Check
// func (s *Service) Check(ctx context.Context, req *authzv1.CheckRequest) (*authzv1.CheckResponse, error) {
// This needs to be done for the database provider
// ns := req.GetNamespace()
// ctx = request.WithNamespace(ctx, ns)
func (s *Service) getUserPermissions(ctx context.Context, req *CheckRequest) ([]accesscontrol.Permission, error) {
var userIDQuery store.UserIdentifierQuery
// Assume that numeric UID is user ID
if userID, err := strconv.Atoi(req.UserUID); err == nil {
userIDQuery = store.UserIdentifierQuery{UserID: int64(userID)}
} else {
userIDQuery = store.UserIdentifierQuery{UserUID: req.UserUID}
}
userIdentifiers, err := s.store.GetUserIdentifiers(ctx, userIDQuery)
if err != nil {
return nil, fmt.Errorf("could not get user internal id: %w", err)
}
// return nil, nil
// }
basicRoles, err := s.store.GetBasicRoles(ctx, req.Namespace, store.BasicRoleQuery{UserID: userIdentifiers.ID})
if err != nil {
return nil, fmt.Errorf("could not get basic roles: %w", err)
}
teamIDs := make([]int64, 0, 50)
teamQuery := legacy.ListUserTeamsQuery{
UserUID: userIdentifiers.UID,
Pagination: common.Pagination{Limit: 50},
}
for {
teams, err := s.identityStore.ListUserTeams(ctx, req.Namespace, teamQuery)
if err != nil {
return nil, fmt.Errorf("could not get user teams: %w", err)
}
for _, team := range teams.Items {
teamIDs = append(teamIDs, team.ID)
}
teamQuery.Pagination.Continue = teams.Continue
if teams.Continue == 0 {
break
}
}
userPermQuery := store.PermissionsQuery{
UserID: userIdentifiers.ID,
Action: req.Action,
TeamIDs: teamIDs,
Role: basicRoles.Role,
IsServerAdmin: basicRoles.IsAdmin,
}
return s.store.GetUserPermissions(ctx, req.Namespace, userPermQuery)
}
func (s *Service) checkPermission(ctx context.Context, permissions []accesscontrol.Permission, req *CheckRequest) (bool, error) {
ctxLogger := s.logger.FromContext(ctx)
// Only check action if the request doesn't specify scope
if req.Name == "" {
return len(permissions) > 0, nil
}
scopeMap := getScopeMap(permissions)
// Wildcard grant, no further checks needed
if scopeMap["*"] {
return true, nil
}
scope, has := s.actionMapper.Scope(req.Group, req.Resource, req.Name)
if !has {
ctxLogger.Error("could not get attribute for resource", "resource", req.Resource)
return false, fmt.Errorf("could not get attribute for resource")
}
return scopeMap[scope], nil
}
func getScopeMap(permissions []accesscontrol.Permission) map[string]bool {
permMap := make(map[string]bool, len(permissions))
for _, perm := range permissions {
// If has any wildcard, return immediately
if perm.Kind == "*" || perm.Attribute == "*" || perm.Identifier == "*" {
return map[string]bool{"*": true}
}
permMap[perm.Scope] = true
}
return permMap
}

@ -0,0 +1,151 @@
package rbac
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/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/authz/mappers"
)
func TestService_checkPermission(t *testing.T) {
type testCase struct {
name string
permissions []accesscontrol.Permission
check CheckRequest
expected bool
}
testCases := []testCase{
{
name: "should return true if user has permission",
permissions: []accesscontrol.Permission{
{
Action: "dashboards:read",
Scope: "dashboards:uid:some_dashboard",
Kind: "dashboards",
Attribute: "uid",
Identifier: "some_dashboard",
},
},
check: CheckRequest{
Action: "dashboards:read",
Group: "dashboard.grafana.app",
Resource: "dashboards",
Name: "some_dashboard",
},
expected: true,
},
{
name: "should return false if user has permission on a different resource",
permissions: []accesscontrol.Permission{
{
Action: "dashboards:read",
Scope: "dashboards:uid:another_dashboard",
Kind: "dashboards",
Attribute: "uid",
Identifier: "another_dashboard",
},
},
check: CheckRequest{
Action: "dashboards:read",
Group: "dashboard.grafana.app",
Resource: "dashboards",
Name: "some_dashboard",
},
expected: false,
},
{
name: "should return true if user has wildcard permission on identifier",
permissions: []accesscontrol.Permission{
{
Action: "dashboards:read",
Scope: "dashboards:uid:*",
Kind: "dashboards",
Attribute: "uid",
Identifier: "*",
},
},
check: CheckRequest{
Action: "dashboards:read",
Group: "dashboard.grafana.app",
Resource: "dashboards",
Name: "some_dashboard",
},
expected: true,
},
{
name: "should return true if user has wildcard permission on attribute",
permissions: []accesscontrol.Permission{
{
Action: "dashboards:read",
Scope: "dashboards:*",
Kind: "dashboards",
Attribute: "*",
},
},
check: CheckRequest{
Action: "dashboards:read",
Group: "dashboard.grafana.app",
Resource: "dashboards",
Name: "some_dashboard",
},
expected: true,
},
{
name: "should return true if user has wildcard permission on kind",
permissions: []accesscontrol.Permission{
{
Action: "dashboards:read",
Scope: "*",
Kind: "*",
},
},
check: CheckRequest{
Action: "dashboards:read",
Group: "dashboard.grafana.app",
Resource: "dashboards",
Name: "some_dashboard",
},
expected: true,
},
{
name: "should return true if no resource is specified",
permissions: []accesscontrol.Permission{
{
Action: "folders:create",
},
},
check: CheckRequest{
Action: "folders:create",
Group: "folder.grafana.app",
Resource: "folders",
},
expected: true,
},
{
name: "should return false if user has no permissions on resource",
permissions: []accesscontrol.Permission{},
check: CheckRequest{
Action: "dashboards:read",
Group: "dashboard.grafana.app",
Resource: "dashboards",
Name: "some_dashboard",
},
expected: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
s := &Service{logger: log.New("test"), actionMapper: mappers.NewK8sRbacMapper()}
got, err := s.checkPermission(context.Background(), tc.permissions, &tc.check)
require.NoError(t, err)
assert.Equal(t, tc.expected, got)
})
}
}

@ -0,0 +1,4 @@
SELECT COALESCE(ou.role, 'None') AS role, u.is_admin
FROM {{ .Ident .UserTable }} as u
LEFT JOIN {{ .Ident .OrgUserTable }} as ou ON ou.user_id = u.id AND ou.org_id = {{ .Arg .Query.OrgID }}
WHERE u.id = {{ .Arg .Query.UserID }}

@ -0,0 +1,30 @@
package store
type UserIdentifiers struct {
ID int64
UID string
}
type BasicRole struct {
Role string
IsAdmin bool
}
type PermissionsQuery struct {
OrgID int64
UserID int64
Action string
TeamIDs []int64
Role string
IsServerAdmin bool
}
type BasicRoleQuery struct {
UserID int64
OrgID int64
}
type UserIdentifierQuery struct {
UserID int64
UserUID string
}

@ -0,0 +1,13 @@
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM {{ .Ident .PermissionTable }} as p
WHERE p.action = {{ .Arg .Query.Action }} AND p.role_id IN (
SELECT role_id FROM {{ .Ident .UserRoleTable }} as ur WHERE ur.user_id = {{ .Arg .Query.UserID }} AND (ur.org_id = {{ .Arg .Query.OrgID }} OR ur.org_id = 0)
{{ if .Query.TeamIDs }}
UNION
SELECT role_id FROM {{ .Ident .TeamRoleTable }} as tr WHERE tr.team_id IN ({{ .ArgList .Query.TeamIDs }}) AND tr.org_id = {{ .Arg .Query.OrgID }}
{{ end }}
UNION ALL
SELECT role_id FROM {{ .Ident .BuiltinRoleTable }} as br WHERE (br.role = {{ .Arg .Query.Role }} AND (br.org_id = {{ .Arg .Query.OrgID }} OR br.org_id = 0))
{{ if .Query.IsServerAdmin }}
OR (br.role = 'Grafana Admin')
{{ end }}
)

@ -0,0 +1,92 @@
package store
import (
"embed"
"fmt"
"text/template"
"github.com/grafana/grafana/pkg/storage/legacysql"
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
)
var (
//go:embed *.sql
sqlTemplatesFS embed.FS
sqlTemplates = template.Must(template.New("sql").ParseFS(sqlTemplatesFS, `*.sql`))
sqlUserPerms = mustTemplate("permission_query.sql")
sqlQueryBasicRoles = mustTemplate("basic_role_query.sql")
sqlUserIdentifiers = mustTemplate("user_identifier_query.sql")
)
func mustTemplate(filename string) *template.Template {
if t := sqlTemplates.Lookup(filename); t != nil {
return t
}
panic(fmt.Sprintf("template file not found: %s", filename))
}
type getUserIdentifiers struct {
sqltemplate.SQLTemplate
Query *UserIdentifierQuery
UserTable string
}
func (r getUserIdentifiers) Validate() error {
return nil
}
func newGetUserIdentifiers(sql *legacysql.LegacyDatabaseHelper, q *UserIdentifierQuery) getUserIdentifiers {
return getUserIdentifiers{
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
Query: q,
UserTable: sql.Table("user"),
}
}
type getBasicRolesQuery struct {
sqltemplate.SQLTemplate
Query *BasicRoleQuery
UserTable string
OrgUserTable string
}
func (r getBasicRolesQuery) Validate() error {
return nil
}
func newGetBasicRoles(sql *legacysql.LegacyDatabaseHelper, q *BasicRoleQuery) getBasicRolesQuery {
return getBasicRolesQuery{
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
Query: q,
UserTable: sql.Table("user"),
OrgUserTable: sql.Table("org_user"),
}
}
type getPermissionsQuery struct {
sqltemplate.SQLTemplate
Query *PermissionsQuery
PermissionTable string
UserRoleTable string
TeamRoleTable string
BuiltinRoleTable string
}
func (r getPermissionsQuery) Validate() error {
return nil
}
func newGetPermissions(sql *legacysql.LegacyDatabaseHelper, q *PermissionsQuery) getPermissionsQuery {
return getPermissionsQuery{
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
Query: q,
PermissionTable: sql.Table("permission"),
UserRoleTable: sql.Table("user_role"),
TeamRoleTable: sql.Table("team_role"),
BuiltinRoleTable: sql.Table("builtin_role"),
}
}

@ -0,0 +1,97 @@
package store
import (
"testing"
"text/template"
"github.com/grafana/grafana/pkg/storage/legacysql"
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate/mocks"
)
func TestIdentityQueries(t *testing.T) {
// prefix tables with grafana
nodb := &legacysql.LegacyDatabaseHelper{
Table: func(n string) string {
return "grafana." + n
},
}
getIdentifiers := func(q *UserIdentifierQuery) sqltemplate.SQLTemplate {
v := newGetUserIdentifiers(nodb, q)
v.SQLTemplate = mocks.NewTestingSQLTemplate()
return &v
}
getBasicRoles := func(q *BasicRoleQuery) sqltemplate.SQLTemplate {
v := newGetBasicRoles(nodb, q)
v.SQLTemplate = mocks.NewTestingSQLTemplate()
return &v
}
getPermissions := func(q *PermissionsQuery) sqltemplate.SQLTemplate {
v := newGetPermissions(nodb, q)
v.SQLTemplate = mocks.NewTestingSQLTemplate()
return &v
}
mocks.CheckQuerySnapshots(t, mocks.TemplateTestSetup{
RootDir: "testdata",
Templates: map[*template.Template][]mocks.TemplateTestCase{
sqlUserIdentifiers: {
{
Name: "id_specified",
Data: getIdentifiers(&UserIdentifierQuery{
UserID: 1,
}),
},
{
Name: "uid_specified",
Data: getIdentifiers(&UserIdentifierQuery{
UserUID: "some_uid",
}),
},
},
sqlQueryBasicRoles: {
{
Name: "basic_roles",
Data: getBasicRoles(&BasicRoleQuery{
UserID: 1,
OrgID: 1,
}),
},
},
sqlUserPerms: {
{
Name: "viewer_user",
Data: getPermissions(&PermissionsQuery{
UserID: 1,
OrgID: 1,
Action: "folders:read",
Role: "Viewer",
}),
},
{
Name: "admin_user",
Data: getPermissions(&PermissionsQuery{
UserID: 1,
OrgID: 1,
Action: "folders:read",
Role: "Admin",
IsServerAdmin: true,
}),
},
{
Name: "user_with_teams",
Data: getPermissions(&PermissionsQuery{
UserID: 1,
OrgID: 1,
Action: "folders:read",
Role: "None",
TeamIDs: []int64{1, 2},
}),
},
},
},
})
}

@ -1,8 +1,15 @@
package store
import "github.com/grafana/grafana/pkg/storage/legacysql"
import (
"fmt"
// TODO (gamab): Implement GetRoles, GetTeams, GetFolders, GetPermissions
"github.com/grafana/authlib/claims"
"golang.org/x/net/context"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/storage/legacysql"
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
)
type Store struct {
sql legacysql.LegacyDatabaseProvider
@ -13,3 +20,107 @@ func NewStore(sql legacysql.LegacyDatabaseProvider) *Store {
sql: sql,
}
}
func (s *Store) GetUserPermissions(ctx context.Context, ns claims.NamespaceInfo, query PermissionsQuery) ([]accesscontrol.Permission, error) {
sql, err := s.sql(ctx)
if err != nil {
return nil, err
}
query.OrgID = ns.OrgID
req := newGetPermissions(sql, &query)
q, err := sqltemplate.Execute(sqlUserPerms, req)
if err != nil {
return nil, err
}
res, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
if err != nil {
return nil, err
}
defer func() {
if res != nil {
_ = res.Close()
}
}()
var perms []accesscontrol.Permission
for res.Next() {
var perm accesscontrol.Permission
if err := res.Scan(&perm.Action, &perm.Kind, &perm.Attribute, &perm.Identifier, &perm.Scope); err != nil {
return nil, err
}
perms = append(perms, perm)
}
return perms, nil
}
func (s *Store) GetUserIdentifiers(ctx context.Context, query UserIdentifierQuery) (*UserIdentifiers, error) {
sql, err := s.sql(ctx)
if err != nil {
return nil, err
}
req := newGetUserIdentifiers(sql, &query)
q, err := sqltemplate.Execute(sqlUserIdentifiers, req)
if err != nil {
return nil, err
}
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
defer func() {
if rows != nil {
_ = rows.Close()
}
}()
if err != nil {
return nil, err
}
if !rows.Next() {
return nil, fmt.Errorf("user could not be found")
}
var userIDs UserIdentifiers
if err := rows.Scan(&userIDs.ID, &userIDs.UID); err != nil {
return nil, err
}
return &userIDs, nil
}
func (s *Store) GetBasicRoles(ctx context.Context, ns claims.NamespaceInfo, query BasicRoleQuery) (*BasicRole, error) {
sql, err := s.sql(ctx)
if err != nil {
return nil, err
}
query.OrgID = ns.OrgID
req := newGetBasicRoles(sql, &query)
q, err := sqltemplate.Execute(sqlQueryBasicRoles, req)
if err != nil {
return nil, err
}
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
defer func() {
if rows != nil {
_ = rows.Close()
}
}()
if err != nil {
return nil, err
}
if !rows.Next() {
return nil, fmt.Errorf("no basic roles found for the user")
}
var role BasicRole
if err := rows.Scan(&role.Role, &role.IsAdmin); err != nil {
return nil, err
}
return &role, nil
}

@ -0,0 +1,4 @@
SELECT COALESCE(ou.role, 'None') AS role, u.is_admin
FROM `grafana`.`user` as u
LEFT JOIN `grafana`.`org_user` as ou ON ou.user_id = u.id AND ou.org_id = 1
WHERE u.id = 1

@ -0,0 +1,7 @@
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
WHERE p.action = 'folders:read' AND p.role_id IN (
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION ALL
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
OR (br.role = 'Grafana Admin')
)

@ -0,0 +1,8 @@
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
WHERE p.action = 'folders:read' AND p.role_id IN (
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION
SELECT role_id FROM `grafana`.`team_role` as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
UNION ALL
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
)

@ -0,0 +1,6 @@
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
WHERE p.action = 'folders:read' AND p.role_id IN (
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION ALL
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
)

@ -0,0 +1,3 @@
SELECT u.id, u.uid
FROM `grafana`.`user` as u
WHERE u.id = 1

@ -0,0 +1,3 @@
SELECT u.id, u.uid
FROM `grafana`.`user` as u
WHERE u.uid = 'some_uid'

@ -0,0 +1,4 @@
SELECT COALESCE(ou.role, 'None') AS role, u.is_admin
FROM "grafana"."user" as u
LEFT JOIN "grafana"."org_user" as ou ON ou.user_id = u.id AND ou.org_id = 1
WHERE u.id = 1

@ -0,0 +1,7 @@
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
WHERE p.action = 'folders:read' AND p.role_id IN (
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION ALL
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
OR (br.role = 'Grafana Admin')
)

@ -0,0 +1,8 @@
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
WHERE p.action = 'folders:read' AND p.role_id IN (
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION
SELECT role_id FROM "grafana"."team_role" as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
UNION ALL
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
)

@ -0,0 +1,6 @@
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
WHERE p.action = 'folders:read' AND p.role_id IN (
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION ALL
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
)

@ -0,0 +1,3 @@
SELECT u.id, u.uid
FROM "grafana"."user" as u
WHERE u.id = 1

@ -0,0 +1,3 @@
SELECT u.id, u.uid
FROM "grafana"."user" as u
WHERE u.uid = 'some_uid'

@ -0,0 +1,4 @@
SELECT COALESCE(ou.role, 'None') AS role, u.is_admin
FROM "grafana"."user" as u
LEFT JOIN "grafana"."org_user" as ou ON ou.user_id = u.id AND ou.org_id = 1
WHERE u.id = 1

@ -0,0 +1,7 @@
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
WHERE p.action = 'folders:read' AND p.role_id IN (
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION ALL
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
OR (br.role = 'Grafana Admin')
)

@ -0,0 +1,8 @@
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
WHERE p.action = 'folders:read' AND p.role_id IN (
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION
SELECT role_id FROM "grafana"."team_role" as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
UNION ALL
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
)

@ -0,0 +1,6 @@
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
WHERE p.action = 'folders:read' AND p.role_id IN (
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
UNION ALL
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
)

@ -0,0 +1,3 @@
SELECT u.id, u.uid
FROM "grafana"."user" as u
WHERE u.id = 1

@ -0,0 +1,3 @@
SELECT u.id, u.uid
FROM "grafana"."user" as u
WHERE u.uid = 'some_uid'

@ -0,0 +1,7 @@
SELECT u.id, u.uid
FROM {{ .Ident .UserTable }} as u
{{ if .Query.UserUID }}
WHERE u.uid = {{ .Arg .Query.UserUID }}
{{ else }}
WHERE u.id = {{ .Arg .Query.UserID }}
{{ end }}

@ -13,6 +13,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/authz/mappers"
@ -25,7 +26,7 @@ import (
)
func RegisterRBACAuthZService(handler grpcserver.Provider, db legacysql.LegacyDatabaseProvider, tracer tracing.Tracer) {
server := rbac.NewService(db, log.New("authz-grpc-server"), tracer)
server := rbac.NewService(db, legacy.NewLegacySQLStores(db), log.New("authz-grpc-server"), tracer)
srv := handler.GetServer()
authzv1.RegisterAuthzServiceServer(srv, server)

Loading…
Cancel
Save