API: Restrict anonymous user information access (#18422)

Existing /api/alert-notifications now requires at least editor access.
Existing /api/alert-notifiers now requires at least editor access.
New /api/alert-notifications/lookup returns less information than
/api/alert-notifications and can be access by any authenticated user.
Existing /api/org/users now requires org admin role.
New /api/org/users/lookup returns less information than
/api/org/users and can be access by users that are org admins,
admin in any folder or admin of any team.
UserPicker component now uses /api/org/users/lookup instead
of /api/org/users.

Fixes #17318
This commit is contained in:
Marcus Efraimsson
2019-08-12 20:03:48 +02:00
committed by GitHub
parent ab17015794
commit 8fd153edb7
18 changed files with 621 additions and 326 deletions
+86 -61
View File
@@ -6,7 +6,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/metrics"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/util"
)
@@ -25,17 +25,18 @@ func init() {
bus.AddHandler("sql", GetDashboardsBySlug)
bus.AddHandler("sql", ValidateDashboardBeforeSave)
bus.AddHandler("sql", HasEditPermissionInFolders)
bus.AddHandler("sql", HasAdminPermissionInFolders)
}
var generateNewUid func() string = util.GenerateShortUID
func SaveDashboard(cmd *m.SaveDashboardCommand) error {
func SaveDashboard(cmd *models.SaveDashboardCommand) error {
return inTransaction(func(sess *DBSession) error {
return saveDashboard(sess, cmd)
})
}
func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error {
func saveDashboard(sess *DBSession, cmd *models.SaveDashboardCommand) error {
dash := cmd.GetDashboardModel()
userId := cmd.UserId
@@ -45,13 +46,13 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error {
}
if dash.Id > 0 {
var existing m.Dashboard
var existing models.Dashboard
dashWithIdExists, err := sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existing)
if err != nil {
return err
}
if !dashWithIdExists {
return m.ErrDashboardNotFound
return models.ErrDashboardNotFound
}
// check for is someone else has written in between
@@ -59,13 +60,13 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error {
if cmd.Overwrite {
dash.SetVersion(existing.Version)
} else {
return m.ErrDashboardVersionMismatch
return models.ErrDashboardVersionMismatch
}
}
// do not allow plugin dashboard updates without overwrite flag
if existing.PluginId != "" && !cmd.Overwrite {
return m.UpdatePluginDashboardError{PluginId: existing.PluginId}
return models.UpdatePluginDashboardError{PluginId: existing.PluginId}
}
}
@@ -108,10 +109,10 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error {
}
if affectedRows == 0 {
return m.ErrDashboardNotFound
return models.ErrDashboardNotFound
}
dashVersion := &m.DashboardVersion{
dashVersion := &models.DashboardVersion{
DashboardId: dash.Id,
ParentVersion: parentVersion,
RestoredFrom: cmd.RestoredFrom,
@@ -126,7 +127,7 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error {
if affectedRows, err = sess.Insert(dashVersion); err != nil {
return err
} else if affectedRows == 0 {
return m.ErrDashboardNotFound
return models.ErrDashboardNotFound
}
// delete existing tags
@@ -154,7 +155,7 @@ func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
for i := 0; i < 3; i++ {
uid := generateNewUid()
exists, err := sess.Where("org_id=? AND uid=?", orgId, uid).Get(&m.Dashboard{})
exists, err := sess.Where("org_id=? AND uid=?", orgId, uid).Get(&models.Dashboard{})
if err != nil {
return "", err
}
@@ -164,17 +165,17 @@ func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
}
}
return "", m.ErrDashboardFailedGenerateUniqueUid
return "", models.ErrDashboardFailedGenerateUniqueUid
}
func GetDashboard(query *m.GetDashboardQuery) error {
dashboard := m.Dashboard{Slug: query.Slug, OrgId: query.OrgId, Id: query.Id, Uid: query.Uid}
func GetDashboard(query *models.GetDashboardQuery) error {
dashboard := models.Dashboard{Slug: query.Slug, OrgId: query.OrgId, Id: query.Id, Uid: query.Uid}
has, err := x.Get(&dashboard)
if err != nil {
return err
} else if !has {
return m.ErrDashboardNotFound
return models.ErrDashboardNotFound
}
dashboard.SetId(dashboard.Id)
@@ -262,7 +263,7 @@ func makeQueryResult(query *search.FindPersistedDashboardsQuery, res []Dashboard
Uid: item.Uid,
Title: item.Title,
Uri: "db/" + item.Slug,
Url: m.GetDashboardFolderUrl(item.IsFolder, item.Uid, item.Slug),
Url: models.GetDashboardFolderUrl(item.IsFolder, item.Uid, item.Slug),
Type: getHitType(item),
FolderId: item.FolderId,
FolderUid: item.FolderUid,
@@ -271,7 +272,7 @@ func makeQueryResult(query *search.FindPersistedDashboardsQuery, res []Dashboard
}
if item.FolderId > 0 {
hit.FolderUrl = m.GetFolderUrl(item.FolderUid, item.FolderSlug)
hit.FolderUrl = models.GetFolderUrl(item.FolderUid, item.FolderSlug)
}
query.Result = append(query.Result, hit)
@@ -283,7 +284,7 @@ func makeQueryResult(query *search.FindPersistedDashboardsQuery, res []Dashboard
}
}
func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
func GetDashboardTags(query *models.GetDashboardTagsQuery) error {
sql := `SELECT
COUNT(*) as count,
term
@@ -293,20 +294,20 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
GROUP BY term
ORDER BY term`
query.Result = make([]*m.DashboardTagCloudItem, 0)
query.Result = make([]*models.DashboardTagCloudItem, 0)
sess := x.SQL(sql, query.OrgId)
err := sess.Find(&query.Result)
return err
}
func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {
func DeleteDashboard(cmd *models.DeleteDashboardCommand) error {
return inTransaction(func(sess *DBSession) error {
dashboard := m.Dashboard{Id: cmd.Id, OrgId: cmd.OrgId}
dashboard := models.Dashboard{Id: cmd.Id, OrgId: cmd.OrgId}
has, err := sess.Get(&dashboard)
if err != nil {
return err
} else if !has {
return m.ErrDashboardNotFound
return models.ErrDashboardNotFound
}
deletes := []string{
@@ -354,12 +355,12 @@ func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {
})
}
func GetDashboards(query *m.GetDashboardsQuery) error {
func GetDashboards(query *models.GetDashboardsQuery) error {
if len(query.DashboardIds) == 0 {
return m.ErrCommandValidationFailed
return models.ErrCommandValidationFailed
}
var dashboards = make([]*m.Dashboard, 0)
var dashboards = make([]*models.Dashboard, 0)
err := x.In("id", query.DashboardIds).Find(&dashboards)
query.Result = dashboards
@@ -368,18 +369,18 @@ func GetDashboards(query *m.GetDashboardsQuery) error {
// 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
func GetDashboardPermissionsForUser(query *m.GetDashboardPermissionsForUserQuery) error {
func GetDashboardPermissionsForUser(query *models.GetDashboardPermissionsForUserQuery) error {
if len(query.DashboardIds) == 0 {
return m.ErrCommandValidationFailed
return models.ErrCommandValidationFailed
}
if query.OrgRole == m.ROLE_ADMIN {
var permissions = make([]*m.DashboardPermissionForUser, 0)
if query.OrgRole == models.ROLE_ADMIN {
var permissions = make([]*models.DashboardPermissionForUser, 0)
for _, d := range query.DashboardIds {
permissions = append(permissions, &m.DashboardPermissionForUser{
permissions = append(permissions, &models.DashboardPermissionForUser{
DashboardId: d,
Permission: m.PERMISSION_ADMIN,
PermissionName: m.PERMISSION_ADMIN.String(),
Permission: models.PERMISSION_ADMIN,
PermissionName: models.PERMISSION_ADMIN.String(),
})
}
query.Result = permissions
@@ -436,8 +437,8 @@ func GetDashboardPermissionsForUser(query *m.GetDashboardPermissionsForUserQuery
return err
}
func GetDashboardsByPluginId(query *m.GetDashboardsByPluginIdQuery) error {
var dashboards = make([]*m.Dashboard, 0)
func GetDashboardsByPluginId(query *models.GetDashboardsByPluginIdQuery) error {
var dashboards = make([]*models.Dashboard, 0)
whereExpr := "org_id=? AND plugin_id=? AND is_folder=" + dialect.BooleanStr(false)
err := x.Where(whereExpr, query.OrgId, query.PluginId).Find(&dashboards)
@@ -449,7 +450,7 @@ type DashboardSlugDTO struct {
Slug string
}
func GetDashboardSlugById(query *m.GetDashboardSlugByIdQuery) error {
func GetDashboardSlugById(query *models.GetDashboardSlugByIdQuery) error {
var rawSql = `SELECT slug from dashboard WHERE Id=?`
var slug = DashboardSlugDTO{}
@@ -458,15 +459,15 @@ func GetDashboardSlugById(query *m.GetDashboardSlugByIdQuery) error {
if err != nil {
return err
} else if !exists {
return m.ErrDashboardNotFound
return models.ErrDashboardNotFound
}
query.Result = slug.Slug
return nil
}
func GetDashboardsBySlug(query *m.GetDashboardsBySlugQuery) error {
var dashboards []*m.Dashboard
func GetDashboardsBySlug(query *models.GetDashboardsBySlugQuery) error {
var dashboards []*models.Dashboard
if err := x.Where("org_id=? AND slug=?", query.OrgId, query.Slug).Find(&dashboards); err != nil {
return err
@@ -476,28 +477,28 @@ func GetDashboardsBySlug(query *m.GetDashboardsBySlugQuery) error {
return nil
}
func GetDashboardUIDById(query *m.GetDashboardRefByIdQuery) error {
func GetDashboardUIDById(query *models.GetDashboardRefByIdQuery) error {
var rawSql = `SELECT uid, slug from dashboard WHERE Id=?`
us := &m.DashboardRef{}
us := &models.DashboardRef{}
exists, err := x.SQL(rawSql, query.Id).Get(us)
if err != nil {
return err
} else if !exists {
return m.ErrDashboardNotFound
return models.ErrDashboardNotFound
}
query.Result = us
return nil
}
func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDashboardBeforeSaveCommand) (err error) {
func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *models.ValidateDashboardBeforeSaveCommand) (err error) {
dash := cmd.Dashboard
dashWithIdExists := false
var existingById m.Dashboard
var existingById models.Dashboard
if dash.Id > 0 {
dashWithIdExists, err = sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existingById)
@@ -506,7 +507,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
}
if !dashWithIdExists {
return m.ErrDashboardNotFound
return models.ErrDashboardNotFound
}
if dash.Uid == "" {
@@ -515,7 +516,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
}
dashWithUidExists := false
var existingByUid m.Dashboard
var existingByUid models.Dashboard
if dash.Uid != "" {
dashWithUidExists, err = sess.Where("org_id=? AND uid=?", dash.OrgId, dash.Uid).Get(&existingByUid)
@@ -525,14 +526,14 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
}
if dash.FolderId > 0 {
var existingFolder m.Dashboard
var existingFolder models.Dashboard
folderExists, folderErr := sess.Where("org_id=? AND id=? AND is_folder=?", dash.OrgId, dash.FolderId, dialect.BooleanStr(true)).Get(&existingFolder)
if folderErr != nil {
return folderErr
}
if !folderExists {
return m.ErrDashboardFolderNotFound
return models.ErrDashboardFolderNotFound
}
}
@@ -541,7 +542,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
}
if dashWithIdExists && dashWithUidExists && existingById.Id != existingByUid.Id {
return m.ErrDashboardWithSameUIDExists
return models.ErrDashboardWithSameUIDExists
}
existing := existingById
@@ -558,7 +559,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
if (existing.IsFolder && !dash.IsFolder) ||
(!existing.IsFolder && dash.IsFolder) {
return m.ErrDashboardTypeMismatch
return models.ErrDashboardTypeMismatch
}
if !dash.IsFolder && dash.FolderId != existing.FolderId {
@@ -570,21 +571,21 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
if cmd.Overwrite {
dash.SetVersion(existing.Version)
} else {
return m.ErrDashboardVersionMismatch
return models.ErrDashboardVersionMismatch
}
}
// do not allow plugin dashboard updates without overwrite flag
if existing.PluginId != "" && !cmd.Overwrite {
return m.UpdatePluginDashboardError{PluginId: existing.PluginId}
return models.UpdatePluginDashboardError{PluginId: existing.PluginId}
}
return nil
}
func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *m.ValidateDashboardBeforeSaveCommand) error {
func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *models.ValidateDashboardBeforeSaveCommand) error {
dash := cmd.Dashboard
var existing m.Dashboard
var existing models.Dashboard
exists, err := sess.Where("org_id=? AND slug=? AND (is_folder=? OR folder_id=?)", dash.OrgId, dash.Slug, dialect.BooleanStr(true), dash.FolderId).Get(&existing)
if err != nil {
@@ -593,11 +594,11 @@ func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *m.ValidateDashbo
if exists && dash.Id != existing.Id {
if existing.IsFolder && !dash.IsFolder {
return m.ErrDashboardWithSameNameAsFolder
return models.ErrDashboardWithSameNameAsFolder
}
if !existing.IsFolder && dash.IsFolder {
return m.ErrDashboardFolderWithSameNameAsDashboard
return models.ErrDashboardFolderWithSameNameAsDashboard
}
if !dash.IsFolder && (dash.FolderId != existing.FolderId || dash.Id == 0) {
@@ -609,15 +610,15 @@ func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *m.ValidateDashbo
dash.SetUid(existing.Uid)
dash.SetVersion(existing.Version)
} else {
return m.ErrDashboardWithSameNameInFolderExists
return models.ErrDashboardWithSameNameInFolderExists
}
}
return nil
}
func ValidateDashboardBeforeSave(cmd *m.ValidateDashboardBeforeSaveCommand) (err error) {
cmd.Result = &m.ValidateDashboardBeforeSaveResult{}
func ValidateDashboardBeforeSave(cmd *models.ValidateDashboardBeforeSaveCommand) (err error) {
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
return inTransaction(func(sess *DBSession) error {
if err = getExistingDashboardByIdOrUidForUpdate(sess, cmd); err != nil {
return err
@@ -631,15 +632,39 @@ func ValidateDashboardBeforeSave(cmd *m.ValidateDashboardBeforeSaveCommand) (err
})
}
func HasEditPermissionInFolders(query *m.HasEditPermissionInFoldersQuery) error {
if query.SignedInUser.HasRole(m.ROLE_EDITOR) {
func HasEditPermissionInFolders(query *models.HasEditPermissionInFoldersQuery) error {
if query.SignedInUser.HasRole(models.ROLE_EDITOR) {
query.Result = true
return nil
}
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, m.PERMISSION_EDIT)
builder.writeDashboardPermissionFilter(query.SignedInUser, models.PERMISSION_EDIT)
type folderCount struct {
Count int64
}
resp := make([]*folderCount, 0)
if err := x.SQL(builder.GetSqlString(), builder.params...).Find(&resp); err != nil {
return err
}
query.Result = len(resp) > 0 && resp[0].Count > 0
return nil
}
func HasAdminPermissionInFolders(query *models.HasAdminPermissionInFoldersQuery) error {
if query.SignedInUser.HasRole(models.ROLE_ADMIN) {
query.Result = true
return nil
}
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)
type folderCount struct {
Count int64