Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
eb765d28
Commit
eb765d28
authored
Jan 30, 2018
by
Daniel Lee
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
alertlist: disable pause button when user does not have permission
parent
4bc5945c
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
191 additions
and
9 deletions
+191
-9
pkg/api/alerting.go
+21
-0
pkg/api/dtos/alerting.go
+1
-0
pkg/models/dashboards.go
+14
-0
pkg/services/sqlstore/dashboard.go
+74
-1
pkg/services/sqlstore/dashboard_test.go
+55
-0
public/app/containers/AlertRuleList/AlertRuleList.jest.tsx
+1
-0
public/app/containers/AlertRuleList/AlertRuleList.tsx
+20
-6
public/app/containers/AlertRuleList/__snapshots__/AlertRuleList.jest.tsx.snap
+3
-2
public/app/stores/AlertListStore/AlertListStore.jest.ts
+1
-0
public/app/stores/AlertListStore/AlertRule.ts
+1
-0
No files found.
pkg/api/alerting.go
View file @
eb765d28
...
@@ -99,6 +99,27 @@ func GetAlerts(c *middleware.Context) Response {
...
@@ -99,6 +99,27 @@ func GetAlerts(c *middleware.Context) Response {
}
}
}
}
permissionsQuery
:=
models
.
GetDashboardPermissionsForUserQuery
{
DashboardIds
:
dashboardIds
,
OrgId
:
c
.
OrgId
,
UserId
:
c
.
SignedInUser
.
UserId
,
OrgRole
:
c
.
SignedInUser
.
OrgRole
,
}
if
len
(
alertDTOs
)
>
0
{
if
err
:=
bus
.
Dispatch
(
&
permissionsQuery
);
err
!=
nil
{
return
ApiError
(
500
,
"List alerts failed"
,
err
)
}
}
for
_
,
alert
:=
range
alertDTOs
{
for
_
,
perm
:=
range
permissionsQuery
.
Result
{
if
alert
.
DashboardId
==
perm
.
DashboardId
{
alert
.
CanEdit
=
perm
.
Permission
>
1
}
}
}
return
Json
(
200
,
alertDTOs
)
return
Json
(
200
,
alertDTOs
)
}
}
...
...
pkg/api/dtos/alerting.go
View file @
eb765d28
...
@@ -20,6 +20,7 @@ type AlertRule struct {
...
@@ -20,6 +20,7 @@ type AlertRule struct {
EvalData
*
simplejson
.
Json
`json:"evalData"`
EvalData
*
simplejson
.
Json
`json:"evalData"`
ExecutionError
string
`json:"executionError"`
ExecutionError
string
`json:"executionError"`
DashbboardUri
string
`json:"dashboardUri"`
DashbboardUri
string
`json:"dashboardUri"`
CanEdit
bool
`json:"canEdit"`
}
}
type
AlertNotification
struct
{
type
AlertNotification
struct
{
...
...
pkg/models/dashboards.go
View file @
eb765d28
...
@@ -199,6 +199,14 @@ type GetDashboardsQuery struct {
...
@@ -199,6 +199,14 @@ type GetDashboardsQuery struct {
Result
[]
*
Dashboard
Result
[]
*
Dashboard
}
}
type
GetDashboardPermissionsForUserQuery
struct
{
DashboardIds
[]
int64
OrgId
int64
UserId
int64
OrgRole
RoleType
Result
[]
*
DashboardPermissionForUser
}
type
GetDashboardsByPluginIdQuery
struct
{
type
GetDashboardsByPluginIdQuery
struct
{
OrgId
int64
OrgId
int64
PluginId
string
PluginId
string
...
@@ -221,3 +229,9 @@ type DashboardFolder struct {
...
@@ -221,3 +229,9 @@ type DashboardFolder struct {
Id
int64
`json:"id"`
Id
int64
`json:"id"`
Title
string
`json:"title"`
Title
string
`json:"title"`
}
}
type
DashboardPermissionForUser
struct
{
DashboardId
int64
`json:"dashboardId"`
Permission
PermissionType
`json:"permission"`
PermissionName
string
`json:"permissionName"`
}
pkg/services/sqlstore/dashboard.go
View file @
eb765d28
package
sqlstore
package
sqlstore
import
(
import
(
"strings"
"time"
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/bus"
...
@@ -19,6 +20,7 @@ func init() {
...
@@ -19,6 +20,7 @@ func init() {
bus
.
AddHandler
(
"sql"
,
GetDashboardSlugById
)
bus
.
AddHandler
(
"sql"
,
GetDashboardSlugById
)
bus
.
AddHandler
(
"sql"
,
GetDashboardsByPluginId
)
bus
.
AddHandler
(
"sql"
,
GetDashboardsByPluginId
)
bus
.
AddHandler
(
"sql"
,
GetFoldersForSignedInUser
)
bus
.
AddHandler
(
"sql"
,
GetFoldersForSignedInUser
)
bus
.
AddHandler
(
"sql"
,
GetDashboardPermissionsForUser
)
}
}
func
SaveDashboard
(
cmd
*
m
.
SaveDashboardCommand
)
error
{
func
SaveDashboard
(
cmd
*
m
.
SaveDashboardCommand
)
error
{
...
@@ -309,9 +311,10 @@ func GetFoldersForSignedInUser(query *m.GetFoldersForSignedInUserQuery) error {
...
@@ -309,9 +311,10 @@ func GetFoldersForSignedInUser(query *m.GetFoldersForSignedInUserQuery) error {
LEFT JOIN dashboard_acl AS da ON d.id = da.dashboard_id
LEFT JOIN dashboard_acl AS da ON d.id = da.dashboard_id
LEFT JOIN team_member AS ugm ON ugm.team_id = da.team_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 = ?
LEFT JOIN org_user ou ON ou.role = da.role AND ou.user_id = ?
LEFT JOIN org_user ouRole ON ouRole.role = 'Editor' AND ouRole.user_id = ?`
LEFT JOIN org_user ouRole ON ouRole.role = 'Editor' AND ouRole.user_id = ?
AND ouRole.org_id = ?
`
params
=
append
(
params
,
query
.
SignedInUser
.
UserId
)
params
=
append
(
params
,
query
.
SignedInUser
.
UserId
)
params
=
append
(
params
,
query
.
SignedInUser
.
UserId
)
params
=
append
(
params
,
query
.
SignedInUser
.
UserId
)
params
=
append
(
params
,
query
.
OrgId
)
sql
+=
`WHERE
sql
+=
`WHERE
d.org_id = ? AND
d.org_id = ? AND
...
@@ -389,6 +392,76 @@ func GetDashboards(query *m.GetDashboardsQuery) error {
...
@@ -389,6 +392,76 @@ func GetDashboards(query *m.GetDashboardsQuery) error {
return
nil
return
nil
}
}
// 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
{
if
len
(
query
.
DashboardIds
)
==
0
{
return
m
.
ErrCommandValidationFailed
}
if
query
.
OrgRole
==
m
.
ROLE_ADMIN
{
var
permissions
=
make
([]
*
m
.
DashboardPermissionForUser
,
0
)
for
_
,
d
:=
range
query
.
DashboardIds
{
permissions
=
append
(
permissions
,
&
m
.
DashboardPermissionForUser
{
DashboardId
:
d
,
Permission
:
m
.
PERMISSION_ADMIN
,
PermissionName
:
m
.
PERMISSION_ADMIN
.
String
(),
})
}
query
.
Result
=
permissions
return
nil
}
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
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 = ?
`
params
=
append
(
params
,
query
.
UserId
)
//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
)
sql
+=
`
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
WHERE
d.Id IN (?`
+
strings
.
Repeat
(
",?"
,
len
(
query
.
DashboardIds
)
-
1
)
+
`) `
for
_
,
id
:=
range
query
.
DashboardIds
{
params
=
append
(
params
,
id
)
}
sql
+=
` AND
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`
params
=
append
(
params
,
dialect
.
BooleanStr
(
true
))
params
=
append
(
params
,
query
.
OrgId
)
params
=
append
(
params
,
query
.
UserId
)
params
=
append
(
params
,
query
.
UserId
)
params
=
append
(
params
,
dialect
.
BooleanStr
(
false
))
err
:=
x
.
Sql
(
sql
,
params
...
)
.
Find
(
&
query
.
Result
)
for
_
,
p
:=
range
query
.
Result
{
p
.
PermissionName
=
p
.
Permission
.
String
()
}
return
err
}
func
GetDashboardsByPluginId
(
query
*
m
.
GetDashboardsByPluginIdQuery
)
error
{
func
GetDashboardsByPluginId
(
query
*
m
.
GetDashboardsByPluginIdQuery
)
error
{
var
dashboards
=
make
([]
*
m
.
Dashboard
,
0
)
var
dashboards
=
make
([]
*
m
.
Dashboard
,
0
)
whereExpr
:=
"org_id=? AND plugin_id=? AND is_folder="
+
dialect
.
BooleanStr
(
false
)
whereExpr
:=
"org_id=? AND plugin_id=? AND is_folder="
+
dialect
.
BooleanStr
(
false
)
...
...
pkg/services/sqlstore/dashboard_test.go
View file @
eb765d28
...
@@ -482,6 +482,24 @@ func TestDashboardDataAccess(t *testing.T) {
...
@@ -482,6 +482,24 @@ func TestDashboardDataAccess(t *testing.T) {
So
(
query
.
Result
[
0
]
.
Id
,
ShouldEqual
,
folder1
.
Id
)
So
(
query
.
Result
[
0
]
.
Id
,
ShouldEqual
,
folder1
.
Id
)
So
(
query
.
Result
[
1
]
.
Id
,
ShouldEqual
,
folder2
.
Id
)
So
(
query
.
Result
[
1
]
.
Id
,
ShouldEqual
,
folder2
.
Id
)
})
})
Convey
(
"should have write access to all folders and dashboards"
,
func
()
{
query
:=
m
.
GetDashboardPermissionsForUserQuery
{
DashboardIds
:
[]
int64
{
folder1
.
Id
,
folder2
.
Id
},
OrgId
:
1
,
UserId
:
adminUser
.
Id
,
OrgRole
:
m
.
ROLE_ADMIN
,
}
err
:=
GetDashboardPermissionsForUser
(
&
query
)
So
(
err
,
ShouldBeNil
)
So
(
len
(
query
.
Result
),
ShouldEqual
,
2
)
So
(
query
.
Result
[
0
]
.
DashboardId
,
ShouldEqual
,
folder1
.
Id
)
So
(
query
.
Result
[
0
]
.
Permission
,
ShouldEqual
,
m
.
PERMISSION_ADMIN
)
So
(
query
.
Result
[
1
]
.
DashboardId
,
ShouldEqual
,
folder2
.
Id
)
So
(
query
.
Result
[
1
]
.
Permission
,
ShouldEqual
,
m
.
PERMISSION_ADMIN
)
})
})
})
Convey
(
"Editor users"
,
func
()
{
Convey
(
"Editor users"
,
func
()
{
...
@@ -499,6 +517,24 @@ func TestDashboardDataAccess(t *testing.T) {
...
@@ -499,6 +517,24 @@ func TestDashboardDataAccess(t *testing.T) {
So
(
query
.
Result
[
1
]
.
Id
,
ShouldEqual
,
folder2
.
Id
)
So
(
query
.
Result
[
1
]
.
Id
,
ShouldEqual
,
folder2
.
Id
)
})
})
Convey
(
"should have edit access to folders with default ACL"
,
func
()
{
query
:=
m
.
GetDashboardPermissionsForUserQuery
{
DashboardIds
:
[]
int64
{
folder1
.
Id
,
folder2
.
Id
},
OrgId
:
1
,
UserId
:
editorUser
.
Id
,
OrgRole
:
m
.
ROLE_EDITOR
,
}
err
:=
GetDashboardPermissionsForUser
(
&
query
)
So
(
err
,
ShouldBeNil
)
So
(
len
(
query
.
Result
),
ShouldEqual
,
2
)
So
(
query
.
Result
[
0
]
.
DashboardId
,
ShouldEqual
,
folder1
.
Id
)
So
(
query
.
Result
[
0
]
.
Permission
,
ShouldEqual
,
m
.
PERMISSION_EDIT
)
So
(
query
.
Result
[
1
]
.
DashboardId
,
ShouldEqual
,
folder2
.
Id
)
So
(
query
.
Result
[
1
]
.
Permission
,
ShouldEqual
,
m
.
PERMISSION_EDIT
)
})
Convey
(
"Should have write access to one dashboard folder if default role changed to view for one folder"
,
func
()
{
Convey
(
"Should have write access to one dashboard folder if default role changed to view for one folder"
,
func
()
{
updateTestDashboardWithAcl
(
folder1
.
Id
,
editorUser
.
Id
,
m
.
PERMISSION_VIEW
)
updateTestDashboardWithAcl
(
folder1
.
Id
,
editorUser
.
Id
,
m
.
PERMISSION_VIEW
)
...
@@ -508,6 +544,7 @@ func TestDashboardDataAccess(t *testing.T) {
...
@@ -508,6 +544,7 @@ func TestDashboardDataAccess(t *testing.T) {
So
(
len
(
query
.
Result
),
ShouldEqual
,
1
)
So
(
len
(
query
.
Result
),
ShouldEqual
,
1
)
So
(
query
.
Result
[
0
]
.
Id
,
ShouldEqual
,
folder2
.
Id
)
So
(
query
.
Result
[
0
]
.
Id
,
ShouldEqual
,
folder2
.
Id
)
})
})
})
})
Convey
(
"Viewer users"
,
func
()
{
Convey
(
"Viewer users"
,
func
()
{
...
@@ -523,6 +560,24 @@ func TestDashboardDataAccess(t *testing.T) {
...
@@ -523,6 +560,24 @@ func TestDashboardDataAccess(t *testing.T) {
So
(
len
(
query
.
Result
),
ShouldEqual
,
0
)
So
(
len
(
query
.
Result
),
ShouldEqual
,
0
)
})
})
Convey
(
"should have view access to folders with default ACL"
,
func
()
{
query
:=
m
.
GetDashboardPermissionsForUserQuery
{
DashboardIds
:
[]
int64
{
folder1
.
Id
,
folder2
.
Id
},
OrgId
:
1
,
UserId
:
viewerUser
.
Id
,
OrgRole
:
m
.
ROLE_VIEWER
,
}
err
:=
GetDashboardPermissionsForUser
(
&
query
)
So
(
err
,
ShouldBeNil
)
So
(
len
(
query
.
Result
),
ShouldEqual
,
2
)
So
(
query
.
Result
[
0
]
.
DashboardId
,
ShouldEqual
,
folder1
.
Id
)
So
(
query
.
Result
[
0
]
.
Permission
,
ShouldEqual
,
m
.
PERMISSION_VIEW
)
So
(
query
.
Result
[
1
]
.
DashboardId
,
ShouldEqual
,
folder2
.
Id
)
So
(
query
.
Result
[
1
]
.
Permission
,
ShouldEqual
,
m
.
PERMISSION_VIEW
)
})
Convey
(
"Should be able to get one dashboard folder if default role changed to edit for one folder"
,
func
()
{
Convey
(
"Should be able to get one dashboard folder if default role changed to edit for one folder"
,
func
()
{
updateTestDashboardWithAcl
(
folder1
.
Id
,
viewerUser
.
Id
,
m
.
PERMISSION_EDIT
)
updateTestDashboardWithAcl
(
folder1
.
Id
,
viewerUser
.
Id
,
m
.
PERMISSION_EDIT
)
...
...
public/app/containers/AlertRuleList/AlertRuleList.jest.tsx
View file @
eb765d28
...
@@ -24,6 +24,7 @@ describe('AlertRuleList', () => {
...
@@ -24,6 +24,7 @@ describe('AlertRuleList', () => {
evalData
:
{},
evalData
:
{},
executionError
:
''
,
executionError
:
''
,
dashboardUri
:
'db/mygool'
,
dashboardUri
:
'db/mygool'
,
canEdit
:
true
,
},
},
])
])
);
);
...
...
public/app/containers/AlertRuleList/AlertRuleList.tsx
View file @
eb765d28
...
@@ -147,7 +147,8 @@ export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
...
@@ -147,7 +147,8 @@ export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
<
div
className=
"alert-rule-item__body"
>
<
div
className=
"alert-rule-item__body"
>
<
div
className=
"alert-rule-item__header"
>
<
div
className=
"alert-rule-item__header"
>
<
div
className=
"alert-rule-item__name"
>
<
div
className=
"alert-rule-item__name"
>
<
a
href=
{
ruleUrl
}
>
{
this
.
renderText
(
rule
.
name
)
}
</
a
>
{
rule
.
canEdit
&&
<
a
href=
{
ruleUrl
}
>
{
this
.
renderText
(
rule
.
name
)
}
</
a
>
}
{
!
rule
.
canEdit
&&
<
span
>
{
this
.
renderText
(
rule
.
name
)
}
</
span
>
}
</
div
>
</
div
>
<
div
className=
"alert-rule-item__text"
>
<
div
className=
"alert-rule-item__text"
>
<
span
className=
{
`${rule.stateClass}`
}
>
{
this
.
renderText
(
rule
.
stateText
)
}
</
span
>
<
span
className=
{
`${rule.stateClass}`
}
>
{
this
.
renderText
(
rule
.
stateText
)
}
</
span
>
...
@@ -156,17 +157,30 @@ export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
...
@@ -156,17 +157,30 @@ export class AlertRuleItem extends React.Component<AlertRuleItemProps, any> {
</
div
>
</
div
>
{
rule
.
info
&&
<
div
className=
"small muted alert-rule-item__info"
>
{
this
.
renderText
(
rule
.
info
)
}
</
div
>
}
{
rule
.
info
&&
<
div
className=
"small muted alert-rule-item__info"
>
{
this
.
renderText
(
rule
.
info
)
}
</
div
>
}
</
div
>
</
div
>
<
div
className=
"alert-rule-item__actions"
>
<
div
className=
"alert-rule-item__actions"
>
<
a
<
button
className=
"btn btn-small btn-inverse alert-list__btn width-2"
className=
"btn btn-small btn-inverse alert-list__btn width-2"
title=
"Pausing an alert rule prevents it from executing"
title=
"Pausing an alert rule prevents it from executing"
onClick=
{
this
.
toggleState
}
onClick=
{
this
.
toggleState
}
disabled=
{
!
rule
.
canEdit
}
>
>
<
i
className=
{
stateClass
}
/>
<
i
className=
{
stateClass
}
/>
</
a
>
</
button
>
<
a
className=
"btn btn-small btn-inverse alert-list__btn width-2"
href=
{
ruleUrl
}
title=
"Edit alert rule"
>
{
rule
.
canEdit
&&
(
<
i
className=
"icon-gf icon-gf-settings"
/>
<
a
className=
"btn btn-small btn-inverse alert-list__btn width-2"
href=
{
ruleUrl
}
title=
"Edit alert rule"
>
</
a
>
<
i
className=
"icon-gf icon-gf-settings"
/>
</
a
>
)
}
{
!
rule
.
canEdit
&&
(
<
button
className=
"btn btn-small btn-inverse alert-list__btn width-2"
title=
"Edit alert rule"
disabled=
{
true
}
>
<
i
className=
"icon-gf icon-gf-settings"
/>
</
button
>
)
}
</
div
>
</
div
>
</
li
>
</
li
>
);
);
...
...
public/app/containers/AlertRuleList/__snapshots__/AlertRuleList.jest.tsx.snap
View file @
eb765d28
...
@@ -80,15 +80,16 @@ exports[`AlertRuleList should render 1 rule 1`] = `
...
@@ -80,15 +80,16 @@ exports[`AlertRuleList should render 1 rule 1`] = `
<div
<div
className="alert-rule-item__actions"
className="alert-rule-item__actions"
>
>
<
a
<
button
className="btn btn-small btn-inverse alert-list__btn width-2"
className="btn btn-small btn-inverse alert-list__btn width-2"
disabled={false}
onClick={[Function]}
onClick={[Function]}
title="Pausing an alert rule prevents it from executing"
title="Pausing an alert rule prevents it from executing"
>
>
<i
<i
className="fa fa-pause"
className="fa fa-pause"
/>
/>
</
a
>
</
button
>
<a
<a
className="btn btn-small btn-inverse alert-list__btn width-2"
className="btn btn-small btn-inverse alert-list__btn width-2"
href="dashboard/db/mygool?panelId=3&fullscreen&edit&tab=alert"
href="dashboard/db/mygool?panelId=3&fullscreen&edit&tab=alert"
...
...
public/app/stores/AlertListStore/AlertListStore.jest.ts
View file @
eb765d28
...
@@ -20,6 +20,7 @@ function getRule(name, state, info) {
...
@@ -20,6 +20,7 @@ function getRule(name, state, info) {
stateClass
:
'asd'
,
stateClass
:
'asd'
,
stateAge
:
'10m'
,
stateAge
:
'10m'
,
info
:
info
,
info
:
info
,
canEdit
:
true
,
};
};
}
}
...
...
public/app/stores/AlertListStore/AlertRule.ts
View file @
eb765d28
...
@@ -14,6 +14,7 @@ export const AlertRule = types
...
@@ -14,6 +14,7 @@ export const AlertRule = types
stateAge
:
types
.
string
,
stateAge
:
types
.
string
,
info
:
types
.
optional
(
types
.
string
,
''
),
info
:
types
.
optional
(
types
.
string
,
''
),
dashboardUri
:
types
.
string
,
dashboardUri
:
types
.
string
,
canEdit
:
types
.
boolean
,
})
})
.
views
(
self
=>
({
.
views
(
self
=>
({
get
isPaused
()
{
get
isPaused
()
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment