2017-06-16 20:25:24 -05:00
package guardian
import (
2021-09-23 10:43:32 -05:00
"context"
2018-02-26 12:12:01 -06:00
"errors"
2022-10-19 08:02:15 -05:00
"github.com/grafana/grafana/pkg/infra/db"
2019-05-13 01:45:54 -05:00
"github.com/grafana/grafana/pkg/infra/log"
2020-02-29 06:35:15 -06:00
"github.com/grafana/grafana/pkg/models"
2022-06-01 13:16:26 -05:00
"github.com/grafana/grafana/pkg/services/dashboards"
2022-08-10 04:56:48 -05:00
"github.com/grafana/grafana/pkg/services/org"
2022-09-20 11:58:04 -05:00
"github.com/grafana/grafana/pkg/services/team"
2022-08-10 04:56:48 -05:00
"github.com/grafana/grafana/pkg/services/user"
2017-12-15 07:19:49 -06:00
"github.com/grafana/grafana/pkg/setting"
2022-12-15 08:34:17 -06:00
"github.com/grafana/grafana/pkg/util/errutil"
2017-06-16 20:25:24 -05:00
)
2018-02-26 12:12:01 -06:00
var (
2022-12-15 08:34:17 -06:00
ErrGuardianPermissionExists = errors . New ( "permission already exists" )
ErrGuardianOverride = errors . New ( "you can only override a permission to be higher" )
ErrGuardianGetDashboardFailure = errutil . NewBase ( errutil . StatusInternal , "guardian.getDashboardFailure" , errutil . WithPublicMessage ( "Failed to get dashboard" ) )
ErrGuardianDashboardNotFound = errutil . NewBase ( errutil . StatusNotFound , "guardian.dashboardNotFound" )
2018-02-26 12:12:01 -06:00
)
2018-02-19 04:12:56 -06:00
// DashboardGuardian to be used for guard against operations without access on dashboard and acl
type DashboardGuardian interface {
CanSave ( ) ( bool , error )
CanEdit ( ) ( bool , error )
CanView ( ) ( bool , error )
CanAdmin ( ) ( bool , error )
2022-03-03 08:05:47 -06:00
CanDelete ( ) ( bool , error )
CanCreate ( folderID int64 , isFolder bool ) ( bool , error )
2022-07-18 08:14:58 -05:00
CheckPermissionBeforeUpdate ( permission models . PermissionType , updatePermissions [ ] * models . DashboardACL ) ( bool , error )
2021-04-28 07:42:18 -05:00
2022-07-18 08:14:58 -05:00
// GetACL returns ACL.
GetACL ( ) ( [ ] * models . DashboardACLInfoDTO , error )
2021-04-28 07:42:18 -05:00
// GetACLWithoutDuplicates returns ACL and strips any permission
// that already has an inherited permission with higher or equal
// permission.
2022-07-18 08:14:58 -05:00
GetACLWithoutDuplicates ( ) ( [ ] * models . DashboardACLInfoDTO , error )
GetHiddenACL ( * setting . Cfg ) ( [ ] * models . DashboardACL , error )
2018-02-19 04:12:56 -06:00
}
type dashboardGuardianImpl struct {
2022-08-10 04:56:48 -05:00
user * user . SignedInUser
2022-06-01 13:16:26 -05:00
dashId int64
orgId int64
2022-07-18 08:14:58 -05:00
acl [ ] * models . DashboardACLInfoDTO
2022-06-01 13:16:26 -05:00
teams [ ] * models . TeamDTO
log log . Logger
ctx context . Context
2022-10-14 14:33:06 -05:00
store db . DB
2022-06-01 13:16:26 -05:00
dashboardService dashboards . DashboardService
2022-09-20 11:58:04 -05:00
teamService team . Service
2017-06-16 20:25:24 -05:00
}
2018-02-19 04:12:56 -06:00
// New factory for creating a new dashboard guardian instance
2022-03-03 08:05:47 -06:00
// When using access control this function is replaced on startup and the AccessControlDashboardGuardian is returned
2022-12-15 08:34:17 -06:00
var New = func ( ctx context . Context , dashId int64 , orgId int64 , user * user . SignedInUser ) ( DashboardGuardian , error ) {
2022-03-21 04:49:49 -05:00
panic ( "no guardian factory implementation provided" )
}
2022-12-15 08:34:17 -06:00
// NewByUID factory for creating a new dashboard guardian instance
// When using access control this function is replaced on startup and the AccessControlDashboardGuardian is returned
var NewByUID = func ( ctx context . Context , dashUID string , orgId int64 , user * user . SignedInUser ) ( DashboardGuardian , error ) {
panic ( "no guardian factory implementation provided" )
}
// NewByDashboard factory for creating a new dashboard guardian instance
// When using access control this function is replaced on startup and the AccessControlDashboardGuardian is returned
var NewByDashboard = func ( ctx context . Context , dash * models . Dashboard , orgId int64 , user * user . SignedInUser ) ( DashboardGuardian , error ) {
panic ( "no guardian factory implementation provided" )
}
// newDashboardGuardian creates a dashboard guardian by the provided dashId.
func newDashboardGuardian ( ctx context . Context , dashId int64 , orgId int64 , user * user . SignedInUser , store db . DB , dashSvc dashboards . DashboardService , teamSvc team . Service ) ( * dashboardGuardianImpl , error ) {
if dashId != 0 {
q := & models . GetDashboardQuery {
Id : dashId ,
OrgId : orgId ,
}
if err := dashSvc . GetDashboard ( ctx , q ) ; err != nil {
if errors . Is ( err , dashboards . ErrDashboardNotFound ) {
return nil , ErrGuardianDashboardNotFound . Errorf ( "failed to get dashboard by UID: %w" , err )
}
return nil , ErrGuardianGetDashboardFailure . Errorf ( "failed to get dashboard by UID: %w" , err )
}
}
2018-02-19 04:12:56 -06:00
return & dashboardGuardianImpl {
2022-06-01 13:16:26 -05:00
user : user ,
dashId : dashId ,
orgId : orgId ,
log : log . New ( "dashboard.permissions" ) ,
ctx : ctx ,
store : store ,
dashboardService : dashSvc ,
2022-09-20 11:58:04 -05:00
teamService : teamSvc ,
2022-12-15 08:34:17 -06:00
} , nil
}
// newDashboardGuardianByUID creates a dashboard guardian by the provided dashUID.
func newDashboardGuardianByUID ( ctx context . Context , dashUID string , orgId int64 , user * user . SignedInUser , store db . DB , dashSvc dashboards . DashboardService , teamSvc team . Service ) ( * dashboardGuardianImpl , error ) {
dashID := int64 ( 0 )
if dashUID != "" {
q := & models . GetDashboardQuery {
Uid : dashUID ,
OrgId : orgId ,
}
if err := dashSvc . GetDashboard ( ctx , q ) ; err != nil {
if errors . Is ( err , dashboards . ErrDashboardNotFound ) {
return nil , ErrGuardianDashboardNotFound . Errorf ( "failed to get dashboard by UID: %w" , err )
}
return nil , ErrGuardianGetDashboardFailure . Errorf ( "failed to get dashboard by UID: %w" , err )
}
dashID = q . Result . Id
2017-06-16 20:25:24 -05:00
}
2022-12-15 08:34:17 -06:00
return & dashboardGuardianImpl {
user : user ,
dashId : dashID ,
orgId : orgId ,
log : log . New ( "dashboard.permissions" ) ,
ctx : ctx ,
store : store ,
dashboardService : dashSvc ,
teamService : teamSvc ,
} , nil
}
// newDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboard.
// This constructor should be preferred over the other two if the dashboard in available
// since it avoids querying the database for fetching the dashboard.
func newDashboardGuardianByDashboard ( ctx context . Context , dash * models . Dashboard , orgId int64 , user * user . SignedInUser , store db . DB , dashSvc dashboards . DashboardService , teamSvc team . Service ) ( * dashboardGuardianImpl , error ) {
return & dashboardGuardianImpl {
user : user ,
dashId : dash . Id ,
orgId : orgId ,
log : log . New ( "dashboard.permissions" ) ,
ctx : ctx ,
store : store ,
dashboardService : dashSvc ,
teamService : teamSvc ,
} , nil
2017-06-16 20:25:24 -05:00
}
2018-02-19 04:12:56 -06:00
func ( g * dashboardGuardianImpl ) CanSave ( ) ( bool , error ) {
2020-02-29 06:35:15 -06:00
return g . HasPermission ( models . PERMISSION_EDIT )
2017-06-16 20:25:24 -05:00
}
2018-02-19 04:12:56 -06:00
func ( g * dashboardGuardianImpl ) CanEdit ( ) ( bool , error ) {
2017-12-15 07:19:49 -06:00
if setting . ViewersCanEdit {
2020-02-29 06:35:15 -06:00
return g . HasPermission ( models . PERMISSION_VIEW )
2017-12-15 07:19:49 -06:00
}
2020-02-29 06:35:15 -06:00
return g . HasPermission ( models . PERMISSION_EDIT )
2017-06-16 20:25:24 -05:00
}
2018-02-19 04:12:56 -06:00
func ( g * dashboardGuardianImpl ) CanView ( ) ( bool , error ) {
2020-02-29 06:35:15 -06:00
return g . HasPermission ( models . PERMISSION_VIEW )
2017-06-16 20:25:24 -05:00
}
2018-02-19 04:12:56 -06:00
func ( g * dashboardGuardianImpl ) CanAdmin ( ) ( bool , error ) {
2020-02-29 06:35:15 -06:00
return g . HasPermission ( models . PERMISSION_ADMIN )
2017-06-22 16:01:04 -05:00
}
2022-03-03 08:05:47 -06:00
func ( g * dashboardGuardianImpl ) CanDelete ( ) ( bool , error ) {
// when using dashboard guardian without access control a user can delete a dashboard if they can save it
return g . CanSave ( )
}
func ( g * dashboardGuardianImpl ) CanCreate ( _ int64 , _ bool ) ( bool , error ) {
// when using dashboard guardian without access control a user can create a dashboard if they can save it
return g . CanSave ( )
}
2020-02-29 06:35:15 -06:00
func ( g * dashboardGuardianImpl ) HasPermission ( permission models . PermissionType ) ( bool , error ) {
2022-08-10 04:56:48 -05:00
if g . user . OrgRole == org . RoleAdmin {
2018-10-22 03:39:25 -05:00
return g . logHasPermissionResult ( permission , true , nil )
2017-06-19 12:47:44 -05:00
}
2022-07-18 08:14:58 -05:00
acl , err := g . GetACL ( )
2017-06-16 20:25:24 -05:00
if err != nil {
2018-10-22 03:39:25 -05:00
return g . logHasPermissionResult ( permission , false , err )
2017-06-16 20:25:24 -05:00
}
2022-07-18 08:14:58 -05:00
result , err := g . checkACL ( permission , acl )
2018-10-22 03:39:25 -05:00
return g . logHasPermissionResult ( permission , result , err )
}
2020-02-29 06:35:15 -06:00
func ( g * dashboardGuardianImpl ) logHasPermissionResult ( permission models . PermissionType , hasPermission bool , err error ) ( bool , error ) {
2018-10-22 03:39:25 -05:00
if err != nil {
return hasPermission , err
}
if hasPermission {
2022-08-11 06:28:55 -05:00
g . log . Debug ( "User granted access to execute action" , "userId" , g . user . UserID , "orgId" , g . orgId , "uname" , g . user . Login , "dashId" , g . dashId , "action" , permission )
2018-10-22 03:39:25 -05:00
} else {
2022-08-11 06:28:55 -05:00
g . log . Debug ( "User denied access to execute action" , "userId" , g . user . UserID , "orgId" , g . orgId , "uname" , g . user . Login , "dashId" , g . dashId , "action" , permission )
2018-10-22 03:39:25 -05:00
}
return hasPermission , err
2018-01-18 07:30:04 -06:00
}
2022-07-18 08:14:58 -05:00
func ( g * dashboardGuardianImpl ) checkACL ( permission models . PermissionType , acl [ ] * models . DashboardACLInfoDTO ) ( bool , error ) {
2017-06-22 16:43:55 -05:00
orgRole := g . user . OrgRole
2022-07-18 08:14:58 -05:00
teamACLItems := [ ] * models . DashboardACLInfoDTO { }
2017-06-22 16:43:55 -05:00
2017-06-16 20:25:24 -05:00
for _ , p := range acl {
2017-06-22 16:43:55 -05:00
// user match
2018-06-19 04:10:17 -05:00
if ! g . user . IsAnonymous && p . UserId > 0 {
2022-08-11 06:28:55 -05:00
if p . UserId == g . user . UserID && p . Permission >= permission {
2017-12-15 07:19:49 -06:00
return true , nil
}
2017-06-16 20:25:24 -05:00
}
2017-06-22 16:43:55 -05:00
// role match
if p . Role != nil {
if * p . Role == orgRole && p . Permission >= permission {
2017-06-16 20:25:24 -05:00
return true , nil
}
}
2017-06-21 18:23:24 -05:00
2017-06-22 16:43:55 -05:00
// remember this rule for later
2017-12-08 09:25:45 -06:00
if p . TeamId > 0 {
2022-07-18 08:14:58 -05:00
teamACLItems = append ( teamACLItems , p )
2017-06-22 16:43:55 -05:00
}
}
2018-01-29 06:56:12 -06:00
// do we have team rules?
2022-07-18 08:14:58 -05:00
if len ( teamACLItems ) == 0 {
2017-06-22 16:43:55 -05:00
return false , nil
}
2018-01-29 06:56:12 -06:00
// load teams
2022-03-21 04:49:49 -05:00
teams , err := g . getTeams ( )
2017-06-22 16:43:55 -05:00
if err != nil {
return false , err
}
2018-04-13 11:40:14 -05:00
// evaluate team rules
2017-06-22 16:43:55 -05:00
for _ , p := range acl {
2017-12-08 09:25:45 -06:00
for _ , ug := range teams {
if ug . Id == p . TeamId && p . Permission >= permission {
2017-06-21 18:23:24 -05:00
return true , nil
}
}
2017-06-16 20:25:24 -05:00
}
return false , nil
}
2022-07-18 08:14:58 -05:00
func ( g * dashboardGuardianImpl ) CheckPermissionBeforeUpdate ( permission models . PermissionType , updatePermissions [ ] * models . DashboardACL ) ( bool , error ) {
acl := [ ] * models . DashboardACLInfoDTO { }
2022-08-10 04:56:48 -05:00
adminRole := org . RoleAdmin
2022-07-18 08:14:58 -05:00
everyoneWithAdminRole := & models . DashboardACLInfoDTO { DashboardId : g . dashId , UserId : 0 , TeamId : 0 , Role : & adminRole , Permission : models . PERMISSION_ADMIN }
2018-01-18 07:30:04 -06:00
2018-02-28 01:48:28 -06:00
// validate that duplicate permissions don't exists
2018-01-18 07:30:04 -06:00
for _ , p := range updatePermissions {
2022-07-18 08:14:58 -05:00
aclItem := & models . DashboardACLInfoDTO { DashboardId : p . DashboardID , UserId : p . UserID , TeamId : p . TeamID , Role : p . Role , Permission : p . Permission }
2018-02-28 01:48:28 -06:00
if aclItem . IsDuplicateOf ( everyoneWithAdminRole ) {
return false , ErrGuardianPermissionExists
}
2018-02-26 12:12:01 -06:00
for _ , a := range acl {
2018-02-28 01:48:28 -06:00
if a . IsDuplicateOf ( aclItem ) {
2018-02-27 09:03:11 -06:00
return false , ErrGuardianPermissionExists
2018-02-26 12:12:01 -06:00
}
}
2018-02-28 01:48:28 -06:00
acl = append ( acl , aclItem )
2018-02-26 12:12:01 -06:00
}
2022-07-18 08:14:58 -05:00
existingPermissions , err := g . GetACL ( )
2018-02-26 12:12:01 -06:00
if err != nil {
return false , err
}
2018-02-28 01:48:28 -06:00
// validate overridden permissions to be higher
2018-02-26 12:12:01 -06:00
for _ , a := range acl {
for _ , existingPerm := range existingPermissions {
2018-04-23 02:23:14 -05:00
if ! existingPerm . Inherited {
2018-02-26 12:12:01 -06:00
continue
}
2018-02-28 01:48:28 -06:00
if a . IsDuplicateOf ( existingPerm ) && a . Permission <= existingPerm . Permission {
2018-02-27 09:03:11 -06:00
return false , ErrGuardianOverride
2018-02-26 12:12:01 -06:00
}
}
}
2022-08-10 04:56:48 -05:00
if g . user . OrgRole == org . RoleAdmin {
2018-02-26 12:12:01 -06:00
return true , nil
2018-01-18 07:30:04 -06:00
}
2022-07-18 08:14:58 -05:00
return g . checkACL ( permission , existingPermissions )
2018-01-18 07:30:04 -06:00
}
2022-07-18 08:14:58 -05:00
// GetACL returns dashboard acl
func ( g * dashboardGuardianImpl ) GetACL ( ) ( [ ] * models . DashboardACLInfoDTO , error ) {
2017-06-16 20:25:24 -05:00
if g . acl != nil {
return g . acl , nil
}
2022-07-18 08:14:58 -05:00
query := models . GetDashboardACLInfoListQuery { DashboardID : g . dashId , OrgID : g . orgId }
if err := g . dashboardService . GetDashboardACLInfoList ( g . ctx , & query ) ; err != nil {
2017-06-16 20:25:24 -05:00
return nil , err
}
g . acl = query . Result
return g . acl , nil
}
2022-07-18 08:14:58 -05:00
func ( g * dashboardGuardianImpl ) GetACLWithoutDuplicates ( ) ( [ ] * models . DashboardACLInfoDTO , error ) {
acl , err := g . GetACL ( )
2021-04-28 07:42:18 -05:00
if err != nil {
return nil , err
}
2022-07-18 08:14:58 -05:00
nonInherited := [ ] * models . DashboardACLInfoDTO { }
inherited := [ ] * models . DashboardACLInfoDTO { }
2021-04-28 07:42:18 -05:00
for _ , aclItem := range acl {
if aclItem . Inherited {
inherited = append ( inherited , aclItem )
} else {
nonInherited = append ( nonInherited , aclItem )
}
}
2022-07-18 08:14:58 -05:00
result := [ ] * models . DashboardACLInfoDTO { }
for _ , nonInheritedACLItem := range nonInherited {
2021-04-28 07:42:18 -05:00
duplicate := false
2022-07-18 08:14:58 -05:00
for _ , inheritedACLItem := range inherited {
if nonInheritedACLItem . IsDuplicateOf ( inheritedACLItem ) && nonInheritedACLItem . Permission <= inheritedACLItem . Permission {
2021-04-28 07:42:18 -05:00
duplicate = true
break
}
}
if ! duplicate {
2022-07-18 08:14:58 -05:00
result = append ( result , nonInheritedACLItem )
2021-04-28 07:42:18 -05:00
}
}
result = append ( inherited , result ... )
return result , nil
}
2022-03-21 04:49:49 -05:00
func ( g * dashboardGuardianImpl ) getTeams ( ) ( [ ] * models . TeamDTO , error ) {
2018-07-11 13:23:07 -05:00
if g . teams != nil {
return g . teams , nil
2017-06-16 20:25:24 -05:00
}
2022-08-11 06:28:55 -05:00
query := models . GetTeamsByUserQuery { OrgId : g . orgId , UserId : g . user . UserID , SignedInUser : g . user }
2022-09-20 11:58:04 -05:00
err := g . teamService . GetTeamsByUser ( g . ctx , & query )
2017-06-16 20:25:24 -05:00
2018-07-11 13:23:07 -05:00
g . teams = query . Result
2017-06-16 20:25:24 -05:00
return query . Result , err
}
2018-02-20 11:08:19 -06:00
2022-07-18 08:14:58 -05:00
func ( g * dashboardGuardianImpl ) GetHiddenACL ( cfg * setting . Cfg ) ( [ ] * models . DashboardACL , error ) {
hiddenACL := make ( [ ] * models . DashboardACL , 0 )
2020-11-24 05:10:32 -06:00
if g . user . IsGrafanaAdmin {
return hiddenACL , nil
}
2022-07-18 08:14:58 -05:00
existingPermissions , err := g . GetACL ( )
2020-11-24 05:10:32 -06:00
if err != nil {
return hiddenACL , err
}
for _ , item := range existingPermissions {
if item . Inherited || item . UserLogin == g . user . Login {
continue
}
if _ , hidden := cfg . HiddenUsers [ item . UserLogin ] ; hidden {
2022-07-18 08:14:58 -05:00
hiddenACL = append ( hiddenACL , & models . DashboardACL {
2020-11-24 05:10:32 -06:00
OrgID : item . OrgId ,
DashboardID : item . DashboardId ,
UserID : item . UserId ,
TeamID : item . TeamId ,
Role : item . Role ,
Permission : item . Permission ,
Created : item . Created ,
Updated : item . Updated ,
} )
}
}
return hiddenACL , nil
}
2020-11-17 04:51:31 -06:00
// nolint:unused
2018-02-20 11:08:19 -06:00
type FakeDashboardGuardian struct {
2022-12-15 08:34:17 -06:00
DashID int64
DashUID string
2018-02-20 11:08:19 -06:00
OrgId int64
2022-08-10 04:56:48 -05:00
User * user . SignedInUser
2018-02-20 11:08:19 -06:00
CanSaveValue bool
CanEditValue bool
CanViewValue bool
CanAdminValue bool
HasPermissionValue bool
CheckPermissionBeforeUpdateValue bool
2018-02-26 12:12:01 -06:00
CheckPermissionBeforeUpdateError error
2022-07-18 08:14:58 -05:00
GetACLValue [ ] * models . DashboardACLInfoDTO
GetHiddenACLValue [ ] * models . DashboardACL
2018-02-20 11:08:19 -06:00
}
func ( g * FakeDashboardGuardian ) CanSave ( ) ( bool , error ) {
return g . CanSaveValue , nil
}
func ( g * FakeDashboardGuardian ) CanEdit ( ) ( bool , error ) {
return g . CanEditValue , nil
}
func ( g * FakeDashboardGuardian ) CanView ( ) ( bool , error ) {
return g . CanViewValue , nil
}
func ( g * FakeDashboardGuardian ) CanAdmin ( ) ( bool , error ) {
return g . CanAdminValue , nil
}
2022-03-03 08:05:47 -06:00
func ( g * FakeDashboardGuardian ) CanDelete ( ) ( bool , error ) {
return g . CanSaveValue , nil
}
func ( g * FakeDashboardGuardian ) CanCreate ( _ int64 , _ bool ) ( bool , error ) {
return g . CanSaveValue , nil
}
2020-02-29 06:35:15 -06:00
func ( g * FakeDashboardGuardian ) HasPermission ( permission models . PermissionType ) ( bool , error ) {
2018-02-20 11:08:19 -06:00
return g . HasPermissionValue , nil
}
2022-07-18 08:14:58 -05:00
func ( g * FakeDashboardGuardian ) CheckPermissionBeforeUpdate ( permission models . PermissionType , updatePermissions [ ] * models . DashboardACL ) ( bool , error ) {
2018-02-26 12:12:01 -06:00
return g . CheckPermissionBeforeUpdateValue , g . CheckPermissionBeforeUpdateError
2018-02-20 11:08:19 -06:00
}
2022-07-18 08:14:58 -05:00
func ( g * FakeDashboardGuardian ) GetACL ( ) ( [ ] * models . DashboardACLInfoDTO , error ) {
return g . GetACLValue , nil
2018-02-20 11:08:19 -06:00
}
2022-07-18 08:14:58 -05:00
func ( g * FakeDashboardGuardian ) GetACLWithoutDuplicates ( ) ( [ ] * models . DashboardACLInfoDTO , error ) {
return g . GetACL ( )
2021-04-28 07:42:18 -05:00
}
2022-07-18 08:14:58 -05:00
func ( g * FakeDashboardGuardian ) GetHiddenACL ( cfg * setting . Cfg ) ( [ ] * models . DashboardACL , error ) {
return g . GetHiddenACLValue , nil
2020-11-24 05:10:32 -06:00
}
2020-11-17 04:51:31 -06:00
// nolint:unused
2018-02-20 11:08:19 -06:00
func MockDashboardGuardian ( mock * FakeDashboardGuardian ) {
2022-12-15 08:34:17 -06:00
New = func ( _ context . Context , dashID int64 , orgId int64 , user * user . SignedInUser ) ( DashboardGuardian , error ) {
mock . OrgId = orgId
mock . DashID = dashID
mock . User = user
return mock , nil
}
NewByUID = func ( _ context . Context , dashUID string , orgId int64 , user * user . SignedInUser ) ( DashboardGuardian , error ) {
mock . OrgId = orgId
mock . DashUID = dashUID
mock . User = user
return mock , nil
}
NewByDashboard = func ( _ context . Context , dash * models . Dashboard , orgId int64 , user * user . SignedInUser ) ( DashboardGuardian , error ) {
2018-02-20 11:08:19 -06:00
mock . OrgId = orgId
2022-12-15 08:34:17 -06:00
mock . DashUID = dash . Uid
mock . DashID = dash . Id
2018-02-20 11:08:19 -06:00
mock . User = user
2022-12-15 08:34:17 -06:00
return mock , nil
2018-02-20 11:08:19 -06:00
}
}