mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Auth: Implement requester interface in access control module (#74289)
* Implement requester interface in the access control module
This commit is contained in:
parent
9310bb632e
commit
13f4382214
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
})
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
})
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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:%'")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user