Auth: Implement requester interface in access control module (#74289)

* Implement requester interface in the access control module
This commit is contained in:
linoman 2023-09-06 11:16:10 +02:00 committed by GitHub
parent 9310bb632e
commit 13f4382214
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 179 additions and 114 deletions

View File

@ -33,6 +33,7 @@ import (
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
"github.com/grafana/grafana/pkg/services/auth/identity"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/database"
@ -45,6 +46,7 @@ import (
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/libraryelements/model"
"github.com/grafana/grafana/pkg/services/librarypanels"
"github.com/grafana/grafana/pkg/services/live"
"github.com/grafana/grafana/pkg/services/org"
pref "github.com/grafana/grafana/pkg/services/preference"
@ -1138,23 +1140,25 @@ func (s mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboard
type mockLibraryPanelService struct {
}
func (m *mockLibraryPanelService) ConnectLibraryPanelsForDashboard(c context.Context, signedInUser *user.SignedInUser, dash *dashboards.Dashboard) error {
var _ librarypanels.Service = (*mockLibraryPanelService)(nil)
func (m *mockLibraryPanelService) ConnectLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error {
return nil
}
func (m *mockLibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser *user.SignedInUser, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
func (m *mockLibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
return nil
}
type mockLibraryElementService struct {
}
func (l *mockLibraryElementService) CreateElement(c context.Context, signedInUser *user.SignedInUser, cmd model.CreateLibraryElementCommand) (model.LibraryElementDTO, error) {
func (l *mockLibraryElementService) CreateElement(c context.Context, signedInUser identity.Requester, cmd model.CreateLibraryElementCommand) (model.LibraryElementDTO, error) {
return model.LibraryElementDTO{}, nil
}
// GetElement gets an element from a UID.
func (l *mockLibraryElementService) GetElement(c context.Context, signedInUser *user.SignedInUser, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error) {
func (l *mockLibraryElementService) GetElement(c context.Context, signedInUser identity.Requester, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error) {
return model.LibraryElementDTO{}, nil
}
@ -1164,7 +1168,7 @@ func (l *mockLibraryElementService) GetElementsForDashboard(c context.Context, d
}
// ConnectElementsToDashboard connects elements to a specific dashboard.
func (l *mockLibraryElementService) ConnectElementsToDashboard(c context.Context, signedInUser *user.SignedInUser, elementUIDs []string, dashboardID int64) error {
func (l *mockLibraryElementService) ConnectElementsToDashboard(c context.Context, signedInUser identity.Requester, elementUIDs []string, dashboardID int64) error {
return nil
}
@ -1174,6 +1178,6 @@ func (l *mockLibraryElementService) DisconnectElementsFromDashboard(c context.Co
}
// DeleteLibraryElementsInFolder deletes all elements for a specific folder.
func (l *mockLibraryElementService) DeleteLibraryElementsInFolder(c context.Context, signedInUser *user.SignedInUser, folderUID string) error {
func (l *mockLibraryElementService) DeleteLibraryElementsInFolder(c context.Context, signedInUser identity.Requester, folderUID string) error {
return nil
}

View File

@ -3,11 +3,11 @@ package db
import (
"bytes"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
)
@ -63,7 +63,7 @@ func (sb *SQLBuilder) AddParams(params ...any) {
sb.params = append(sb.params, params...)
}
func (sb *SQLBuilder) WriteDashboardPermissionFilter(user *user.SignedInUser, permission dashboards.PermissionType, queryType string) {
func (sb *SQLBuilder) WriteDashboardPermissionFilter(user identity.Requester, permission dashboards.PermissionType, queryType string) {
var (
sql string
params []any

View File

@ -377,7 +377,7 @@ func GetOrgRoles(user identity.Requester) []string {
return roles
}
func BackgroundUser(name string, orgID int64, role org.RoleType, permissions []Permission) *user.SignedInUser {
func BackgroundUser(name string, orgID int64, role org.RoleType, permissions []Permission) identity.Requester {
return &user.SignedInUser{
OrgID: orgID,
OrgRole: role,

View File

@ -44,16 +44,16 @@ type Mock struct {
Calls Calls
// Override functions
EvaluateFunc func(context.Context, *user.SignedInUser, accesscontrol.Evaluator) (bool, error)
GetUserPermissionsFunc func(context.Context, *user.SignedInUser, accesscontrol.Options) ([]accesscontrol.Permission, error)
ClearUserPermissionCacheFunc func(*user.SignedInUser)
EvaluateFunc func(context.Context, identity.Requester, accesscontrol.Evaluator) (bool, error)
GetUserPermissionsFunc func(context.Context, identity.Requester, accesscontrol.Options) ([]accesscontrol.Permission, error)
ClearUserPermissionCacheFunc func(identity.Requester)
DeclareFixedRolesFunc func(...accesscontrol.RoleRegistration) error
DeclarePluginRolesFunc func(context.Context, string, string, []plugins.RoleRegistration) error
GetUserBuiltInRolesFunc func(user *user.SignedInUser) []string
GetUserBuiltInRolesFunc func(user identity.Requester) []string
RegisterFixedRolesFunc func() error
RegisterScopeAttributeResolverFunc func(string, accesscontrol.ScopeAttributeResolver)
DeleteUserPermissionsFunc func(context.Context, int64) error
SearchUsersPermissionsFunc func(context.Context, *user.SignedInUser, int64, accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error)
SearchUsersPermissionsFunc func(context.Context, identity.Requester, int64, accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error)
SearchUserPermissionsFunc func(ctx context.Context, orgID int64, searchOptions accesscontrol.SearchOptions) ([]accesscontrol.Permission, error)
SaveExternalServiceRoleFunc func(ctx context.Context, cmd accesscontrol.SaveExternalServiceRoleCommand) error
DeleteExternalServiceRoleFunc func(ctx context.Context, externalServiceID string) error
@ -92,8 +92,7 @@ func (m *Mock) WithBuiltInRoles(builtInRoles []string) *Mock {
// Evaluate evaluates access to the given resource.
// This mock uses GetUserPermissions to then call the evaluator Evaluate function.
func (m *Mock) Evaluate(ctx context.Context, us identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) {
usr := us.(*user.SignedInUser)
func (m *Mock) Evaluate(ctx context.Context, usr identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) {
m.Calls.Evaluate = append(m.Calls.Evaluate, []interface{}{ctx, usr, evaluator})
// Use override if provided
if m.EvaluateFunc != nil {
@ -101,11 +100,9 @@ func (m *Mock) Evaluate(ctx context.Context, us identity.Requester, evaluator ac
}
var permissions map[string][]string
if usr.Permissions != nil && usr.Permissions[usr.OrgID] != nil {
permissions = usr.Permissions[usr.OrgID]
}
permissions = usr.GetPermissions()
if permissions == nil {
if len(permissions) == 0 {
userPermissions, err := m.GetUserPermissions(ctx, usr, accesscontrol.Options{ReloadCache: true})
if err != nil {
return false, err
@ -117,7 +114,7 @@ func (m *Mock) Evaluate(ctx context.Context, us identity.Requester, evaluator ac
return true, nil
}
resolvedEvaluator, err := evaluator.MutateScopes(ctx, m.scopeResolvers.GetScopeAttributeMutator(usr.OrgID))
resolvedEvaluator, err := evaluator.MutateScopes(ctx, m.scopeResolvers.GetScopeAttributeMutator(usr.GetOrgID()))
if err != nil {
if errors.Is(err, accesscontrol.ErrResolverNotFound) {
return false, nil
@ -130,8 +127,7 @@ func (m *Mock) Evaluate(ctx context.Context, us identity.Requester, evaluator ac
// GetUserPermissions returns user permissions.
// This mock return m.permissions unless an override is provided.
func (m *Mock) GetUserPermissions(ctx context.Context, usr identity.Requester, opts accesscontrol.Options) ([]accesscontrol.Permission, error) {
user := usr.(*user.SignedInUser)
func (m *Mock) GetUserPermissions(ctx context.Context, user identity.Requester, opts accesscontrol.Options) ([]accesscontrol.Permission, error) {
m.Calls.GetUserPermissions = append(m.Calls.GetUserPermissions, []interface{}{ctx, user, opts})
// Use override if provided
if m.GetUserPermissionsFunc != nil {
@ -141,8 +137,7 @@ func (m *Mock) GetUserPermissions(ctx context.Context, usr identity.Requester, o
return m.permissions, nil
}
func (m *Mock) ClearUserPermissionCache(usr identity.Requester) {
user := usr.(*user.SignedInUser)
func (m *Mock) ClearUserPermissionCache(user identity.Requester) {
m.Calls.ClearUserPermissionCache = append(m.Calls.ClearUserPermissionCache, []interface{}{user})
// Use override if provided
if m.ClearUserPermissionCacheFunc != nil {

View File

@ -60,6 +60,8 @@ type Requester interface {
GetCacheKey() (string, error)
// HasUniqueId returns true if the entity has a unique id
HasUniqueId() bool
// AuthenticatedBy returns the authentication method used to authenticate the entity.
GetAuthenticatedBy() string
}
// IntIdentifier converts a string identifier to an int64.

View File

@ -7,8 +7,8 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -51,7 +51,7 @@ func TestPermissionsSync_SyncPermission(t *testing.T) {
func setupTestEnv() *PermissionsSync {
acMock := &acmock.Mock{
GetUserPermissionsFunc: func(ctx context.Context, siu *user.SignedInUser, o accesscontrol.Options) ([]accesscontrol.Permission, error) {
GetUserPermissionsFunc: func(ctx context.Context, siu identity.Requester, o accesscontrol.Options) ([]accesscontrol.Permission, error) {
return []accesscontrol.Permission{
{Action: accesscontrol.ActionUsersRead},
}, nil

View File

@ -4,7 +4,7 @@ import (
"context"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/auth/identity"
)
// ImportDashboardInput definition of input parameters when importing a dashboard.
@ -25,7 +25,7 @@ type ImportDashboardRequest struct {
FolderId int64 `json:"folderId"`
FolderUid string `json:"folderUid"`
User *user.SignedInUser `json:"-"`
User identity.Requester `json:"-"`
}
// ImportDashboardResponse response object returned when importing a dashboard.

View File

@ -7,6 +7,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/dashboardimport"
"github.com/grafana/grafana/pkg/services/dashboardimport/api"
"github.com/grafana/grafana/pkg/services/dashboardimport/utils"
@ -85,7 +86,7 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
// here we need to get FolderId from FolderUID if it present in the request, if both exist, FolderUID would overwrite FolderID
if req.FolderUid != "" {
folder, err := s.folderService.Get(ctx, &folder.GetFolderQuery{
OrgID: req.User.OrgID,
OrgID: req.User.GetOrgID(),
UID: &req.FolderUid,
SignedInUser: req.User,
})
@ -96,7 +97,7 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
} else {
folder, err := s.folderService.Get(ctx, &folder.GetFolderQuery{
ID: &req.FolderId,
OrgID: req.User.OrgID,
OrgID: req.User.GetOrgID(),
SignedInUser: req.User,
})
if err != nil {
@ -105,10 +106,18 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
req.FolderUid = folder.UID
}
namespaceID, identifier := req.User.GetNamespacedID()
userID := int64(0)
switch namespaceID {
case identity.NamespaceUser, identity.NamespaceServiceAccount:
userID, _ = identity.IntIdentifier(namespaceID, identifier)
}
saveCmd := dashboards.SaveDashboardCommand{
Dashboard: generatedDash,
OrgID: req.User.OrgID,
UserID: req.User.UserID,
OrgID: req.User.GetOrgID(),
UserID: userID,
Overwrite: req.Overwrite,
PluginID: req.PluginId,
FolderID: req.FolderId,

View File

@ -47,11 +47,11 @@ func TestImportDashboardService(t *testing.T) {
importLibraryPanelsForDashboard := false
connectLibraryPanelsForDashboardCalled := false
libraryPanelService := &libraryPanelServiceMock{
importLibraryPanelsForDashboardFunc: func(ctx context.Context, signedInUser *user.SignedInUser, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
importLibraryPanelsForDashboardFunc: func(ctx context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
importLibraryPanelsForDashboard = true
return nil
},
connectLibraryPanelsForDashboardFunc: func(ctx context.Context, signedInUser *user.SignedInUser, dash *dashboards.Dashboard) error {
connectLibraryPanelsForDashboardFunc: func(ctx context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error {
connectLibraryPanelsForDashboardCalled = true
return nil
},
@ -211,11 +211,13 @@ func (s *dashboardServiceMock) ImportDashboard(ctx context.Context, dto *dashboa
type libraryPanelServiceMock struct {
librarypanels.Service
connectLibraryPanelsForDashboardFunc func(c context.Context, signedInUser *user.SignedInUser, dash *dashboards.Dashboard) error
importLibraryPanelsForDashboardFunc func(c context.Context, signedInUser *user.SignedInUser, libraryPanels *simplejson.Json, panels []any, folderID int64) error
connectLibraryPanelsForDashboardFunc func(c context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error
importLibraryPanelsForDashboardFunc func(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error
}
func (s *libraryPanelServiceMock) ConnectLibraryPanelsForDashboard(ctx context.Context, signedInUser *user.SignedInUser, dash *dashboards.Dashboard) error {
var _ librarypanels.Service = (*libraryPanelServiceMock)(nil)
func (s *libraryPanelServiceMock) ConnectLibraryPanelsForDashboard(ctx context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error {
if s.connectLibraryPanelsForDashboardFunc != nil {
return s.connectLibraryPanelsForDashboardFunc(ctx, signedInUser, dash)
}
@ -223,7 +225,7 @@ func (s *libraryPanelServiceMock) ConnectLibraryPanelsForDashboard(ctx context.C
return nil
}
func (s *libraryPanelServiceMock) ImportLibraryPanelsForDashboard(ctx context.Context, signedInUser *user.SignedInUser, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
func (s *libraryPanelServiceMock) ImportLibraryPanelsForDashboard(ctx context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
if s.importLibraryPanelsForDashboardFunc != nil {
return s.importLibraryPanelsForDashboardFunc(ctx, signedInUser, libraryPanels, panels, folderID)
}

View File

@ -254,7 +254,7 @@ func (s *Service) getFolderByTitle(ctx context.Context, orgID int64, title strin
func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
logger := s.log.FromContext(ctx)
if cmd.SignedInUser == nil {
if cmd.SignedInUser == nil || cmd.SignedInUser.IsNil() {
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
}

View File

@ -138,8 +138,7 @@ type GetFolderQuery struct {
Title *string
OrgID int64
SignedInUser *user.SignedInUser `json:"-"`
Requester identity.Requester `json:"-"`
SignedInUser identity.Requester `json:"-"`
}
// GetParentsQuery captures the information required by the folder service to

View File

@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/kinds/librarypanel"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/libraryelements/model"
@ -106,7 +107,7 @@ func getLibraryElement(dialect migrator.Dialect, session *db.Session, uid string
}
// createLibraryElement adds a library element.
func (l *LibraryElementService) createLibraryElement(c context.Context, signedInUser *user.SignedInUser, cmd model.CreateLibraryElementCommand) (model.LibraryElementDTO, error) {
func (l *LibraryElementService) createLibraryElement(c context.Context, signedInUser identity.Requester, cmd model.CreateLibraryElementCommand) (model.LibraryElementDTO, error) {
if err := l.requireSupportedElementKind(cmd.Kind); err != nil {
return model.LibraryElementDTO{}, err
}
@ -130,8 +131,18 @@ func (l *LibraryElementService) createLibraryElement(c context.Context, signedIn
}
}
userID := int64(0)
namespaceID, identifier := signedInUser.GetNamespacedID()
switch namespaceID {
case identity.NamespaceUser, identity.NamespaceServiceAccount:
userID, err = identity.IntIdentifier(namespaceID, identifier)
if err != nil {
l.log.Warn("Error while parsing userID", "namespaceID", namespaceID, "userID", identifier)
}
}
element := model.LibraryElement{
OrgID: signedInUser.OrgID,
OrgID: signedInUser.GetOrgID(),
FolderID: cmd.FolderID,
UID: createUID,
Name: cmd.Name,
@ -142,8 +153,8 @@ func (l *LibraryElementService) createLibraryElement(c context.Context, signedIn
Created: time.Now(),
Updated: time.Now(),
CreatedBy: signedInUser.UserID,
UpdatedBy: signedInUser.UserID,
CreatedBy: userID,
UpdatedBy: userID,
}
if err := syncFieldsWithModel(&element); err != nil {
@ -180,13 +191,13 @@ func (l *LibraryElementService) createLibraryElement(c context.Context, signedIn
Updated: element.Updated,
CreatedBy: librarypanel.LibraryElementDTOMetaUser{
Id: element.CreatedBy,
Name: signedInUser.Login,
AvatarUrl: dtos.GetGravatarUrl(signedInUser.Email),
Name: signedInUser.GetLogin(),
AvatarUrl: dtos.GetGravatarUrl(signedInUser.GetEmail()),
},
UpdatedBy: librarypanel.LibraryElementDTOMetaUser{
Id: element.UpdatedBy,
Name: signedInUser.Login,
AvatarUrl: dtos.GetGravatarUrl(signedInUser.Email),
Name: signedInUser.GetLogin(),
AvatarUrl: dtos.GetGravatarUrl(signedInUser.GetEmail()),
},
},
}
@ -238,7 +249,7 @@ func (l *LibraryElementService) deleteLibraryElement(c context.Context, signedIn
}
// getLibraryElements gets a Library Element where param == value
func (l *LibraryElementService) getLibraryElements(c context.Context, store db.DB, cfg *setting.Cfg, signedInUser *user.SignedInUser, params []Pair, features featuremgmt.FeatureToggles, cmd model.GetLibraryElementCommand) ([]model.LibraryElementDTO, error) {
func (l *LibraryElementService) getLibraryElements(c context.Context, store db.DB, cfg *setting.Cfg, signedInUser identity.Requester, params []Pair, features featuremgmt.FeatureToggles, cmd model.GetLibraryElementCommand) ([]model.LibraryElementDTO, error) {
libraryElements := make([]model.LibraryElementWithMeta, 0)
recursiveQueriesAreSupported, err := store.RecursiveQueriesAreSupported()
@ -321,8 +332,8 @@ func (l *LibraryElementService) getLibraryElements(c context.Context, store db.D
}
// getLibraryElementByUid gets a Library Element by uid.
func (l *LibraryElementService) getLibraryElementByUid(c context.Context, signedInUser *user.SignedInUser, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error) {
libraryElements, err := l.getLibraryElements(c, l.SQLStore, l.Cfg, signedInUser, []Pair{{key: "org_id", value: signedInUser.OrgID}, {key: "uid", value: cmd.UID}}, l.features, cmd)
func (l *LibraryElementService) getLibraryElementByUid(c context.Context, signedInUser identity.Requester, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error) {
libraryElements, err := l.getLibraryElements(c, l.SQLStore, l.Cfg, signedInUser, []Pair{{key: "org_id", value: signedInUser.GetOrgID()}, {key: "uid", value: cmd.UID}}, l.features, cmd)
if err != nil {
return model.LibraryElementDTO{}, err
}
@ -334,15 +345,15 @@ func (l *LibraryElementService) getLibraryElementByUid(c context.Context, signed
}
// getLibraryElementByName gets a Library Element by name.
func (l *LibraryElementService) getLibraryElementsByName(c context.Context, signedInUser *user.SignedInUser, name string) ([]model.LibraryElementDTO, error) {
return l.getLibraryElements(c, l.SQLStore, l.Cfg, signedInUser, []Pair{{"org_id", signedInUser.OrgID}, {"name", name}}, l.features,
func (l *LibraryElementService) getLibraryElementsByName(c context.Context, signedInUser identity.Requester, name string) ([]model.LibraryElementDTO, error) {
return l.getLibraryElements(c, l.SQLStore, l.Cfg, signedInUser, []Pair{{"org_id", signedInUser.GetOrgID()}, {"name", name}}, l.features,
model.GetLibraryElementCommand{
FolderName: dashboards.RootFolderName,
})
}
// getAllLibraryElements gets all Library Elements.
func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedInUser *user.SignedInUser, query model.SearchLibraryElementsQuery) (model.LibraryElementSearchResult, error) {
func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedInUser identity.Requester, query model.SearchLibraryElementsQuery) (model.LibraryElementSearchResult, error) {
elements := make([]model.LibraryElementWithMeta, 0)
result := model.LibraryElementSearchResult{}
@ -372,7 +383,7 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
builder.Write(", 'General' as folder_name ")
builder.Write(", '' as folder_uid ")
builder.Write(getFromLibraryElementDTOWithMeta(l.SQLStore.GetDialect()))
builder.Write(` WHERE le.org_id=? AND le.folder_id=0`, signedInUser.OrgID)
builder.Write(` WHERE le.org_id=? AND le.folder_id=0`, signedInUser.GetOrgID())
writeKindSQL(query, &builder)
writeSearchStringSQL(query, l.SQLStore, &builder)
writeExcludeSQL(query, &builder)
@ -384,7 +395,7 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
builder.Write(", dashboard.uid as folder_uid ")
builder.Write(getFromLibraryElementDTOWithMeta(l.SQLStore.GetDialect()))
builder.Write(" INNER JOIN dashboard AS dashboard on le.folder_id = dashboard.id AND le.folder_id<>0")
builder.Write(` WHERE le.org_id=?`, signedInUser.OrgID)
builder.Write(` WHERE le.org_id=?`, signedInUser.GetOrgID())
writeKindSQL(query, &builder)
writeSearchStringSQL(query, l.SQLStore, &builder)
writeExcludeSQL(query, &builder)
@ -392,7 +403,7 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
if err := folderFilter.writeFolderFilterSQL(false, &builder); err != nil {
return err
}
if signedInUser.OrgRole != org.RoleAdmin {
if !signedInUser.HasRole(org.RoleAdmin) {
builder.WriteDashboardPermissionFilter(signedInUser, dashboards.PERMISSION_VIEW, "")
}
if query.SortDirection == search.SortAlphaDesc.Name {
@ -444,7 +455,7 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
if folderFilter.includeGeneralFolder {
countBuilder.Write(selectLibraryElementDTOWithMeta)
countBuilder.Write(getFromLibraryElementDTOWithMeta(l.SQLStore.GetDialect()))
countBuilder.Write(` WHERE le.org_id=? AND le.folder_id=0`, signedInUser.OrgID)
countBuilder.Write(` WHERE le.org_id=? AND le.folder_id=0`, signedInUser.GetOrgID())
writeKindSQL(query, &countBuilder)
writeSearchStringSQL(query, l.SQLStore, &countBuilder)
writeExcludeSQL(query, &countBuilder)
@ -454,7 +465,7 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
countBuilder.Write(selectLibraryElementDTOWithMeta)
countBuilder.Write(getFromLibraryElementDTOWithMeta(l.SQLStore.GetDialect()))
countBuilder.Write(" INNER JOIN dashboard AS dashboard on le.folder_id = dashboard.id and le.folder_id<>0")
countBuilder.Write(` WHERE le.org_id=?`, signedInUser.OrgID)
countBuilder.Write(` WHERE le.org_id=?`, signedInUser.GetOrgID())
writeKindSQL(query, &countBuilder)
writeSearchStringSQL(query, l.SQLStore, &countBuilder)
writeExcludeSQL(query, &countBuilder)
@ -708,14 +719,14 @@ func (l *LibraryElementService) getElementsForDashboardID(c context.Context, das
}
// connectElementsToDashboardID adds connections for all elements Library Elements in a Dashboard.
func (l *LibraryElementService) connectElementsToDashboardID(c context.Context, signedInUser *user.SignedInUser, elementUIDs []string, dashboardID int64) error {
func (l *LibraryElementService) connectElementsToDashboardID(c context.Context, signedInUser identity.Requester, elementUIDs []string, dashboardID int64) error {
err := l.SQLStore.WithTransactionalDbSession(c, func(session *db.Session) error {
_, err := session.Exec("DELETE FROM "+model.LibraryElementConnectionTableName+" WHERE kind=1 AND connection_id=?", dashboardID)
if err != nil {
return err
}
for _, elementUID := range elementUIDs {
element, err := getLibraryElement(l.SQLStore.GetDialect(), session, elementUID, signedInUser.OrgID)
element, err := getLibraryElement(l.SQLStore.GetDialect(), session, elementUID, signedInUser.GetOrgID())
if err != nil {
return err
}
@ -723,12 +734,22 @@ func (l *LibraryElementService) connectElementsToDashboardID(c context.Context,
return err
}
namespaceID, identifier := signedInUser.GetNamespacedID()
userID := int64(0)
switch namespaceID {
case identity.NamespaceUser, identity.NamespaceServiceAccount:
userID, err = identity.IntIdentifier(namespaceID, identifier)
if err != nil {
l.log.Warn("Failed to parse user ID from namespace identifier", "namespace", namespaceID, "identifier", identifier, "error", err)
}
}
connection := model.LibraryElementConnection{
ElementID: element.ID,
Kind: 1,
ConnectionID: dashboardID,
Created: time.Now(),
CreatedBy: signedInUser.UserID,
CreatedBy: userID,
}
if _, err := session.Insert(&connection); err != nil {
if l.SQLStore.GetDialect().IsUniqueConstraintViolation(err) {
@ -755,12 +776,12 @@ func (l *LibraryElementService) disconnectElementsFromDashboardID(c context.Cont
}
// deleteLibraryElementsInFolderUID deletes all Library Elements in a folder.
func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c context.Context, signedInUser *user.SignedInUser, folderUID string) error {
func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c context.Context, signedInUser identity.Requester, folderUID string) error {
return l.SQLStore.WithTransactionalDbSession(c, func(session *db.Session) error {
var folderUIDs []struct {
ID int64 `xorm:"id"`
}
err := session.SQL("SELECT id from dashboard WHERE uid=? AND org_id=? AND is_folder=?", folderUID, signedInUser.OrgID, l.SQLStore.GetDialect().BooleanStr(true)).Find(&folderUIDs)
err := session.SQL("SELECT id from dashboard WHERE uid=? AND org_id=? AND is_folder=?", folderUID, signedInUser.GetOrgID(), l.SQLStore.GetDialect().BooleanStr(true)).Find(&folderUIDs)
if err != nil {
return err
}
@ -784,7 +805,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c context.Conte
sql := "SELECT lec.connection_id FROM library_element AS le"
sql += " INNER JOIN " + model.LibraryElementConnectionTableName + " AS lec on le.id = lec.element_id"
sql += " WHERE le.folder_id=? AND le.org_id=?"
err = session.SQL(sql, folderID, signedInUser.OrgID).Find(&connectionIDs)
err = session.SQL(sql, folderID, signedInUser.GetOrgID()).Find(&connectionIDs)
if err != nil {
return err
}
@ -795,7 +816,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c context.Conte
var elementIDs []struct {
ID int64 `xorm:"id"`
}
err = session.SQL("SELECT id from library_element WHERE folder_id=? AND org_id=?", folderID, signedInUser.OrgID).Find(&elementIDs)
err = session.SQL("SELECT id from library_element WHERE folder_id=? AND org_id=?", folderID, signedInUser.GetOrgID()).Find(&elementIDs)
if err != nil {
return err
}
@ -805,7 +826,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c context.Conte
return err
}
}
if _, err := session.Exec("DELETE FROM library_element WHERE folder_id=? AND org_id=?", folderID, signedInUser.OrgID); err != nil {
if _, err := session.Exec("DELETE FROM library_element WHERE folder_id=? AND org_id=?", folderID, signedInUser.GetOrgID()); err != nil {
return err
}

View File

@ -4,11 +4,11 @@ import (
"context"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/libraryelements/model"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/user"
)
func isGeneralFolder(folderID int64) bool {
@ -31,7 +31,7 @@ func (l *LibraryElementService) requireSupportedElementKind(kindAsInt int64) err
}
}
func (l *LibraryElementService) requireEditPermissionsOnFolder(ctx context.Context, user *user.SignedInUser, folderID int64) error {
func (l *LibraryElementService) requireEditPermissionsOnFolder(ctx context.Context, user identity.Requester, folderID int64) error {
// TODO remove these special cases and handle General folder case in access control guardian
if isGeneralFolder(folderID) && user.HasRole(org.RoleEditor) {
return nil
@ -41,7 +41,7 @@ func (l *LibraryElementService) requireEditPermissionsOnFolder(ctx context.Conte
return dashboards.ErrFolderAccessDenied
}
g, err := guardian.New(ctx, folderID, user.OrgID, user)
g, err := guardian.New(ctx, folderID, user.GetOrgID(), user)
if err != nil {
return err
}
@ -57,12 +57,12 @@ func (l *LibraryElementService) requireEditPermissionsOnFolder(ctx context.Conte
return nil
}
func (l *LibraryElementService) requireViewPermissionsOnFolder(ctx context.Context, user *user.SignedInUser, folderID int64) error {
func (l *LibraryElementService) requireViewPermissionsOnFolder(ctx context.Context, user identity.Requester, folderID int64) error {
if isGeneralFolder(folderID) && user.HasRole(org.RoleViewer) {
return nil
}
g, err := guardian.New(ctx, folderID, user.OrgID, user)
g, err := guardian.New(ctx, folderID, user.GetOrgID(), user)
if err != nil {
return err
}

View File

@ -7,10 +7,10 @@ import (
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/libraryelements/model"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
)
@ -29,12 +29,12 @@ func ProvideService(cfg *setting.Cfg, sqlStore db.DB, routeRegister routing.Rout
// Service is a service for operating on library elements.
type Service interface {
CreateElement(c context.Context, signedInUser *user.SignedInUser, cmd model.CreateLibraryElementCommand) (model.LibraryElementDTO, error)
GetElement(c context.Context, signedInUser *user.SignedInUser, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error)
CreateElement(c context.Context, signedInUser identity.Requester, cmd model.CreateLibraryElementCommand) (model.LibraryElementDTO, error)
GetElement(c context.Context, signedInUser identity.Requester, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error)
GetElementsForDashboard(c context.Context, dashboardID int64) (map[string]model.LibraryElementDTO, error)
ConnectElementsToDashboard(c context.Context, signedInUser *user.SignedInUser, elementUIDs []string, dashboardID int64) error
ConnectElementsToDashboard(c context.Context, signedInUser identity.Requester, elementUIDs []string, dashboardID int64) error
DisconnectElementsFromDashboard(c context.Context, dashboardID int64) error
DeleteLibraryElementsInFolder(c context.Context, signedInUser *user.SignedInUser, folderUID string) error
DeleteLibraryElementsInFolder(c context.Context, signedInUser identity.Requester, folderUID string) error
}
// LibraryElementService is the service for the Library Element feature.
@ -47,13 +47,15 @@ type LibraryElementService struct {
features featuremgmt.FeatureToggles
}
var _ Service = (*LibraryElementService)(nil)
// CreateElement creates a Library Element.
func (l *LibraryElementService) CreateElement(c context.Context, signedInUser *user.SignedInUser, cmd model.CreateLibraryElementCommand) (model.LibraryElementDTO, error) {
func (l *LibraryElementService) CreateElement(c context.Context, signedInUser identity.Requester, cmd model.CreateLibraryElementCommand) (model.LibraryElementDTO, error) {
return l.createLibraryElement(c, signedInUser, cmd)
}
// GetElement gets an element from a UID.
func (l *LibraryElementService) GetElement(c context.Context, signedInUser *user.SignedInUser, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error) {
func (l *LibraryElementService) GetElement(c context.Context, signedInUser identity.Requester, cmd model.GetLibraryElementCommand) (model.LibraryElementDTO, error) {
return l.getLibraryElementByUid(c, signedInUser, cmd)
}
@ -63,7 +65,7 @@ func (l *LibraryElementService) GetElementsForDashboard(c context.Context, dashb
}
// ConnectElementsToDashboard connects elements to a specific dashboard.
func (l *LibraryElementService) ConnectElementsToDashboard(c context.Context, signedInUser *user.SignedInUser, elementUIDs []string, dashboardID int64) error {
func (l *LibraryElementService) ConnectElementsToDashboard(c context.Context, signedInUser identity.Requester, elementUIDs []string, dashboardID int64) error {
return l.connectElementsToDashboardID(c, signedInUser, elementUIDs, dashboardID)
}
@ -73,7 +75,7 @@ func (l *LibraryElementService) DisconnectElementsFromDashboard(c context.Contex
}
// DeleteLibraryElementsInFolder deletes all elements for a specific folder.
func (l *LibraryElementService) DeleteLibraryElementsInFolder(c context.Context, signedInUser *user.SignedInUser, folderUID string) error {
func (l *LibraryElementService) DeleteLibraryElementsInFolder(c context.Context, signedInUser identity.Requester, folderUID string) error {
return l.deleteLibraryElementsInFolderUID(c, signedInUser, folderUID)
}

View File

@ -18,6 +18,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/appcontext"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/kinds/librarypanel"
"github.com/grafana/grafana/pkg/services/accesscontrol"
@ -268,6 +269,7 @@ type scenarioContext struct {
folder *folder.Folder
initialResult libraryElementResult
sqlStore db.DB
log log.Logger
}
func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash *dashboards.Dashboard, folderID int64) *dashboards.Dashboard {
@ -389,6 +391,7 @@ func scenarioWithPanel(t *testing.T, desc string, fn func(t *testing.T, sc scena
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
sc.initialResult = validateAndUnMarshalResponse(t, resp)
sc.log = log.New("libraryelements-test")
fn(t, sc)
})

View File

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/libraryelements"
@ -38,8 +39,8 @@ func ProvideService(cfg *setting.Cfg, sqlStore db.DB, routeRegister routing.Rout
// Service is a service for operating on library panels.
type Service interface {
ConnectLibraryPanelsForDashboard(c context.Context, signedInUser *user.SignedInUser, dash *dashboards.Dashboard) error
ImportLibraryPanelsForDashboard(c context.Context, signedInUser *user.SignedInUser, libraryPanels *simplejson.Json, panels []any, folderID int64) error
ConnectLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error
ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error
}
type LibraryInfo struct {
@ -57,8 +58,10 @@ type LibraryPanelService struct {
log log.Logger
}
var _ Service = (*LibraryPanelService)(nil)
// ConnectLibraryPanelsForDashboard loops through all panels in dashboard JSON and connects any library panels to the dashboard.
func (lps *LibraryPanelService) ConnectLibraryPanelsForDashboard(c context.Context, signedInUser *user.SignedInUser, dash *dashboards.Dashboard) error {
func (lps *LibraryPanelService) ConnectLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error {
panels := dash.Data.Get("panels").MustArray()
libraryPanels := make(map[string]string)
err := connectLibraryPanelsRecursively(c, panels, libraryPanels)
@ -112,11 +115,11 @@ func connectLibraryPanelsRecursively(c context.Context, panels []any, libraryPan
}
// ImportLibraryPanelsForDashboard loops through all panels in dashboard JSON and creates any missing library panels in the database.
func (lps *LibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser *user.SignedInUser, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
func (lps *LibraryPanelService) ImportLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
return importLibraryPanelsRecursively(c, lps.LibraryElementService, signedInUser, libraryPanels, panels, folderID)
}
func importLibraryPanelsRecursively(c context.Context, service libraryelements.Service, signedInUser *user.SignedInUser, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
func importLibraryPanelsRecursively(c context.Context, service libraryelements.Service, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64) error {
for _, panel := range panels {
panelAsJSON := simplejson.NewFromAny(panel)
libraryPanel := panelAsJSON.Get("libraryPanel")

View File

@ -192,8 +192,8 @@ func TestDashboardUpdater(t *testing.T) {
require.Len(t, ctx.importDashboardArgs, 1)
require.Equal(t, "test", ctx.importDashboardArgs[0].PluginId)
require.Equal(t, "updated.json", ctx.importDashboardArgs[0].Path)
require.Equal(t, int64(2), ctx.importDashboardArgs[0].User.OrgID)
require.Equal(t, org.RoleAdmin, ctx.importDashboardArgs[0].User.OrgRole)
require.Equal(t, int64(2), ctx.importDashboardArgs[0].User.GetOrgID())
require.Equal(t, org.RoleAdmin, ctx.importDashboardArgs[0].User.GetOrgRole())
require.Equal(t, int64(0), ctx.importDashboardArgs[0].FolderId)
require.True(t, ctx.importDashboardArgs[0].Overwrite)
})
@ -319,22 +319,22 @@ func TestDashboardUpdater(t *testing.T) {
require.Len(t, ctx.importDashboardArgs, 3)
require.Equal(t, "test", ctx.importDashboardArgs[0].PluginId)
require.Equal(t, "dashboard1.json", ctx.importDashboardArgs[0].Path)
require.Equal(t, int64(2), ctx.importDashboardArgs[0].User.OrgID)
require.Equal(t, org.RoleAdmin, ctx.importDashboardArgs[0].User.OrgRole)
require.Equal(t, int64(2), ctx.importDashboardArgs[0].User.GetOrgID())
require.Equal(t, org.RoleAdmin, ctx.importDashboardArgs[0].User.GetOrgRole())
require.Equal(t, int64(0), ctx.importDashboardArgs[0].FolderId)
require.True(t, ctx.importDashboardArgs[0].Overwrite)
require.Equal(t, "test", ctx.importDashboardArgs[1].PluginId)
require.Equal(t, "dashboard2.json", ctx.importDashboardArgs[1].Path)
require.Equal(t, int64(2), ctx.importDashboardArgs[1].User.OrgID)
require.Equal(t, org.RoleAdmin, ctx.importDashboardArgs[1].User.OrgRole)
require.Equal(t, int64(2), ctx.importDashboardArgs[1].User.GetOrgID())
require.Equal(t, org.RoleAdmin, ctx.importDashboardArgs[1].User.GetOrgRole())
require.Equal(t, int64(0), ctx.importDashboardArgs[1].FolderId)
require.True(t, ctx.importDashboardArgs[1].Overwrite)
require.Equal(t, "test", ctx.importDashboardArgs[2].PluginId)
require.Equal(t, "dashboard3.json", ctx.importDashboardArgs[2].Path)
require.Equal(t, int64(2), ctx.importDashboardArgs[2].User.OrgID)
require.Equal(t, org.RoleAdmin, ctx.importDashboardArgs[2].User.OrgRole)
require.Equal(t, int64(2), ctx.importDashboardArgs[2].User.GetOrgID())
require.Equal(t, org.RoleAdmin, ctx.importDashboardArgs[2].User.GetOrgRole())
require.Equal(t, int64(0), ctx.importDashboardArgs[2].FolderId)
require.True(t, ctx.importDashboardArgs[2].Overwrite)
})

View File

@ -6,12 +6,12 @@ import (
"strings"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/auth/identity"
"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/login"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/services/user"
)
// maximum possible capacity for recursive queries array: one query for folder and one for dashboard actions
@ -23,7 +23,7 @@ type clause struct {
}
type accessControlDashboardPermissionFilter struct {
user *user.SignedInUser
user identity.Requester
dashboardActions []string
folderActions []string
features featuremgmt.FeatureToggles
@ -46,7 +46,7 @@ type PermissionsFilter interface {
// NewAccessControlDashboardPermissionFilter creates a new AccessControlDashboardPermissionFilter that is configured with specific actions calculated based on the dashboards.PermissionType and query type
// The filter is configured to use the new permissions filter (without subqueries) if the feature flag is enabled
// The filter is configured to use the old permissions filter (with subqueries) if the feature flag is disabled
func NewAccessControlDashboardPermissionFilter(user *user.SignedInUser, permissionLevel dashboards.PermissionType, queryType string, features featuremgmt.FeatureToggles, recursiveQueriesAreSupported bool) PermissionsFilter {
func NewAccessControlDashboardPermissionFilter(user identity.Requester, permissionLevel dashboards.PermissionType, queryType string, features featuremgmt.FeatureToggles, recursiveQueriesAreSupported bool) PermissionsFilter {
needEdit := permissionLevel > dashboards.PERMISSION_VIEW
var folderActions []string
@ -111,14 +111,21 @@ func (f *accessControlDashboardPermissionFilter) Where() (string, []any) {
}
func (f *accessControlDashboardPermissionFilter) buildClauses() {
if f.user == nil || f.user.Permissions == nil || f.user.Permissions[f.user.OrgID] == nil {
if f.user == nil || f.user.IsNil() || len(f.user.GetPermissions()) == 0 {
f.where = clause{string: "(1 = 0)"}
return
}
dashWildcards := accesscontrol.WildcardsFromPrefix(dashboards.ScopeDashboardsPrefix)
folderWildcards := accesscontrol.WildcardsFromPrefix(dashboards.ScopeFoldersPrefix)
filter, params := accesscontrol.UserRolesFilter(f.user.OrgID, f.user.UserID, f.user.Teams, accesscontrol.GetOrgRoles(f.user))
userID := int64(0)
namespaceID, identifier := f.user.GetNamespacedID()
switch namespaceID {
case identity.NamespaceUser, identity.NamespaceServiceAccount:
userID, _ = identity.IntIdentifier(namespaceID, identifier)
}
filter, params := accesscontrol.UserRolesFilter(f.user.GetOrgID(), userID, f.user.GetTeams(), accesscontrol.GetOrgRoles(f.user))
rolesFilter := " AND role_id IN(SELECT id FROM role " + filter + ") "
var args []any
builder := strings.Builder{}
@ -129,10 +136,10 @@ func (f *accessControlDashboardPermissionFilter) buildClauses() {
// useSelfContainedPermissions is true if the user's permissions are stored and set from the JWT token
// currently it's used for the extended JWT module (when the user is authenticated via a JWT token generated by Grafana)
useSelfContainedPermissions := f.user.AuthenticatedBy == login.ExtendedJWTModule
useSelfContainedPermissions := f.user.GetAuthenticatedBy() == login.ExtendedJWTModule
if len(f.dashboardActions) > 0 {
toCheck := actionsToCheck(f.dashboardActions, f.user.Permissions[f.user.OrgID], dashWildcards, folderWildcards)
toCheck := actionsToCheck(f.dashboardActions, f.user.GetPermissions(), dashWildcards, folderWildcards)
if len(toCheck) > 0 {
if !useSelfContainedPermissions {
@ -236,7 +243,7 @@ func (f *accessControlDashboardPermissionFilter) buildClauses() {
builder.WriteString(" OR ")
}
toCheck := actionsToCheck(f.folderActions, f.user.Permissions[f.user.OrgID], folderWildcards)
toCheck := actionsToCheck(f.folderActions, f.user.GetPermissions(), folderWildcards)
if len(toCheck) > 0 {
if !useSelfContainedPermissions {
permSelector.WriteString("(SELECT substr(scope, 13) FROM permission WHERE scope LIKE 'folders:uid:%'")
@ -392,10 +399,10 @@ func parseStringSliceFromInterfaceSlice(slice []any) []string {
return result
}
func getAllowedUIDs(actions []string, user *user.SignedInUser, scopePrefix string) []any {
func getAllowedUIDs(actions []string, user identity.Requester, scopePrefix string) []any {
uidToActions := make(map[string]map[string]struct{})
for _, action := range actions {
for _, uidScope := range user.Permissions[user.OrgID][action] {
for _, uidScope := range user.GetPermissions()[action] {
if !strings.HasPrefix(uidScope, scopePrefix) {
continue
}

View File

@ -5,6 +5,7 @@ import (
"strings"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/auth/identity"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
@ -25,14 +26,21 @@ func (f *accessControlDashboardPermissionFilterNoFolderSubquery) LeftJoin() stri
}
func (f *accessControlDashboardPermissionFilterNoFolderSubquery) buildClauses() {
if f.user == nil || f.user.Permissions == nil || f.user.Permissions[f.user.OrgID] == nil {
if f.user == nil || f.user.IsNil() || len(f.user.GetPermissions()) == 0 {
f.where = clause{string: "(1 = 0)"}
return
}
dashWildcards := accesscontrol.WildcardsFromPrefix(dashboards.ScopeDashboardsPrefix)
folderWildcards := accesscontrol.WildcardsFromPrefix(dashboards.ScopeFoldersPrefix)
filter, params := accesscontrol.UserRolesFilter(f.user.OrgID, f.user.UserID, f.user.Teams, accesscontrol.GetOrgRoles(f.user))
userID := int64(0)
namespaceID, identifier := f.user.GetNamespacedID()
switch namespaceID {
case identity.NamespaceUser, identity.NamespaceServiceAccount:
userID, _ = identity.IntIdentifier(namespaceID, identifier)
}
filter, params := accesscontrol.UserRolesFilter(f.user.GetOrgID(), userID, f.user.GetTeams(), accesscontrol.GetOrgRoles(f.user))
rolesFilter := " AND role_id IN(SELECT id FROM role " + filter + ") "
var args []any
builder := strings.Builder{}
@ -43,10 +51,10 @@ func (f *accessControlDashboardPermissionFilterNoFolderSubquery) buildClauses()
// useSelfContainedPermissions is true if the user's permissions are stored and set from the JWT token
// currently it's used for the extended JWT module (when the user is authenticated via a JWT token generated by Grafana)
useSelfContainedPermissions := f.user.AuthenticatedBy == login.ExtendedJWTModule
useSelfContainedPermissions := f.user.GetAuthenticatedBy() == login.ExtendedJWTModule
if len(f.dashboardActions) > 0 {
toCheck := actionsToCheck(f.dashboardActions, f.user.Permissions[f.user.OrgID], dashWildcards, folderWildcards)
toCheck := actionsToCheck(f.dashboardActions, f.user.GetPermissions(), dashWildcards, folderWildcards)
if len(toCheck) > 0 {
if !useSelfContainedPermissions {
@ -152,7 +160,7 @@ func (f *accessControlDashboardPermissionFilterNoFolderSubquery) buildClauses()
builder.WriteString(" OR ")
}
toCheck := actionsToCheck(f.folderActions, f.user.Permissions[f.user.OrgID], folderWildcards)
toCheck := actionsToCheck(f.folderActions, f.user.GetPermissions(), folderWildcards)
if len(toCheck) > 0 {
if !useSelfContainedPermissions {
permSelector.WriteString("(SELECT substr(scope, 13) FROM permission WHERE scope LIKE 'folders:uid:%'")

View File

@ -46,11 +46,14 @@ func (u *SignedInUser) NameOrFallback() string {
// TODO: There's a need to remove this struct since it creates a circular dependency
// DEPRECATED: This function uses `UserDisplayDTO` model which we want to remove
// In order to retrieve the user URL, we need the dto library. However, adding
// the dto library to the user service creates a circular dependency
func (u *SignedInUser) ToUserDisplayDTO() *UserDisplayDTO {
return &UserDisplayDTO{
ID: u.UserID,
Login: u.Login,
Name: u.Name,
// AvatarURL: dtos.GetGravatarUrl(u.GetEmail()),
}
}
@ -200,3 +203,8 @@ func (u *SignedInUser) GetEmail() string {
func (u *SignedInUser) GetDisplayName() string {
return u.NameOrFallback()
}
// DEPRECATEAD: Returns the authentication method used
func (u *SignedInUser) GetAuthenticatedBy() string {
return u.AuthenticatedBy
}

View File

@ -5,6 +5,8 @@ import (
"fmt"
"strings"
"time"
"github.com/grafana/grafana/pkg/services/auth/identity"
)
type HelpFlags1 uint64
@ -104,7 +106,7 @@ type SetUsingOrgCommand struct {
}
type SearchUsersQuery struct {
SignedInUser *SignedInUser
SignedInUser identity.Requester
OrgID int64 `xorm:"org_id"`
Query string
Page int

View File

@ -111,9 +111,9 @@ func RequestWithWebContext(req *http.Request, c *contextmodel.ReqContext) *http.
return req
}
func RequestWithSignedInUser(req *http.Request, user *user.SignedInUser) *http.Request {
func RequestWithSignedInUser(req *http.Request, usr *user.SignedInUser) *http.Request {
return RequestWithWebContext(req, &contextmodel.ReqContext{
SignedInUser: user,
SignedInUser: usr,
IsSignedIn: true,
})
}