2014-12-22 12:25:08 +01:00
package sqlstore
import (
2021-03-17 16:06:10 +01:00
"context"
2020-04-27 14:16:03 +02:00
"strings"
2015-02-04 11:35:59 +01:00
2022-03-03 15:05:47 +01:00
"github.com/prometheus/client_golang/prometheus"
2015-02-05 10:37:13 +01:00
"github.com/grafana/grafana/pkg/bus"
2019-08-12 20:03:48 +02:00
"github.com/grafana/grafana/pkg/models"
2022-03-16 10:07:04 -04:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2022-02-16 14:15:44 +01:00
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
2018-01-31 17:27:28 +01:00
"github.com/grafana/grafana/pkg/util"
2014-12-22 12:25:08 +01:00
)
2020-04-20 16:20:45 +02:00
var shadowSearchCounter = prometheus . NewCounterVec (
prometheus . CounterOpts {
Subsystem : "db_dashboard" ,
Name : "search_shadow" ,
} ,
[ ] string { "equal" , "error" } ,
)
2014-12-22 12:25:08 +01:00
func init ( ) {
2020-04-20 16:20:45 +02:00
prometheus . MustRegister ( shadowSearchCounter )
2014-12-22 12:25:08 +01:00
}
2021-07-16 12:40:03 +02:00
func ( ss * SQLStore ) addDashboardQueryAndCommandHandlers ( ) {
2022-02-04 17:21:06 +01:00
bus . AddHandler ( "sql" , ss . GetDashboard )
2021-12-28 16:08:07 +01:00
bus . AddHandler ( "sql" , ss . GetDashboardUIDById )
2022-02-07 12:43:43 +01:00
bus . AddHandler ( "sql" , ss . GetDashboardTags )
2021-12-28 16:08:07 +01:00
bus . AddHandler ( "sql" , ss . SearchDashboards )
2022-01-27 10:33:02 +01:00
bus . AddHandler ( "sql" , ss . GetDashboards )
2022-02-03 12:22:21 -05:00
bus . AddHandler ( "sql" , ss . HasEditPermissionInFolders )
2022-02-17 10:59:09 -05:00
bus . AddHandler ( "sql" , ss . GetDashboardPermissionsForUser )
bus . AddHandler ( "sql" , ss . GetDashboardSlugById )
bus . AddHandler ( "sql" , ss . HasAdminPermissionInFolders )
2021-07-16 12:40:03 +02:00
}
2019-01-28 22:37:44 +01:00
var generateNewUid func ( ) string = util . GenerateShortUID
2018-01-31 17:27:28 +01:00
2022-02-04 17:21:06 +01:00
func ( ss * SQLStore ) GetDashboard ( ctx context . Context , query * models . GetDashboardQuery ) error {
2022-02-17 10:59:09 -05:00
return ss . WithDbSession ( ctx , func ( dbSession * DBSession ) error {
2021-06-15 16:08:27 +02:00
if query . Id == 0 && len ( query . Slug ) == 0 && len ( query . Uid ) == 0 {
return models . ErrDashboardIdentifierNotSet
}
2017-06-05 17:45:27 +02:00
2021-06-15 16:08:27 +02:00
dashboard := models . Dashboard { Slug : query . Slug , OrgId : query . OrgId , Id : query . Id , Uid : query . Uid }
has , err := dbSession . Get ( & dashboard )
2014-12-22 12:25:08 +01:00
2021-06-15 16:08:27 +02:00
if err != nil {
return err
} else if ! has {
return models . ErrDashboardNotFound
}
dashboard . SetId ( dashboard . Id )
dashboard . SetUid ( dashboard . Uid )
query . Result = & dashboard
return nil
} )
2014-12-22 12:25:08 +01:00
}
2015-01-07 12:37:24 +01:00
type DashboardSearchProjection struct {
2021-02-11 08:49:16 +01:00
ID int64 ` xorm:"id" `
UID string ` xorm:"uid" `
2017-06-17 03:00:13 +02:00
Title string
Slug string
Term string
IsFolder bool
2021-02-11 08:49:16 +01:00
FolderID int64 ` xorm:"folder_id" `
FolderUID string ` xorm:"folder_uid" `
2017-06-17 03:00:13 +02:00
FolderSlug string
FolderTitle string
2021-02-11 08:49:16 +01:00
SortMeta int64
2015-01-07 12:37:24 +01:00
}
2022-03-21 16:54:30 +01:00
func ( ss * SQLStore ) FindDashboards ( ctx context . Context , query * models . FindPersistedDashboardsQuery ) ( [ ] DashboardSearchProjection , error ) {
2020-04-27 14:16:03 +02:00
filters := [ ] interface { } {
2020-04-20 16:20:45 +02:00
permissions . DashboardPermissionFilter {
OrgRole : query . SignedInUser . OrgRole ,
OrgId : query . SignedInUser . OrgId ,
Dialect : dialect ,
UserId : query . SignedInUser . UserId ,
PermissionLevel : query . Permission ,
} ,
}
2022-03-16 10:07:04 -04:00
if ss . Cfg . IsFeatureToggleEnabled ( featuremgmt . FlagAccesscontrol ) {
// if access control is enabled, overwrite the filters so far
2022-03-03 15:05:47 +01:00
filters = [ ] interface { } {
2022-03-16 10:07:04 -04:00
permissions . NewAccessControlDashboardPermissionFilter ( query . SignedInUser , query . Permission , query . Type ) ,
2022-03-03 15:05:47 +01:00
}
}
2021-02-11 08:49:16 +01:00
for _ , filter := range query . Sort . Filter {
filters = append ( filters , filter )
}
2020-05-06 11:42:52 +02:00
2022-03-18 17:38:32 -04:00
filters = append ( filters , query . Filters ... )
2020-04-27 14:16:03 +02:00
if query . OrgId != 0 {
filters = append ( filters , searchstore . OrgFilter { OrgId : query . OrgId } )
} else if query . SignedInUser . OrgId != 0 {
filters = append ( filters , searchstore . OrgFilter { OrgId : query . SignedInUser . OrgId } )
}
2020-04-20 16:20:45 +02:00
if len ( query . Tags ) > 0 {
2020-04-27 14:16:03 +02:00
filters = append ( filters , searchstore . TagsFilter { Tags : query . Tags } )
2020-04-20 16:20:45 +02:00
}
if len ( query . DashboardIds ) > 0 {
2020-04-27 14:16:03 +02:00
filters = append ( filters , searchstore . DashboardFilter { IDs : query . DashboardIds } )
2020-04-20 16:20:45 +02:00
}
2015-02-04 11:35:59 +01:00
if query . IsStarred {
2020-04-27 14:16:03 +02:00
filters = append ( filters , searchstore . StarredFilter { UserId : query . SignedInUser . UserId } )
2017-06-17 02:33:53 +02:00
}
2015-02-04 11:35:59 +01:00
if len ( query . Title ) > 0 {
2020-04-27 14:16:03 +02:00
filters = append ( filters , searchstore . TitleFilter { Dialect : dialect , Title : query . Title } )
2015-02-04 11:35:59 +01:00
}
2014-12-22 12:25:08 +01:00
2017-11-17 00:16:33 +01:00
if len ( query . Type ) > 0 {
2020-04-27 14:16:03 +02:00
filters = append ( filters , searchstore . TypeFilter { Dialect : dialect , Type : query . Type } )
2017-05-24 18:28:13 +02:00
}
2017-11-20 12:47:03 +01:00
if len ( query . FolderIds ) > 0 {
2022-01-25 15:04:26 +01:00
filters = append ( filters , searchstore . FolderFilter { IDs : query . FolderIds } )
2017-11-17 15:30:21 +01:00
}
2017-11-17 00:16:33 +01:00
var res [ ] DashboardSearchProjection
2020-04-27 14:16:03 +02:00
sb := & searchstore . Builder { Dialect : dialect , Filters : filters }
2017-11-17 00:16:33 +01:00
2020-04-27 14:16:03 +02:00
limit := query . Limit
if limit < 1 {
limit = 1000
2017-06-01 23:30:31 +02:00
}
2020-04-27 14:16:03 +02:00
page := query . Page
if page < 1 {
page = 1
}
2020-04-20 16:20:45 +02:00
2020-11-11 06:21:08 +01:00
sql , params := sb . ToSQL ( limit , page )
2021-10-28 11:29:07 +02:00
err := ss . WithDbSession ( ctx , func ( dbSession * DBSession ) error {
return dbSession . SQL ( sql , params ... ) . Find ( & res )
} )
2020-04-27 14:16:03 +02:00
if err != nil {
return nil , err
2020-04-20 16:20:45 +02:00
}
2017-11-17 00:16:33 +01:00
return res , nil
2017-03-27 14:36:28 +02:00
}
2022-03-21 16:54:30 +01:00
func ( ss * SQLStore ) SearchDashboards ( ctx context . Context , query * models . FindPersistedDashboardsQuery ) error {
2022-02-16 14:15:44 +01:00
res , err := ss . FindDashboards ( ctx , query )
2015-01-07 12:37:24 +01:00
if err != nil {
return err
}
2017-06-01 23:30:31 +02:00
makeQueryResult ( query , res )
2015-01-07 12:37:24 +01:00
2017-06-01 23:30:31 +02:00
return nil
2014-12-22 12:25:08 +01:00
}
2022-03-21 16:54:30 +01:00
func getHitType ( item DashboardSearchProjection ) models . HitType {
var hitType models . HitType
2017-03-27 14:36:28 +02:00
if item . IsFolder {
2022-03-21 16:54:30 +01:00
hitType = models . DashHitFolder
2017-03-27 14:36:28 +02:00
} else {
2022-03-21 16:54:30 +01:00
hitType = models . DashHitDB
2017-03-27 14:36:28 +02:00
}
return hitType
}
2022-03-21 16:54:30 +01:00
func makeQueryResult ( query * models . FindPersistedDashboardsQuery , res [ ] DashboardSearchProjection ) {
query . Result = make ( [ ] * models . Hit , 0 )
hits := make ( map [ int64 ] * models . Hit )
2017-06-01 23:30:31 +02:00
for _ , item := range res {
2021-02-11 08:49:16 +01:00
hit , exists := hits [ item . ID ]
2017-06-01 23:30:31 +02:00
if ! exists {
2022-03-21 16:54:30 +01:00
hit = & models . Hit {
2021-02-11 08:49:16 +01:00
ID : item . ID ,
UID : item . UID ,
2017-06-23 16:00:26 -04:00
Title : item . Title ,
2021-02-11 08:49:16 +01:00
URI : "db/" + item . Slug ,
URL : models . GetDashboardFolderUrl ( item . IsFolder , item . UID , item . Slug ) ,
2017-06-23 16:00:26 -04:00
Type : getHitType ( item ) ,
2021-02-11 08:49:16 +01:00
FolderID : item . FolderID ,
FolderUID : item . FolderUID ,
2017-06-23 16:00:26 -04:00
FolderTitle : item . FolderTitle ,
Tags : [ ] string { } ,
2017-06-01 23:30:31 +02:00
}
2018-01-30 15:24:14 +01:00
2021-02-11 08:49:16 +01:00
if item . FolderID > 0 {
hit . FolderURL = models . GetFolderUrl ( item . FolderUID , item . FolderSlug )
}
if query . Sort . MetaName != "" {
2021-02-17 14:06:19 +02:00
hit . SortMeta = item . SortMeta
hit . SortMetaName = query . Sort . MetaName
2018-02-05 13:23:24 +01:00
}
2017-06-01 23:30:31 +02:00
query . Result = append ( query . Result , hit )
2021-02-11 08:49:16 +01:00
hits [ item . ID ] = hit
2017-06-01 23:30:31 +02:00
}
if len ( item . Term ) > 0 {
hit . Tags = append ( hit . Tags , item . Term )
}
}
}
2022-02-07 12:43:43 +01:00
func ( ss * SQLStore ) GetDashboardTags ( ctx context . Context , query * models . GetDashboardTagsQuery ) error {
2022-02-17 10:59:09 -05:00
return ss . WithDbSession ( ctx , func ( dbSession * DBSession ) error {
sql := ` SELECT
2015-01-20 15:23:14 +01:00
COUNT ( * ) as count ,
term
FROM dashboard
INNER JOIN dashboard_tag on dashboard_tag . dashboard_id = dashboard . id
2015-02-23 20:07:49 +01:00
WHERE dashboard . org_id = ?
2018-04-20 12:17:17 -04:00
GROUP BY term
ORDER BY term `
2015-01-20 15:23:14 +01:00
2022-02-17 10:59:09 -05:00
query . Result = make ( [ ] * models . DashboardTagCloudItem , 0 )
sess := dbSession . SQL ( sql , query . OrgId )
err := sess . Find ( & query . Result )
return err
} )
2015-01-06 18:39:26 +01:00
}
2022-01-27 10:33:02 +01:00
func ( ss * SQLStore ) GetDashboards ( ctx context . Context , query * models . GetDashboardsQuery ) error {
2022-02-17 10:59:09 -05:00
return ss . WithDbSession ( ctx , func ( dbSession * DBSession ) error {
if len ( query . DashboardIds ) == 0 {
return models . ErrCommandValidationFailed
}
2016-01-29 01:41:23 +01:00
2022-02-17 10:59:09 -05:00
var dashboards = make ( [ ] * models . Dashboard , 0 )
2016-01-29 01:41:23 +01:00
2022-02-17 10:59:09 -05:00
err := dbSession . In ( "id" , query . DashboardIds ) . Find ( & dashboards )
query . Result = dashboards
return err
} )
2016-01-29 01:41:23 +01:00
}
2016-03-17 01:01:58 -07:00
2018-01-30 23:31:02 +01:00
// GetDashboardPermissionsForUser returns the maximum permission the specified user has for a dashboard(s)
// The function takes in a list of dashboard ids and the user id and role
2022-02-17 10:59:09 -05:00
func ( ss * SQLStore ) GetDashboardPermissionsForUser ( ctx context . Context , query * models . GetDashboardPermissionsForUserQuery ) error {
return ss . WithDbSession ( ctx , func ( dbSession * DBSession ) error {
if len ( query . DashboardIds ) == 0 {
return models . ErrCommandValidationFailed
2018-01-30 23:31:02 +01:00
}
2022-02-17 10:59:09 -05:00
if query . OrgRole == models . ROLE_ADMIN {
var permissions = make ( [ ] * models . DashboardPermissionForUser , 0 )
for _ , d := range query . DashboardIds {
permissions = append ( permissions , & models . DashboardPermissionForUser {
DashboardId : d ,
Permission : models . PERMISSION_ADMIN ,
PermissionName : models . PERMISSION_ADMIN . String ( ) ,
} )
}
query . Result = permissions
2018-01-30 23:31:02 +01:00
2022-02-17 10:59:09 -05:00
return nil
}
2018-01-30 23:31:02 +01:00
2022-02-17 10:59:09 -05:00
params := make ( [ ] interface { } , 0 )
// check dashboards that have ACLs via user id, team id or role
sql := ` SELECT d . id AS dashboard_id , MAX ( COALESCE ( da . permission , pt . permission ) ) AS permission
2018-01-30 23:31:02 +01:00
FROM dashboard AS d
LEFT JOIN dashboard_acl as da on d . folder_id = da . dashboard_id or d . id = da . dashboard_id
LEFT JOIN team_member as ugm on ugm . team_id = da . team_id
LEFT JOIN org_user ou ON ou . role = da . role AND ou . user_id = ?
`
2022-02-17 10:59:09 -05:00
params = append ( params , query . UserId )
2018-01-30 23:31:02 +01:00
2022-02-17 10:59:09 -05:00
// check the user's role for dashboards that do not have hasAcl set
sql += ` LEFT JOIN org_user ouRole ON ouRole.user_id = ? AND ouRole.org_id = ? `
params = append ( params , query . UserId )
params = append ( params , query . OrgId )
2018-01-30 23:31:02 +01:00
2022-02-17 10:59:09 -05:00
sql += `
2018-02-01 17:27:29 +01:00
LEFT JOIN ( SELECT 1 AS permission , ' Viewer ' AS role
UNION SELECT 2 AS permission , ' Editor ' AS role
UNION SELECT 4 AS permission , ' Admin ' AS role ) pt ON ouRole . role = pt . role
2018-01-30 23:31:02 +01:00
WHERE
d . Id IN ( ? ` + strings.Repeat(",?", len(query.DashboardIds)-1) + ` ) `
2022-02-17 10:59:09 -05:00
for _ , id := range query . DashboardIds {
params = append ( params , id )
}
2018-01-30 23:31:02 +01:00
2022-02-17 10:59:09 -05:00
sql += ` AND
2018-01-30 23:31:02 +01:00
d . org_id = ? AND
(
( d . has_acl = ? AND ( da . user_id = ? OR ugm . user_id = ? OR ou . id IS NOT NULL ) )
OR ( d . has_acl = ? AND ouRole . id IS NOT NULL )
)
group by d . id
order by d . id asc `
2022-02-17 10:59:09 -05:00
params = append ( params , query . OrgId )
params = append ( params , dialect . BooleanStr ( true ) )
params = append ( params , query . UserId )
params = append ( params , query . UserId )
params = append ( params , dialect . BooleanStr ( false ) )
2018-01-30 23:31:02 +01:00
2022-02-17 10:59:09 -05:00
err := dbSession . SQL ( sql , params ... ) . Find ( & query . Result )
2018-01-30 23:31:02 +01:00
2022-02-17 10:59:09 -05:00
for _ , p := range query . Result {
p . PermissionName = p . Permission . String ( )
}
2018-01-30 23:31:02 +01:00
2022-02-17 10:59:09 -05:00
return err
} )
2018-01-30 23:31:02 +01:00
}
2016-03-20 11:52:19 +01:00
type DashboardSlugDTO struct {
Slug string
}
2022-02-17 10:59:09 -05:00
func ( ss * SQLStore ) GetDashboardSlugById ( ctx context . Context , query * models . GetDashboardSlugByIdQuery ) error {
return ss . WithDbSession ( ctx , func ( dbSession * DBSession ) error {
var rawSQL = ` SELECT slug from dashboard WHERE Id=? `
var slug = DashboardSlugDTO { }
2016-03-20 11:52:19 +01:00
2022-02-17 10:59:09 -05:00
exists , err := dbSession . SQL ( rawSQL , query . Id ) . Get ( & slug )
2016-03-17 01:01:58 -07:00
2022-02-17 10:59:09 -05:00
if err != nil {
return err
} else if ! exists {
return models . ErrDashboardNotFound
}
2016-03-17 01:01:58 -07:00
2022-02-17 10:59:09 -05:00
query . Result = slug . Slug
return nil
} )
2016-03-17 01:01:58 -07:00
}
2018-01-31 16:51:06 +01:00
2021-07-16 12:40:03 +02:00
func ( ss * SQLStore ) GetDashboardUIDById ( ctx context . Context , query * models . GetDashboardRefByIdQuery ) error {
return ss . WithDbSession ( ctx , func ( dbSession * DBSession ) error {
var rawSQL = ` SELECT uid, slug from dashboard WHERE Id=? `
2018-02-01 13:32:00 +01:00
2021-07-16 12:40:03 +02:00
us := & models . DashboardRef { }
2018-02-01 13:32:00 +01:00
2021-07-16 12:40:03 +02:00
exists , err := dbSession . SQL ( rawSQL , query . Id ) . Get ( us )
2018-02-01 13:32:00 +01:00
2021-07-16 12:40:03 +02:00
if err != nil {
return err
} else if ! exists {
return models . ErrDashboardNotFound
}
2018-02-01 13:32:00 +01:00
2021-07-16 12:40:03 +02:00
query . Result = us
return nil
} )
2018-02-01 13:32:00 +01:00
}
2018-02-19 11:12:56 +01:00
2021-10-11 14:36:57 +02:00
// HasEditPermissionInFolders validates that an user have access to a certain folder
2022-02-03 12:22:21 -05:00
func ( ss * SQLStore ) HasEditPermissionInFolders ( ctx context . Context , query * models . HasEditPermissionInFoldersQuery ) error {
2022-02-17 10:59:09 -05:00
return ss . WithDbSession ( ctx , func ( dbSession * DBSession ) error {
2021-10-11 14:36:57 +02:00
if query . SignedInUser . HasRole ( models . ROLE_EDITOR ) {
query . Result = true
return nil
}
2018-04-30 15:34:31 +02:00
2021-10-11 14:36:57 +02:00
builder := & SQLBuilder { }
builder . Write ( "SELECT COUNT(dashboard.id) AS count FROM dashboard WHERE dashboard.org_id = ? AND dashboard.is_folder = ?" ,
query . SignedInUser . OrgId , dialect . BooleanStr ( true ) )
builder . WriteDashboardPermissionFilter ( query . SignedInUser , models . PERMISSION_EDIT )
2019-08-12 20:03:48 +02:00
2021-10-11 14:36:57 +02:00
type folderCount struct {
Count int64
}
2019-08-12 20:03:48 +02:00
2021-10-11 14:36:57 +02:00
resp := make ( [ ] * folderCount , 0 )
if err := dbSession . SQL ( builder . GetSQLString ( ) , builder . params ... ) . Find ( & resp ) ; err != nil {
return err
}
2019-08-12 20:03:48 +02:00
2021-10-11 14:36:57 +02:00
query . Result = len ( resp ) > 0 && resp [ 0 ] . Count > 0
2019-08-12 20:03:48 +02:00
2021-10-11 14:36:57 +02:00
return nil
} )
2019-08-12 20:03:48 +02:00
}
2022-02-17 10:59:09 -05:00
func ( ss * SQLStore ) HasAdminPermissionInFolders ( ctx context . Context , query * models . HasAdminPermissionInFoldersQuery ) error {
return ss . WithDbSession ( ctx , func ( dbSession * DBSession ) error {
if query . SignedInUser . HasRole ( models . ROLE_ADMIN ) {
query . Result = true
return nil
}
2019-08-12 20:03:48 +02:00
2022-02-17 10:59:09 -05:00
builder := & SQLBuilder { }
builder . Write ( "SELECT COUNT(dashboard.id) AS count FROM dashboard WHERE dashboard.org_id = ? AND dashboard.is_folder = ?" , query . SignedInUser . OrgId , dialect . BooleanStr ( true ) )
builder . WriteDashboardPermissionFilter ( query . SignedInUser , models . PERMISSION_ADMIN )
2018-04-30 15:34:31 +02:00
2022-02-17 10:59:09 -05:00
type folderCount struct {
Count int64
}
2018-04-30 15:34:31 +02:00
2022-02-17 10:59:09 -05:00
resp := make ( [ ] * folderCount , 0 )
if err := dbSession . SQL ( builder . GetSQLString ( ) , builder . params ... ) . Find ( & resp ) ; err != nil {
return err
}
2018-04-30 15:34:31 +02:00
2022-02-17 10:59:09 -05:00
query . Result = len ( resp ) > 0 && resp [ 0 ] . Count > 0
2018-04-30 15:34:31 +02:00
2022-02-17 10:59:09 -05:00
return nil
} )
2018-04-30 15:34:31 +02:00
}