1package guardian
2
3import (
4	"context"
5	"errors"
6	"fmt"
7	"runtime"
8	"testing"
9
10	"github.com/grafana/grafana/pkg/bus"
11	"github.com/grafana/grafana/pkg/models"
12	"github.com/grafana/grafana/pkg/setting"
13
14	"github.com/stretchr/testify/require"
15)
16
17const (
18	orgID              = int64(1)
19	defaultDashboardID = int64(-1)
20	dashboardID        = int64(1)
21	parentFolderID     = int64(2)
22	childDashboardID   = int64(3)
23	userID             = int64(1)
24	otherUserID        = int64(2)
25	teamID             = int64(1)
26	otherTeamID        = int64(2)
27)
28
29var (
30	adminRole  = models.ROLE_ADMIN
31	editorRole = models.ROLE_EDITOR
32	viewerRole = models.ROLE_VIEWER
33)
34
35func TestGuardianAdmin(t *testing.T) {
36	orgRoleScenario("Given user has admin org role", t, models.ROLE_ADMIN, func(sc *scenarioContext) {
37		// dashboard has default permissions
38		sc.defaultPermissionScenario(USER, FULL_ACCESS)
39
40		// dashboard has user with permission
41		sc.dashboardPermissionScenario(USER, models.PERMISSION_ADMIN, FULL_ACCESS)
42		sc.dashboardPermissionScenario(USER, models.PERMISSION_EDIT, FULL_ACCESS)
43		sc.dashboardPermissionScenario(USER, models.PERMISSION_VIEW, FULL_ACCESS)
44
45		// dashboard has team with permission
46		sc.dashboardPermissionScenario(TEAM, models.PERMISSION_ADMIN, FULL_ACCESS)
47		sc.dashboardPermissionScenario(TEAM, models.PERMISSION_EDIT, FULL_ACCESS)
48		sc.dashboardPermissionScenario(TEAM, models.PERMISSION_VIEW, FULL_ACCESS)
49
50		// dashboard has editor role with permission
51		sc.dashboardPermissionScenario(EDITOR, models.PERMISSION_ADMIN, FULL_ACCESS)
52		sc.dashboardPermissionScenario(EDITOR, models.PERMISSION_EDIT, FULL_ACCESS)
53		sc.dashboardPermissionScenario(EDITOR, models.PERMISSION_VIEW, FULL_ACCESS)
54
55		// dashboard has viewer role with permission
56		sc.dashboardPermissionScenario(VIEWER, models.PERMISSION_ADMIN, FULL_ACCESS)
57		sc.dashboardPermissionScenario(VIEWER, models.PERMISSION_EDIT, FULL_ACCESS)
58		sc.dashboardPermissionScenario(VIEWER, models.PERMISSION_VIEW, FULL_ACCESS)
59
60		// parent folder has user with permission
61		sc.parentFolderPermissionScenario(USER, models.PERMISSION_ADMIN, FULL_ACCESS)
62		sc.parentFolderPermissionScenario(USER, models.PERMISSION_EDIT, FULL_ACCESS)
63		sc.parentFolderPermissionScenario(USER, models.PERMISSION_VIEW, FULL_ACCESS)
64
65		// parent folder has team with permission
66		sc.parentFolderPermissionScenario(TEAM, models.PERMISSION_ADMIN, FULL_ACCESS)
67		sc.parentFolderPermissionScenario(TEAM, models.PERMISSION_EDIT, FULL_ACCESS)
68		sc.parentFolderPermissionScenario(TEAM, models.PERMISSION_VIEW, FULL_ACCESS)
69
70		// parent folder has editor role with permission
71		sc.parentFolderPermissionScenario(EDITOR, models.PERMISSION_ADMIN, FULL_ACCESS)
72		sc.parentFolderPermissionScenario(EDITOR, models.PERMISSION_EDIT, FULL_ACCESS)
73		sc.parentFolderPermissionScenario(EDITOR, models.PERMISSION_VIEW, FULL_ACCESS)
74
75		// parent folder has viewer role with permission
76		sc.parentFolderPermissionScenario(VIEWER, models.PERMISSION_ADMIN, FULL_ACCESS)
77		sc.parentFolderPermissionScenario(VIEWER, models.PERMISSION_EDIT, FULL_ACCESS)
78		sc.parentFolderPermissionScenario(VIEWER, models.PERMISSION_VIEW, FULL_ACCESS)
79	})
80}
81
82func TestGuardianEditor(t *testing.T) {
83	orgRoleScenario("Given user has editor org role", t, models.ROLE_EDITOR, func(sc *scenarioContext) {
84		// dashboard has default permissions
85		sc.defaultPermissionScenario(USER, EDITOR_ACCESS)
86
87		// dashboard has user with permission
88		sc.dashboardPermissionScenario(USER, models.PERMISSION_ADMIN, FULL_ACCESS)
89		sc.dashboardPermissionScenario(USER, models.PERMISSION_EDIT, EDITOR_ACCESS)
90		sc.dashboardPermissionScenario(USER, models.PERMISSION_VIEW, CAN_VIEW)
91
92		// dashboard has team with permission
93		sc.dashboardPermissionScenario(TEAM, models.PERMISSION_ADMIN, FULL_ACCESS)
94		sc.dashboardPermissionScenario(TEAM, models.PERMISSION_EDIT, EDITOR_ACCESS)
95		sc.dashboardPermissionScenario(TEAM, models.PERMISSION_VIEW, CAN_VIEW)
96
97		// dashboard has editor role with permission
98		sc.dashboardPermissionScenario(EDITOR, models.PERMISSION_ADMIN, FULL_ACCESS)
99		sc.dashboardPermissionScenario(EDITOR, models.PERMISSION_EDIT, EDITOR_ACCESS)
100		sc.dashboardPermissionScenario(EDITOR, models.PERMISSION_VIEW, VIEWER_ACCESS)
101
102		// dashboard has viewer role with permission
103		sc.dashboardPermissionScenario(VIEWER, models.PERMISSION_ADMIN, NO_ACCESS)
104		sc.dashboardPermissionScenario(VIEWER, models.PERMISSION_EDIT, NO_ACCESS)
105		sc.dashboardPermissionScenario(VIEWER, models.PERMISSION_VIEW, NO_ACCESS)
106
107		// parent folder has user with permission
108		sc.parentFolderPermissionScenario(USER, models.PERMISSION_ADMIN, FULL_ACCESS)
109		sc.parentFolderPermissionScenario(USER, models.PERMISSION_EDIT, EDITOR_ACCESS)
110		sc.parentFolderPermissionScenario(USER, models.PERMISSION_VIEW, VIEWER_ACCESS)
111
112		// parent folder has team with permission
113		sc.parentFolderPermissionScenario(TEAM, models.PERMISSION_ADMIN, FULL_ACCESS)
114		sc.parentFolderPermissionScenario(TEAM, models.PERMISSION_EDIT, EDITOR_ACCESS)
115		sc.parentFolderPermissionScenario(TEAM, models.PERMISSION_VIEW, VIEWER_ACCESS)
116
117		// parent folder has editor role with permission
118		sc.parentFolderPermissionScenario(EDITOR, models.PERMISSION_ADMIN, FULL_ACCESS)
119		sc.parentFolderPermissionScenario(EDITOR, models.PERMISSION_EDIT, EDITOR_ACCESS)
120		sc.parentFolderPermissionScenario(EDITOR, models.PERMISSION_VIEW, VIEWER_ACCESS)
121
122		// parent folder has viewer role with permission
123		sc.parentFolderPermissionScenario(VIEWER, models.PERMISSION_ADMIN, NO_ACCESS)
124		sc.parentFolderPermissionScenario(VIEWER, models.PERMISSION_EDIT, NO_ACCESS)
125		sc.parentFolderPermissionScenario(VIEWER, models.PERMISSION_VIEW, NO_ACCESS)
126	})
127}
128
129func TestGuardianViewer(t *testing.T) {
130	orgRoleScenario("Given user has viewer org role", t, models.ROLE_VIEWER, func(sc *scenarioContext) {
131		// dashboard has default permissions
132		sc.defaultPermissionScenario(USER, VIEWER_ACCESS)
133
134		// dashboard has user with permission
135		sc.dashboardPermissionScenario(USER, models.PERMISSION_ADMIN, FULL_ACCESS)
136		sc.dashboardPermissionScenario(USER, models.PERMISSION_EDIT, EDITOR_ACCESS)
137		sc.dashboardPermissionScenario(USER, models.PERMISSION_VIEW, VIEWER_ACCESS)
138
139		// dashboard has team with permission
140		sc.dashboardPermissionScenario(TEAM, models.PERMISSION_ADMIN, FULL_ACCESS)
141		sc.dashboardPermissionScenario(TEAM, models.PERMISSION_EDIT, EDITOR_ACCESS)
142		sc.dashboardPermissionScenario(TEAM, models.PERMISSION_VIEW, VIEWER_ACCESS)
143
144		// dashboard has editor role with permission
145		sc.dashboardPermissionScenario(EDITOR, models.PERMISSION_ADMIN, NO_ACCESS)
146		sc.dashboardPermissionScenario(EDITOR, models.PERMISSION_EDIT, NO_ACCESS)
147		sc.dashboardPermissionScenario(EDITOR, models.PERMISSION_VIEW, NO_ACCESS)
148
149		// dashboard has viewer role with permission
150		sc.dashboardPermissionScenario(VIEWER, models.PERMISSION_ADMIN, FULL_ACCESS)
151		sc.dashboardPermissionScenario(VIEWER, models.PERMISSION_EDIT, EDITOR_ACCESS)
152		sc.dashboardPermissionScenario(VIEWER, models.PERMISSION_VIEW, VIEWER_ACCESS)
153
154		// parent folder has user with permission
155		sc.parentFolderPermissionScenario(USER, models.PERMISSION_ADMIN, FULL_ACCESS)
156		sc.parentFolderPermissionScenario(USER, models.PERMISSION_EDIT, EDITOR_ACCESS)
157		sc.parentFolderPermissionScenario(USER, models.PERMISSION_VIEW, VIEWER_ACCESS)
158
159		// parent folder has team with permission
160		sc.parentFolderPermissionScenario(TEAM, models.PERMISSION_ADMIN, FULL_ACCESS)
161		sc.parentFolderPermissionScenario(TEAM, models.PERMISSION_EDIT, EDITOR_ACCESS)
162		sc.parentFolderPermissionScenario(TEAM, models.PERMISSION_VIEW, VIEWER_ACCESS)
163
164		// parent folder has editor role with permission
165		sc.parentFolderPermissionScenario(EDITOR, models.PERMISSION_ADMIN, NO_ACCESS)
166		sc.parentFolderPermissionScenario(EDITOR, models.PERMISSION_EDIT, NO_ACCESS)
167		sc.parentFolderPermissionScenario(EDITOR, models.PERMISSION_VIEW, NO_ACCESS)
168
169		// parent folder has viewer role with permission
170		sc.parentFolderPermissionScenario(VIEWER, models.PERMISSION_ADMIN, FULL_ACCESS)
171		sc.parentFolderPermissionScenario(VIEWER, models.PERMISSION_EDIT, EDITOR_ACCESS)
172		sc.parentFolderPermissionScenario(VIEWER, models.PERMISSION_VIEW, VIEWER_ACCESS)
173	})
174
175	apiKeyScenario("Given api key with viewer role", t, models.ROLE_VIEWER, func(sc *scenarioContext) {
176		// dashboard has default permissions
177		sc.defaultPermissionScenario(VIEWER, VIEWER_ACCESS)
178	})
179}
180
181func (sc *scenarioContext) defaultPermissionScenario(pt permissionType, flag permissionFlags) {
182	_, callerFile, callerLine, _ := runtime.Caller(1)
183	sc.callerFile = callerFile
184	sc.callerLine = callerLine
185	existingPermissions := []*models.DashboardAclInfoDTO{
186		toDto(newEditorRolePermission(defaultDashboardID, models.PERMISSION_EDIT)),
187		toDto(newViewerRolePermission(defaultDashboardID, models.PERMISSION_VIEW)),
188	}
189
190	permissionScenario("and existing permissions are the default permissions (everyone with editor role can edit, everyone with viewer role can view)",
191		dashboardID, sc, existingPermissions, func(sc *scenarioContext) {
192			sc.expectedFlags = flag
193			sc.verifyExpectedPermissionsFlags()
194			sc.verifyDuplicatePermissionsShouldNotBeAllowed()
195			sc.verifyUpdateDashboardPermissionsShouldBeAllowed(pt)
196			sc.verifyUpdateDashboardPermissionsShouldNotBeAllowed(pt)
197		})
198}
199
200func (sc *scenarioContext) dashboardPermissionScenario(pt permissionType, permission models.PermissionType, flag permissionFlags) {
201	_, callerFile, callerLine, _ := runtime.Caller(1)
202	sc.callerFile = callerFile
203	sc.callerLine = callerLine
204	var existingPermissions []*models.DashboardAclInfoDTO
205
206	switch pt {
207	case USER:
208		existingPermissions = []*models.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: dashboardID, UserId: userID, Permission: permission}}
209	case TEAM:
210		existingPermissions = []*models.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: dashboardID, TeamId: teamID, Permission: permission}}
211	case EDITOR:
212		existingPermissions = []*models.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: dashboardID, Role: &editorRole, Permission: permission}}
213	case VIEWER:
214		existingPermissions = []*models.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: dashboardID, Role: &viewerRole, Permission: permission}}
215	}
216
217	permissionScenario(fmt.Sprintf("and %s has permission to %s dashboard", pt.String(), permission.String()),
218		dashboardID, sc, existingPermissions, func(sc *scenarioContext) {
219			sc.expectedFlags = flag
220			sc.verifyExpectedPermissionsFlags()
221			sc.verifyDuplicatePermissionsShouldNotBeAllowed()
222			sc.verifyUpdateDashboardPermissionsShouldBeAllowed(pt)
223			sc.verifyUpdateDashboardPermissionsShouldNotBeAllowed(pt)
224		})
225}
226
227func (sc *scenarioContext) parentFolderPermissionScenario(pt permissionType, permission models.PermissionType, flag permissionFlags) {
228	_, callerFile, callerLine, _ := runtime.Caller(1)
229	sc.callerFile = callerFile
230	sc.callerLine = callerLine
231	var folderPermissionList []*models.DashboardAclInfoDTO
232
233	switch pt {
234	case USER:
235		folderPermissionList = []*models.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: parentFolderID,
236			UserId: userID, Permission: permission, Inherited: true}}
237	case TEAM:
238		folderPermissionList = []*models.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: parentFolderID, TeamId: teamID,
239			Permission: permission, Inherited: true}}
240	case EDITOR:
241		folderPermissionList = []*models.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: parentFolderID,
242			Role: &editorRole, Permission: permission, Inherited: true}}
243	case VIEWER:
244		folderPermissionList = []*models.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: parentFolderID,
245			Role: &viewerRole, Permission: permission, Inherited: true}}
246	}
247
248	permissionScenario(fmt.Sprintf("and parent folder has %s with permission to %s", pt.String(), permission.String()),
249		childDashboardID, sc, folderPermissionList, func(sc *scenarioContext) {
250			sc.expectedFlags = flag
251			sc.verifyExpectedPermissionsFlags()
252			sc.verifyDuplicatePermissionsShouldNotBeAllowed()
253			sc.verifyUpdateChildDashboardPermissionsShouldBeAllowed(pt, permission)
254			sc.verifyUpdateChildDashboardPermissionsShouldNotBeAllowed(pt, permission)
255			sc.verifyUpdateChildDashboardPermissionsWithOverrideShouldBeAllowed(pt, permission)
256			sc.verifyUpdateChildDashboardPermissionsWithOverrideShouldNotBeAllowed(pt, permission)
257		})
258}
259
260func (sc *scenarioContext) verifyExpectedPermissionsFlags() {
261	tc := fmt.Sprintf("should have permissions to %s", sc.expectedFlags.String())
262	sc.t.Run(tc, func(t *testing.T) {
263		canAdmin, err := sc.g.CanAdmin()
264		require.NoError(t, err)
265		canEdit, err := sc.g.CanEdit()
266		require.NoError(t, err)
267		canSave, err := sc.g.CanSave()
268		require.NoError(t, err)
269		canView, err := sc.g.CanView()
270		require.NoError(t, err)
271
272		var actualFlag permissionFlags
273
274		if canAdmin {
275			actualFlag |= CAN_ADMIN
276		}
277
278		if canEdit {
279			actualFlag |= CAN_EDIT
280		}
281
282		if canSave {
283			actualFlag |= CAN_SAVE
284		}
285
286		if canView {
287			actualFlag |= CAN_VIEW
288		}
289
290		if actualFlag.noAccess() {
291			actualFlag = NO_ACCESS
292		}
293
294		if actualFlag&sc.expectedFlags != actualFlag {
295			sc.reportFailure(tc, sc.expectedFlags.String(), actualFlag.String())
296		}
297
298		sc.reportSuccess()
299	})
300}
301
302func (sc *scenarioContext) verifyDuplicatePermissionsShouldNotBeAllowed() {
303	if !sc.expectedFlags.canAdmin() {
304		return
305	}
306
307	tc := "When updating dashboard permissions with duplicate permission for user should not be allowed"
308	sc.t.Run(tc, func(t *testing.T) {
309		p := []*models.DashboardAcl{
310			newDefaultUserPermission(dashboardID, models.PERMISSION_VIEW),
311			newDefaultUserPermission(dashboardID, models.PERMISSION_ADMIN),
312		}
313		sc.updatePermissions = p
314		_, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, p)
315
316		if !errors.Is(err, ErrGuardianPermissionExists) {
317			sc.reportFailure(tc, ErrGuardianPermissionExists, err)
318		}
319		sc.reportSuccess()
320	})
321
322	tc = "When updating dashboard permissions with duplicate permission for team should not be allowed"
323	sc.t.Run(tc, func(t *testing.T) {
324		p := []*models.DashboardAcl{
325			newDefaultTeamPermission(dashboardID, models.PERMISSION_VIEW),
326			newDefaultTeamPermission(dashboardID, models.PERMISSION_ADMIN),
327		}
328		sc.updatePermissions = p
329		_, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, p)
330		if !errors.Is(err, ErrGuardianPermissionExists) {
331			sc.reportFailure(tc, ErrGuardianPermissionExists, err)
332		}
333		sc.reportSuccess()
334	})
335
336	tc = "When updating dashboard permissions with duplicate permission for editor role should not be allowed"
337	sc.t.Run(tc, func(t *testing.T) {
338		p := []*models.DashboardAcl{
339			newEditorRolePermission(dashboardID, models.PERMISSION_VIEW),
340			newEditorRolePermission(dashboardID, models.PERMISSION_ADMIN),
341		}
342		sc.updatePermissions = p
343		_, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, p)
344
345		if !errors.Is(err, ErrGuardianPermissionExists) {
346			sc.reportFailure(tc, ErrGuardianPermissionExists, err)
347		}
348		sc.reportSuccess()
349	})
350
351	tc = "When updating dashboard permissions with duplicate permission for viewer role should not be allowed"
352	sc.t.Run(tc, func(t *testing.T) {
353		p := []*models.DashboardAcl{
354			newViewerRolePermission(dashboardID, models.PERMISSION_VIEW),
355			newViewerRolePermission(dashboardID, models.PERMISSION_ADMIN),
356		}
357		sc.updatePermissions = p
358		_, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, p)
359		if !errors.Is(err, ErrGuardianPermissionExists) {
360			sc.reportFailure(tc, ErrGuardianPermissionExists, err)
361		}
362		sc.reportSuccess()
363	})
364
365	tc = "When updating dashboard permissions with duplicate permission for admin role should not be allowed"
366	sc.t.Run(tc, func(t *testing.T) {
367		p := []*models.DashboardAcl{
368			newAdminRolePermission(dashboardID, models.PERMISSION_ADMIN),
369		}
370		sc.updatePermissions = p
371		_, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, p)
372		if !errors.Is(err, ErrGuardianPermissionExists) {
373			sc.reportFailure(tc, ErrGuardianPermissionExists, err)
374		}
375		sc.reportSuccess()
376	})
377}
378
379func (sc *scenarioContext) verifyUpdateDashboardPermissionsShouldBeAllowed(pt permissionType) {
380	if !sc.expectedFlags.canAdmin() {
381		return
382	}
383
384	for _, p := range []models.PermissionType{models.PERMISSION_ADMIN, models.PERMISSION_EDIT, models.PERMISSION_VIEW} {
385		tc := fmt.Sprintf("When updating dashboard permissions with %s permissions should be allowed", p.String())
386		sc.t.Run(tc, func(t *testing.T) {
387			permissionList := []*models.DashboardAcl{}
388			switch pt {
389			case USER:
390				permissionList = []*models.DashboardAcl{
391					newEditorRolePermission(dashboardID, p),
392					newViewerRolePermission(dashboardID, p),
393					newCustomUserPermission(dashboardID, otherUserID, p),
394					newDefaultTeamPermission(dashboardID, p),
395				}
396			case TEAM:
397				permissionList = []*models.DashboardAcl{
398					newEditorRolePermission(dashboardID, p),
399					newViewerRolePermission(dashboardID, p),
400					newDefaultUserPermission(dashboardID, p),
401					newCustomTeamPermission(dashboardID, otherTeamID, p),
402				}
403			case EDITOR, VIEWER:
404				permissionList = []*models.DashboardAcl{
405					newEditorRolePermission(dashboardID, p),
406					newViewerRolePermission(dashboardID, p),
407					newDefaultUserPermission(dashboardID, p),
408					newDefaultTeamPermission(dashboardID, p),
409				}
410			}
411
412			sc.updatePermissions = permissionList
413			ok, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, permissionList)
414			if err != nil {
415				sc.reportFailure(tc, nil, err)
416			}
417			if !ok {
418				sc.reportFailure(tc, false, true)
419			}
420			sc.reportSuccess()
421		})
422	}
423}
424
425func (sc *scenarioContext) verifyUpdateDashboardPermissionsShouldNotBeAllowed(pt permissionType) {
426	if sc.expectedFlags.canAdmin() {
427		return
428	}
429
430	for _, p := range []models.PermissionType{models.PERMISSION_ADMIN, models.PERMISSION_EDIT, models.PERMISSION_VIEW} {
431		tc := fmt.Sprintf("When updating dashboard permissions with %s permissions should NOT be allowed", p.String())
432		sc.t.Run(tc, func(t *testing.T) {
433			permissionList := []*models.DashboardAcl{
434				newEditorRolePermission(dashboardID, p),
435				newViewerRolePermission(dashboardID, p),
436			}
437			switch pt {
438			case USER:
439				permissionList = append(permissionList, []*models.DashboardAcl{
440					newCustomUserPermission(dashboardID, otherUserID, p),
441					newDefaultTeamPermission(dashboardID, p),
442				}...)
443			case TEAM:
444				permissionList = append(permissionList, []*models.DashboardAcl{
445					newDefaultUserPermission(dashboardID, p),
446					newCustomTeamPermission(dashboardID, otherTeamID, p),
447				}...)
448			default:
449				// TODO: Handle other cases?
450			}
451
452			sc.updatePermissions = permissionList
453			ok, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, permissionList)
454			if err != nil {
455				sc.reportFailure(tc, nil, err)
456			}
457			if ok {
458				sc.reportFailure(tc, true, false)
459			}
460			sc.reportSuccess()
461		})
462	}
463}
464
465func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsShouldBeAllowed(pt permissionType, parentFolderPermission models.PermissionType) {
466	if !sc.expectedFlags.canAdmin() {
467		return
468	}
469
470	for _, p := range []models.PermissionType{models.PERMISSION_ADMIN, models.PERMISSION_EDIT, models.PERMISSION_VIEW} {
471		tc := fmt.Sprintf("When updating child dashboard permissions with %s permissions should be allowed", p.String())
472		sc.t.Run(tc, func(t *testing.T) {
473			permissionList := []*models.DashboardAcl{}
474			switch pt {
475			case USER:
476				permissionList = []*models.DashboardAcl{
477					newEditorRolePermission(childDashboardID, p),
478					newViewerRolePermission(childDashboardID, p),
479					newCustomUserPermission(childDashboardID, otherUserID, p),
480					newDefaultTeamPermission(childDashboardID, p),
481				}
482			case TEAM:
483				permissionList = []*models.DashboardAcl{
484					newEditorRolePermission(childDashboardID, p),
485					newViewerRolePermission(childDashboardID, p),
486					newDefaultUserPermission(childDashboardID, p),
487					newCustomTeamPermission(childDashboardID, otherTeamID, p),
488				}
489			case EDITOR:
490				permissionList = []*models.DashboardAcl{
491					newViewerRolePermission(childDashboardID, p),
492					newDefaultUserPermission(childDashboardID, p),
493					newDefaultTeamPermission(childDashboardID, p),
494				}
495
496				// permission to update is higher than parent folder permission
497				if p > parentFolderPermission {
498					permissionList = append(permissionList, newEditorRolePermission(childDashboardID, p))
499				}
500			case VIEWER:
501				permissionList = []*models.DashboardAcl{
502					newEditorRolePermission(childDashboardID, p),
503					newDefaultUserPermission(childDashboardID, p),
504					newDefaultTeamPermission(childDashboardID, p),
505				}
506
507				// permission to update is higher than parent folder permission
508				if p > parentFolderPermission {
509					permissionList = append(permissionList, newViewerRolePermission(childDashboardID, p))
510				}
511			}
512
513			sc.updatePermissions = permissionList
514			ok, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, permissionList)
515			if err != nil {
516				sc.reportFailure(tc, nil, err)
517			}
518			if !ok {
519				sc.reportFailure(tc, false, true)
520			}
521			sc.reportSuccess()
522		})
523	}
524}
525
526func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsShouldNotBeAllowed(pt permissionType, parentFolderPermission models.PermissionType) {
527	if sc.expectedFlags.canAdmin() {
528		return
529	}
530
531	for _, p := range []models.PermissionType{models.PERMISSION_ADMIN, models.PERMISSION_EDIT, models.PERMISSION_VIEW} {
532		tc := fmt.Sprintf("When updating child dashboard permissions with %s permissions should NOT be allowed", p.String())
533		sc.t.Run(tc, func(t *testing.T) {
534			permissionList := []*models.DashboardAcl{}
535			switch pt {
536			case USER:
537				permissionList = []*models.DashboardAcl{
538					newEditorRolePermission(childDashboardID, p),
539					newViewerRolePermission(childDashboardID, p),
540					newCustomUserPermission(childDashboardID, otherUserID, p),
541					newDefaultTeamPermission(childDashboardID, p),
542				}
543			case TEAM:
544				permissionList = []*models.DashboardAcl{
545					newEditorRolePermission(childDashboardID, p),
546					newViewerRolePermission(childDashboardID, p),
547					newDefaultUserPermission(childDashboardID, p),
548					newCustomTeamPermission(childDashboardID, otherTeamID, p),
549				}
550			case EDITOR:
551				permissionList = []*models.DashboardAcl{
552					newViewerRolePermission(childDashboardID, p),
553					newDefaultUserPermission(childDashboardID, p),
554					newDefaultTeamPermission(childDashboardID, p),
555				}
556
557				// permission to update is higher than parent folder permission
558				if p > parentFolderPermission {
559					permissionList = append(permissionList, newEditorRolePermission(childDashboardID, p))
560				}
561			case VIEWER:
562				permissionList = []*models.DashboardAcl{
563					newEditorRolePermission(childDashboardID, p),
564					newDefaultUserPermission(childDashboardID, p),
565					newDefaultTeamPermission(childDashboardID, p),
566				}
567
568				// permission to update is higher than parent folder permission
569				if p > parentFolderPermission {
570					permissionList = append(permissionList, newViewerRolePermission(childDashboardID, p))
571				}
572			}
573
574			sc.updatePermissions = permissionList
575			ok, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, permissionList)
576			if err != nil {
577				sc.reportFailure(tc, nil, err)
578			}
579			if ok {
580				sc.reportFailure(tc, true, false)
581			}
582			sc.reportSuccess()
583		})
584	}
585}
586
587func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsWithOverrideShouldBeAllowed(pt permissionType, parentFolderPermission models.PermissionType) {
588	if !sc.expectedFlags.canAdmin() {
589		return
590	}
591
592	for _, p := range []models.PermissionType{models.PERMISSION_ADMIN, models.PERMISSION_EDIT, models.PERMISSION_VIEW} {
593		// permission to update is higher than parent folder permission
594		if p > parentFolderPermission {
595			continue
596		}
597
598		tc := fmt.Sprintf("When updating child dashboard permissions overriding parent %s permission with %s permission should NOT be allowed", pt.String(), p.String())
599		sc.t.Run(tc, func(t *testing.T) {
600			permissionList := []*models.DashboardAcl{}
601			switch pt {
602			case USER:
603				permissionList = []*models.DashboardAcl{
604					newDefaultUserPermission(childDashboardID, p),
605				}
606			case TEAM:
607				permissionList = []*models.DashboardAcl{
608					newDefaultTeamPermission(childDashboardID, p),
609				}
610			case EDITOR:
611				permissionList = []*models.DashboardAcl{
612					newEditorRolePermission(childDashboardID, p),
613				}
614			case VIEWER:
615				permissionList = []*models.DashboardAcl{
616					newViewerRolePermission(childDashboardID, p),
617				}
618			}
619
620			sc.updatePermissions = permissionList
621			_, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, permissionList)
622			if !errors.Is(err, ErrGuardianOverride) {
623				sc.reportFailure(tc, ErrGuardianOverride, err)
624			}
625			sc.reportSuccess()
626		})
627	}
628}
629
630func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsWithOverrideShouldNotBeAllowed(pt permissionType, parentFolderPermission models.PermissionType) {
631	if !sc.expectedFlags.canAdmin() {
632		return
633	}
634
635	for _, p := range []models.PermissionType{models.PERMISSION_ADMIN, models.PERMISSION_EDIT, models.PERMISSION_VIEW} {
636		// permission to update is lower than or equal to parent folder permission
637		if p <= parentFolderPermission {
638			continue
639		}
640
641		tc := fmt.Sprintf(
642			"When updating child dashboard permissions overriding parent %s permission with %s permission should be allowed",
643			pt.String(), p.String(),
644		)
645		sc.t.Run(tc, func(t *testing.T) {
646			permissionList := []*models.DashboardAcl{}
647			switch pt {
648			case USER:
649				permissionList = []*models.DashboardAcl{
650					newDefaultUserPermission(childDashboardID, p),
651				}
652			case TEAM:
653				permissionList = []*models.DashboardAcl{
654					newDefaultTeamPermission(childDashboardID, p),
655				}
656			case EDITOR:
657				permissionList = []*models.DashboardAcl{
658					newEditorRolePermission(childDashboardID, p),
659				}
660			case VIEWER:
661				permissionList = []*models.DashboardAcl{
662					newViewerRolePermission(childDashboardID, p),
663				}
664			}
665
666			_, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, permissionList)
667			if err != nil {
668				sc.reportFailure(tc, nil, err)
669			}
670			sc.updatePermissions = permissionList
671			ok, err := sc.g.CheckPermissionBeforeUpdate(models.PERMISSION_ADMIN, permissionList)
672			if err != nil {
673				sc.reportFailure(tc, nil, err)
674			}
675			if !ok {
676				sc.reportFailure(tc, false, true)
677			}
678			sc.reportSuccess()
679		})
680	}
681}
682
683func TestGuardianGetHiddenACL(t *testing.T) {
684	t.Run("Get hidden ACL tests", func(t *testing.T) {
685		bus.ClearBusHandlers()
686
687		bus.AddHandler("test", func(query *models.GetDashboardAclInfoListQuery) error {
688			query.Result = []*models.DashboardAclInfoDTO{
689				{Inherited: false, UserId: 1, UserLogin: "user1", Permission: models.PERMISSION_EDIT},
690				{Inherited: false, UserId: 2, UserLogin: "user2", Permission: models.PERMISSION_ADMIN},
691				{Inherited: true, UserId: 3, UserLogin: "user3", Permission: models.PERMISSION_VIEW},
692			}
693			return nil
694		})
695
696		cfg := setting.NewCfg()
697		cfg.HiddenUsers = map[string]struct{}{"user2": {}}
698
699		t.Run("Should get hidden acl", func(t *testing.T) {
700			user := &models.SignedInUser{
701				OrgId:  orgID,
702				UserId: 1,
703				Login:  "user1",
704			}
705			g := New(context.Background(), dashboardID, orgID, user)
706
707			hiddenACL, err := g.GetHiddenACL(cfg)
708			require.NoError(t, err)
709
710			require.Equal(t, len(hiddenACL), 1)
711			require.Equal(t, hiddenACL[0].UserID, int64(2))
712		})
713
714		t.Run("Grafana admin should not get hidden acl", func(t *testing.T) {
715			user := &models.SignedInUser{
716				OrgId:          orgID,
717				UserId:         1,
718				Login:          "user1",
719				IsGrafanaAdmin: true,
720			}
721			g := New(context.Background(), dashboardID, orgID, user)
722
723			hiddenACL, err := g.GetHiddenACL(cfg)
724			require.NoError(t, err)
725
726			require.Equal(t, len(hiddenACL), 0)
727		})
728	})
729}
730
731func TestGuardianGetAclWithoutDuplicates(t *testing.T) {
732	t.Run("Get hidden ACL tests", func(t *testing.T) {
733		t.Cleanup(bus.ClearBusHandlers)
734
735		bus.AddHandler("test", func(query *models.GetDashboardAclInfoListQuery) error {
736			query.Result = []*models.DashboardAclInfoDTO{
737				{Inherited: true, UserId: 3, UserLogin: "user3", Permission: models.PERMISSION_EDIT},
738				{Inherited: false, UserId: 3, UserLogin: "user3", Permission: models.PERMISSION_VIEW},
739				{Inherited: false, UserId: 2, UserLogin: "user2", Permission: models.PERMISSION_ADMIN},
740				{Inherited: true, UserId: 4, UserLogin: "user4", Permission: models.PERMISSION_ADMIN},
741				{Inherited: false, UserId: 4, UserLogin: "user4", Permission: models.PERMISSION_ADMIN},
742				{Inherited: false, UserId: 5, UserLogin: "user5", Permission: models.PERMISSION_EDIT},
743				{Inherited: true, UserId: 6, UserLogin: "user6", Permission: models.PERMISSION_VIEW},
744				{Inherited: false, UserId: 6, UserLogin: "user6", Permission: models.PERMISSION_EDIT},
745			}
746			return nil
747		})
748
749		t.Run("Should get acl without duplicates", func(t *testing.T) {
750			user := &models.SignedInUser{
751				OrgId:  orgID,
752				UserId: 1,
753				Login:  "user1",
754			}
755			g := New(context.Background(), dashboardID, orgID, user)
756
757			acl, err := g.GetACLWithoutDuplicates()
758			require.NoError(t, err)
759			require.NotNil(t, acl)
760			require.Len(t, acl, 6)
761			require.ElementsMatch(t, []*models.DashboardAclInfoDTO{
762				{Inherited: true, UserId: 3, UserLogin: "user3", Permission: models.PERMISSION_EDIT},
763				{Inherited: true, UserId: 4, UserLogin: "user4", Permission: models.PERMISSION_ADMIN},
764				{Inherited: true, UserId: 6, UserLogin: "user6", Permission: models.PERMISSION_VIEW},
765				{Inherited: false, UserId: 2, UserLogin: "user2", Permission: models.PERMISSION_ADMIN},
766				{Inherited: false, UserId: 5, UserLogin: "user5", Permission: models.PERMISSION_EDIT},
767				{Inherited: false, UserId: 6, UserLogin: "user6", Permission: models.PERMISSION_EDIT},
768			}, acl)
769		})
770	})
771}
772