Commit ebcbcdc2 by Daniel Lee

dashfolders: inherited permissions for dashboards

Allow duplicate permissions if the original is an
inherited permission from a parent folder.

If a dashboard has permissions but the parent folder
does not, return the default permissions for the
editor and view role combined with the permissions
for the child dashboard.
parent 86a7266f
...@@ -167,8 +167,10 @@ func GetDashboardAclInfoList(query *m.GetDashboardAclInfoListQuery) error { ...@@ -167,8 +167,10 @@ func GetDashboardAclInfoList(query *m.GetDashboardAclInfoListQuery) error {
'' as user_login, '' as user_login,
'' as user_email, '' as user_email,
'' as user_group '' as user_group
FROM dashboard_acl as da, dashboard as dash FROM dashboard_acl as da,
WHERE dash.id = ? AND dash.has_acl = 0 AND da.dashboard_id = -1 dashboard as dash
LEFT JOIN dashboard folder on dash.folder_id = folder.id
WHERE dash.id = ? AND (dash.has_acl = 0 or folder.has_acl = 0) AND da.dashboard_id = -1
` `
query.Result = make([]*m.DashboardAclInfoDTO, 0) query.Result = make([]*m.DashboardAclInfoDTO, 0)
......
...@@ -25,6 +25,22 @@ func TestDashboardAclDataAccess(t *testing.T) { ...@@ -25,6 +25,22 @@ func TestDashboardAclDataAccess(t *testing.T) {
So(err, ShouldEqual, m.ErrDashboardAclInfoMissing) So(err, ShouldEqual, m.ErrDashboardAclInfoMissing)
}) })
Convey("Given dashboard folder with default permissions", func() {
Convey("When reading dashboard acl should include acl for parent folder", func() {
query := m.GetDashboardAclInfoListQuery{DashboardId: childDash.Id, OrgId: 1}
err := GetDashboardAclInfoList(&query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2)
defaultPermissionsId := -1
So(query.Result[0].DashboardId, ShouldEqual, defaultPermissionsId)
So(*query.Result[0].Role, ShouldEqual, m.ROLE_VIEWER)
So(query.Result[1].DashboardId, ShouldEqual, defaultPermissionsId)
So(*query.Result[1].Role, ShouldEqual, m.ROLE_EDITOR)
})
})
Convey("Given dashboard folder permission", func() { Convey("Given dashboard folder permission", func() {
err := SetDashboardAcl(&m.SetDashboardAclCommand{ err := SetDashboardAcl(&m.SetDashboardAclCommand{
OrgId: 1, OrgId: 1,
...@@ -66,6 +82,31 @@ func TestDashboardAclDataAccess(t *testing.T) { ...@@ -66,6 +82,31 @@ func TestDashboardAclDataAccess(t *testing.T) {
}) })
}) })
Convey("Given child dashboard permission in folder with no permissions", func() {
err := SetDashboardAcl(&m.SetDashboardAclCommand{
OrgId: 1,
UserId: currentUser.Id,
DashboardId: childDash.Id,
Permission: m.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
Convey("When reading dashboard acl should include default acl for parent folder and the child acl", func() {
query := m.GetDashboardAclInfoListQuery{OrgId: 1, DashboardId: childDash.Id}
err := GetDashboardAclInfoList(&query)
So(err, ShouldBeNil)
defaultPermissionsId := -1
So(len(query.Result), ShouldEqual, 3)
So(query.Result[0].DashboardId, ShouldEqual, defaultPermissionsId)
So(*query.Result[0].Role, ShouldEqual, m.ROLE_VIEWER)
So(query.Result[1].DashboardId, ShouldEqual, defaultPermissionsId)
So(*query.Result[1].Role, ShouldEqual, m.ROLE_EDITOR)
So(query.Result[2].DashboardId, ShouldEqual, childDash.Id)
})
})
Convey("Should be able to add dashboard permission", func() { Convey("Should be able to add dashboard permission", func() {
setDashAclCmd := m.SetDashboardAclCommand{ setDashAclCmd := m.SetDashboardAclCommand{
OrgId: 1, OrgId: 1,
......
...@@ -50,7 +50,7 @@ export class AclCtrl { ...@@ -50,7 +50,7 @@ export class AclCtrl {
} }
prepareViewModel(item: DashboardAcl): DashboardAcl { prepareViewModel(item: DashboardAcl): DashboardAcl {
item.inherited = this.dashboard.id !== item.dashboardId; item.inherited = !this.dashboard.meta.isFolder && this.dashboard.id !== item.dashboardId;
item.sortRank = 0; item.sortRank = 0;
if (item.userId > 0) { if (item.userId > 0) {
...@@ -138,6 +138,10 @@ export class AclCtrl { ...@@ -138,6 +138,10 @@ export class AclCtrl {
} }
isDuplicate(origItem, newItem) { isDuplicate(origItem, newItem) {
if (origItem.inherited) {
return false;
}
return (origItem.role && newItem.role && origItem.role === newItem.role) || return (origItem.role && newItem.role && origItem.role === newItem.role) ||
(origItem.userId && newItem.userId && origItem.userId === newItem.userId) || (origItem.userId && newItem.userId && origItem.userId === newItem.userId) ||
(origItem.userGroupId && newItem.userGroupId && origItem.userGroupId === newItem.userGroupId); (origItem.userGroupId && newItem.userGroupId && origItem.userGroupId === newItem.userGroupId);
......
...@@ -9,7 +9,7 @@ describe('AclCtrl', () => { ...@@ -9,7 +9,7 @@ describe('AclCtrl', () => {
}; };
const dashboardSrv = { const dashboardSrv = {
getCurrent: sinon.stub().returns({id: 1}) getCurrent: sinon.stub().returns({id: 1, meta: { isFolder: false }})
}; };
beforeEach(angularMocks.module('grafana.core')); beforeEach(angularMocks.module('grafana.core'));
...@@ -130,7 +130,7 @@ describe('AclCtrl', () => { ...@@ -130,7 +130,7 @@ describe('AclCtrl', () => {
backendSrv.post.reset(); backendSrv.post.reset();
ctx.ctrl.items = []; ctx.ctrl.items = [];
const userGroupItem = { const userGroupItem = {
id: 2, id: 2,
name: 'ug1', name: 'ug1',
}; };
...@@ -147,4 +147,34 @@ describe('AclCtrl', () => { ...@@ -147,4 +147,34 @@ describe('AclCtrl', () => {
expect(ctx.ctrl.items.length).to.eql(1); expect(ctx.ctrl.items.length).to.eql(1);
}); });
}); });
describe('when one inherited and one not inherited user group permission are added', () => {
beforeEach(() => {
backendSrv.get.reset();
backendSrv.post.reset();
ctx.ctrl.items = [];
const inheritedUserGroupItem = {
id: 2,
name: 'ug1',
dashboardId: -1
};
ctx.ctrl.items.push(inheritedUserGroupItem);
const userGroupItem = {
id: 2,
name: 'ug1',
};
ctx.ctrl.groupPicked(userGroupItem);
});
it('should not throw a validation error', () => {
expect(ctx.ctrl.error).to.eql('');
});
it('should add both permissions', () => {
expect(ctx.ctrl.items.length).to.eql(2);
});
});
}); });
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