Commit f17100e9 by Marcus Efraimsson Committed by GitHub

Merge pull request #11786 from marefr/11625_save_as

dashboard: show save as button if user has edit permission

This will show the "Save As..." button in dashboard settings page if the user 
has edit permissions (org role admin/editor or viewers_can_edit enabled) 
and has at least edit permission in any folder.
parents 1f21b3e2 5c57c7cf
...@@ -37,6 +37,7 @@ type CurrentUser struct { ...@@ -37,6 +37,7 @@ type CurrentUser struct {
Timezone string `json:"timezone"` Timezone string `json:"timezone"`
Locale string `json:"locale"` Locale string `json:"locale"`
HelpFlags1 m.HelpFlags1 `json:"helpFlags1"` HelpFlags1 m.HelpFlags1 `json:"helpFlags1"`
HasEditPermissionInFolders bool `json:"hasEditPermissionInFolders"`
} }
type MetricRequest struct { type MetricRequest struct {
......
...@@ -42,6 +42,11 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) { ...@@ -42,6 +42,11 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) {
settings["appSubUrl"] = "" settings["appSubUrl"] = ""
} }
hasEditPermissionInFoldersQuery := m.HasEditPermissionInFoldersQuery{SignedInUser: c.SignedInUser}
if err := bus.Dispatch(&hasEditPermissionInFoldersQuery); err != nil {
return nil, err
}
var data = dtos.IndexViewData{ var data = dtos.IndexViewData{
User: &dtos.CurrentUser{ User: &dtos.CurrentUser{
Id: c.UserId, Id: c.UserId,
...@@ -59,6 +64,7 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) { ...@@ -59,6 +64,7 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) {
Timezone: prefs.Timezone, Timezone: prefs.Timezone,
Locale: locale, Locale: locale,
HelpFlags1: c.HelpFlags1, HelpFlags1: c.HelpFlags1,
HasEditPermissionInFolders: hasEditPermissionInFoldersQuery.Result,
}, },
Settings: settings, Settings: settings,
Theme: prefs.Theme, Theme: prefs.Theme,
......
...@@ -89,3 +89,12 @@ type UpdateFolderCommand struct { ...@@ -89,3 +89,12 @@ type UpdateFolderCommand struct {
Result *Folder Result *Folder
} }
//
// QUERIES
//
type HasEditPermissionInFoldersQuery struct {
SignedInUser *SignedInUser
Result bool
}
...@@ -24,6 +24,7 @@ func init() { ...@@ -24,6 +24,7 @@ func init() {
bus.AddHandler("sql", GetDashboardPermissionsForUser) bus.AddHandler("sql", GetDashboardPermissionsForUser)
bus.AddHandler("sql", GetDashboardsBySlug) bus.AddHandler("sql", GetDashboardsBySlug)
bus.AddHandler("sql", ValidateDashboardBeforeSave) bus.AddHandler("sql", ValidateDashboardBeforeSave)
bus.AddHandler("sql", HasEditPermissionInFolders)
} }
var generateNewUid func() string = util.GenerateShortUid var generateNewUid func() string = util.GenerateShortUid
...@@ -614,3 +615,27 @@ func ValidateDashboardBeforeSave(cmd *m.ValidateDashboardBeforeSaveCommand) (err ...@@ -614,3 +615,27 @@ func ValidateDashboardBeforeSave(cmd *m.ValidateDashboardBeforeSaveCommand) (err
return nil return nil
}) })
} }
func HasEditPermissionInFolders(query *m.HasEditPermissionInFoldersQuery) error {
if query.SignedInUser.HasRole(m.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)
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
}
...@@ -221,7 +221,6 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -221,7 +221,6 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("Given two dashboard folders", func() { Convey("Given two dashboard folders", func() {
folder1 := insertTestDashboard("1 test dash folder", 1, 0, true, "prod") folder1 := insertTestDashboard("1 test dash folder", 1, 0, true, "prod")
folder2 := insertTestDashboard("2 test dash folder", 1, 0, true, "prod") folder2 := insertTestDashboard("2 test dash folder", 1, 0, true, "prod")
insertTestDashboard("folder in another org", 2, 0, true, "prod") insertTestDashboard("folder in another org", 2, 0, true, "prod")
...@@ -264,6 +263,15 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -264,6 +263,15 @@ func TestDashboardFolderDataAccess(t *testing.T) {
So(query.Result[1].DashboardId, ShouldEqual, folder2.Id) So(query.Result[1].DashboardId, ShouldEqual, folder2.Id)
So(query.Result[1].Permission, ShouldEqual, m.PERMISSION_ADMIN) So(query.Result[1].Permission, ShouldEqual, m.PERMISSION_ADMIN)
}) })
Convey("should have edit permission in folders", func() {
query := &m.HasEditPermissionInFoldersQuery{
SignedInUser: &m.SignedInUser{UserId: adminUser.Id, OrgId: 1, OrgRole: m.ROLE_ADMIN},
}
err := HasEditPermissionInFolders(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeTrue)
})
}) })
Convey("Editor users", func() { Convey("Editor users", func() {
...@@ -310,6 +318,14 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -310,6 +318,14 @@ func TestDashboardFolderDataAccess(t *testing.T) {
So(query.Result[0].Id, ShouldEqual, folder2.Id) So(query.Result[0].Id, ShouldEqual, folder2.Id)
}) })
Convey("should have edit permission in folders", func() {
query := &m.HasEditPermissionInFoldersQuery{
SignedInUser: &m.SignedInUser{UserId: editorUser.Id, OrgId: 1, OrgRole: m.ROLE_EDITOR},
}
err := HasEditPermissionInFolders(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeTrue)
})
}) })
Convey("Viewer users", func() { Convey("Viewer users", func() {
...@@ -353,6 +369,41 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -353,6 +369,41 @@ func TestDashboardFolderDataAccess(t *testing.T) {
So(len(query.Result), ShouldEqual, 1) So(len(query.Result), ShouldEqual, 1)
So(query.Result[0].Id, ShouldEqual, folder1.Id) So(query.Result[0].Id, ShouldEqual, folder1.Id)
}) })
Convey("should not have edit permission in folders", func() {
query := &m.HasEditPermissionInFoldersQuery{
SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER},
}
err := HasEditPermissionInFolders(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeFalse)
})
Convey("and admin permission is given for user with org role viewer in one dashboard folder", func() {
testHelperUpdateDashboardAcl(folder1.Id, m.DashboardAcl{DashboardId: folder1.Id, OrgId: 1, UserId: viewerUser.Id, Permission: m.PERMISSION_ADMIN})
Convey("should have edit permission in folders", func() {
query := &m.HasEditPermissionInFoldersQuery{
SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER},
}
err := HasEditPermissionInFolders(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeTrue)
})
})
Convey("and edit permission is given for user with org role viewer in one dashboard folder", func() {
testHelperUpdateDashboardAcl(folder1.Id, m.DashboardAcl{DashboardId: folder1.Id, OrgId: 1, UserId: viewerUser.Id, Permission: m.PERMISSION_EDIT})
Convey("should have edit permission in folders", func() {
query := &m.HasEditPermissionInFoldersQuery{
SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER},
}
err := HasEditPermissionInFolders(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeTrue)
})
})
}) })
}) })
}) })
......
...@@ -11,6 +11,7 @@ export class User { ...@@ -11,6 +11,7 @@ export class User {
timezone: string; timezone: string;
helpFlags1: number; helpFlags1: number;
lightTheme: boolean; lightTheme: boolean;
hasEditPermissionInFolders: boolean;
constructor() { constructor() {
if (config.bootData.user) { if (config.bootData.user) {
...@@ -28,6 +29,7 @@ export class ContextSrv { ...@@ -28,6 +29,7 @@ export class ContextSrv {
isEditor: any; isEditor: any;
sidemenu: any; sidemenu: any;
sidemenuSmallBreakpoint = false; sidemenuSmallBreakpoint = false;
hasEditPermissionInFolders: boolean;
constructor() { constructor() {
this.sidemenu = store.getBool('grafana.sidemenu', true); this.sidemenu = store.getBool('grafana.sidemenu', true);
...@@ -44,6 +46,7 @@ export class ContextSrv { ...@@ -44,6 +46,7 @@ export class ContextSrv {
this.isSignedIn = this.user.isSignedIn; this.isSignedIn = this.user.isSignedIn;
this.isGrafanaAdmin = this.user.isGrafanaAdmin; this.isGrafanaAdmin = this.user.isGrafanaAdmin;
this.isEditor = this.hasRole('Editor') || this.hasRole('Admin'); this.isEditor = this.hasRole('Editor') || this.hasRole('Admin');
this.hasEditPermissionInFolders = this.user.hasEditPermissionInFolders;
} }
hasRole(role) { hasRole(role) {
......
...@@ -30,7 +30,7 @@ export class SettingsCtrl { ...@@ -30,7 +30,7 @@ export class SettingsCtrl {
}); });
}); });
this.canSaveAs = contextSrv.isEditor; this.canSaveAs = this.dashboard.meta.canEdit && contextSrv.hasEditPermissionInFolders;
this.canSave = this.dashboard.meta.canSave; this.canSave = this.dashboard.meta.canSave;
this.canDelete = this.dashboard.meta.canSave; this.canDelete = this.dashboard.meta.canSave;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment