Commit 8fd153ed by Marcus Efraimsson Committed by GitHub

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
parent ab170157
...@@ -63,6 +63,48 @@ Content-Type: application/json ...@@ -63,6 +63,48 @@ Content-Type: application/json
``` ```
## Get all notification channels (lookup)
Returns all notification channels, but with less detailed information.
Accessible by any authenticated user and is mainly used by providing
alert notification channels in Grafana UI when configuring alert rule.
`GET /api/alert-notifications/lookup`
**Example Request**:
```http
GET /api/alert-notifications/lookup HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
[
{
"id": 1,
"uid": "000000001",
"name": "Test",
"type": "email",
"isDefault": false
},
{
"id": 2,
"uid": "000000002",
"name": "Slack",
"type": "slack",
"isDefault": false
}
]
```
## Get notification channel by uid ## Get notification channel by uid
`GET /api/alert-notifications/uid/:uid` `GET /api/alert-notifications/uid/:uid`
......
...@@ -47,6 +47,9 @@ Content-Type: application/json ...@@ -47,6 +47,9 @@ Content-Type: application/json
`GET /api/org/users` `GET /api/org/users`
Returns all org users within the current organization.
Accessible to users with org admin role.
**Example Request**: **Example Request**:
```http ```http
...@@ -64,11 +67,47 @@ Content-Type: application/json ...@@ -64,11 +67,47 @@ Content-Type: application/json
[ [
{ {
"orgId":1, "orgId": 1,
"userId":1, "userId": 1,
"email":"admin@mygraf.com", "email": "admin@localhost",
"login":"admin", "avatarUrl": "/avatar/46d229b033af06a191ff2267bca9ae56",
"role":"Admin" "login": "admin",
"role": "Admin",
"lastSeenAt": "2019-08-09T11:02:49+02:00",
"lastSeenAtAge": "< 1m"
}
]
```
### Get all users within the current organization (lookup)
`GET /api/org/users/lookup`
Returns all org users within the current organization, but with less detailed information.
Accessible to users with org admin role, admin in any folder or admin of any team.
Mainly used by Grafana UI for providing list of users when adding team members and
when editing folder/dashboard permissions.
**Example Request**:
```http
GET /api/org/users/lookup HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
[
{
"userId": 1,
"login": "admin",
"avatarUrl": "/avatar/46d229b033af06a191ff2267bca9ae56"
} }
] ]
``` ```
......
...@@ -6,15 +6,15 @@ import ( ...@@ -6,15 +6,15 @@ import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting" "github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/search" "github.com/grafana/grafana/pkg/services/search"
) )
func ValidateOrgAlert(c *m.ReqContext) { func ValidateOrgAlert(c *models.ReqContext) {
id := c.ParamsInt64(":alertId") id := c.ParamsInt64(":alertId")
query := m.GetAlertByIdQuery{Id: id} query := models.GetAlertByIdQuery{Id: id}
if err := bus.Dispatch(&query); err != nil { if err := bus.Dispatch(&query); err != nil {
c.JsonApiErr(404, "Alert not found", nil) c.JsonApiErr(404, "Alert not found", nil)
...@@ -27,14 +27,14 @@ func ValidateOrgAlert(c *m.ReqContext) { ...@@ -27,14 +27,14 @@ func ValidateOrgAlert(c *m.ReqContext) {
} }
} }
func GetAlertStatesForDashboard(c *m.ReqContext) Response { func GetAlertStatesForDashboard(c *models.ReqContext) Response {
dashboardID := c.QueryInt64("dashboardId") dashboardID := c.QueryInt64("dashboardId")
if dashboardID == 0 { if dashboardID == 0 {
return Error(400, "Missing query parameter dashboardId", nil) return Error(400, "Missing query parameter dashboardId", nil)
} }
query := m.GetAlertStatesForDashboardQuery{ query := models.GetAlertStatesForDashboardQuery{
OrgId: c.OrgId, OrgId: c.OrgId,
DashboardId: c.QueryInt64("dashboardId"), DashboardId: c.QueryInt64("dashboardId"),
} }
...@@ -47,7 +47,7 @@ func GetAlertStatesForDashboard(c *m.ReqContext) Response { ...@@ -47,7 +47,7 @@ func GetAlertStatesForDashboard(c *m.ReqContext) Response {
} }
// GET /api/alerts // GET /api/alerts
func GetAlerts(c *m.ReqContext) Response { func GetAlerts(c *models.ReqContext) Response {
dashboardQuery := c.Query("dashboardQuery") dashboardQuery := c.Query("dashboardQuery")
dashboardTags := c.QueryStrings("dashboardTag") dashboardTags := c.QueryStrings("dashboardTag")
stringDashboardIDs := c.QueryStrings("dashboardId") stringDashboardIDs := c.QueryStrings("dashboardId")
...@@ -79,7 +79,7 @@ func GetAlerts(c *m.ReqContext) Response { ...@@ -79,7 +79,7 @@ func GetAlerts(c *m.ReqContext) Response {
DashboardIds: dashboardIDs, DashboardIds: dashboardIDs,
Type: string(search.DashHitDB), Type: string(search.DashHitDB),
FolderIds: folderIDs, FolderIds: folderIDs,
Permission: m.PERMISSION_VIEW, Permission: models.PERMISSION_VIEW,
} }
err := bus.Dispatch(&searchQuery) err := bus.Dispatch(&searchQuery)
...@@ -95,11 +95,11 @@ func GetAlerts(c *m.ReqContext) Response { ...@@ -95,11 +95,11 @@ func GetAlerts(c *m.ReqContext) Response {
// if we didn't find any dashboards, return empty result // if we didn't find any dashboards, return empty result
if len(dashboardIDs) == 0 { if len(dashboardIDs) == 0 {
return JSON(200, []*m.AlertListItemDTO{}) return JSON(200, []*models.AlertListItemDTO{})
} }
} }
query := m.GetAlertsQuery{ query := models.GetAlertsQuery{
OrgId: c.OrgId, OrgId: c.OrgId,
DashboardIDs: dashboardIDs, DashboardIDs: dashboardIDs,
PanelId: c.QueryInt64("panelId"), PanelId: c.QueryInt64("panelId"),
...@@ -118,14 +118,14 @@ func GetAlerts(c *m.ReqContext) Response { ...@@ -118,14 +118,14 @@ func GetAlerts(c *m.ReqContext) Response {
} }
for _, alert := range query.Result { for _, alert := range query.Result {
alert.Url = m.GetDashboardUrl(alert.DashboardUid, alert.DashboardSlug) alert.Url = models.GetDashboardUrl(alert.DashboardUid, alert.DashboardSlug)
} }
return JSON(200, query.Result) return JSON(200, query.Result)
} }
// POST /api/alerts/test // POST /api/alerts/test
func AlertTest(c *m.ReqContext, dto dtos.AlertTestCommand) Response { func AlertTest(c *models.ReqContext, dto dtos.AlertTestCommand) Response {
if _, idErr := dto.Dashboard.Get("id").Int64(); idErr != nil { if _, idErr := dto.Dashboard.Get("id").Int64(); idErr != nil {
return Error(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil) return Error(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
} }
...@@ -141,7 +141,7 @@ func AlertTest(c *m.ReqContext, dto dtos.AlertTestCommand) Response { ...@@ -141,7 +141,7 @@ func AlertTest(c *m.ReqContext, dto dtos.AlertTestCommand) Response {
if validationErr, ok := err.(alerting.ValidationError); ok { if validationErr, ok := err.(alerting.ValidationError); ok {
return Error(422, validationErr.Error(), nil) return Error(422, validationErr.Error(), nil)
} }
if err == m.ErrDataSourceAccessDenied { if err == models.ErrDataSourceAccessDenied {
return Error(403, "Access denied to datasource", err) return Error(403, "Access denied to datasource", err)
} }
return Error(500, "Failed to test rule", err) return Error(500, "Failed to test rule", err)
...@@ -171,9 +171,9 @@ func AlertTest(c *m.ReqContext, dto dtos.AlertTestCommand) Response { ...@@ -171,9 +171,9 @@ func AlertTest(c *m.ReqContext, dto dtos.AlertTestCommand) Response {
} }
// GET /api/alerts/:id // GET /api/alerts/:id
func GetAlert(c *m.ReqContext) Response { func GetAlert(c *models.ReqContext) Response {
id := c.ParamsInt64(":alertId") id := c.ParamsInt64(":alertId")
query := m.GetAlertByIdQuery{Id: id} query := models.GetAlertByIdQuery{Id: id}
if err := bus.Dispatch(&query); err != nil { if err := bus.Dispatch(&query); err != nil {
return Error(500, "List alerts failed", err) return Error(500, "List alerts failed", err)
...@@ -182,28 +182,52 @@ func GetAlert(c *m.ReqContext) Response { ...@@ -182,28 +182,52 @@ func GetAlert(c *m.ReqContext) Response {
return JSON(200, &query.Result) return JSON(200, &query.Result)
} }
func GetAlertNotifiers(c *m.ReqContext) Response { func GetAlertNotifiers(c *models.ReqContext) Response {
return JSON(200, alerting.GetNotifiers()) return JSON(200, alerting.GetNotifiers())
} }
func GetAlertNotifications(c *m.ReqContext) Response { func GetAlertNotificationLookup(c *models.ReqContext) Response {
query := &m.GetAllAlertNotificationsQuery{OrgId: c.OrgId} alertNotifications, err := getAlertNotificationsInternal(c)
if err != nil {
return Error(500, "Failed to get alert notifications", err)
}
if err := bus.Dispatch(query); err != nil { result := make([]*dtos.AlertNotificationLookup, 0)
for _, notification := range alertNotifications {
result = append(result, dtos.NewAlertNotificationLookup(notification))
}
return JSON(200, result)
}
func GetAlertNotifications(c *models.ReqContext) Response {
alertNotifications, err := getAlertNotificationsInternal(c)
if err != nil {
return Error(500, "Failed to get alert notifications", err) return Error(500, "Failed to get alert notifications", err)
} }
result := make([]*dtos.AlertNotification, 0) result := make([]*dtos.AlertNotification, 0)
for _, notification := range query.Result { for _, notification := range alertNotifications {
result = append(result, dtos.NewAlertNotification(notification)) result = append(result, dtos.NewAlertNotification(notification))
} }
return JSON(200, result) return JSON(200, result)
} }
func GetAlertNotificationByID(c *m.ReqContext) Response { func getAlertNotificationsInternal(c *models.ReqContext) ([]*models.AlertNotification, error) {
query := &m.GetAlertNotificationsQuery{ query := &models.GetAllAlertNotificationsQuery{OrgId: c.OrgId}
if err := bus.Dispatch(query); err != nil {
return nil, err
}
return query.Result, nil
}
func GetAlertNotificationByID(c *models.ReqContext) Response {
query := &models.GetAlertNotificationsQuery{
OrgId: c.OrgId, OrgId: c.OrgId,
Id: c.ParamsInt64("notificationId"), Id: c.ParamsInt64("notificationId"),
} }
...@@ -223,8 +247,8 @@ func GetAlertNotificationByID(c *m.ReqContext) Response { ...@@ -223,8 +247,8 @@ func GetAlertNotificationByID(c *m.ReqContext) Response {
return JSON(200, dtos.NewAlertNotification(query.Result)) return JSON(200, dtos.NewAlertNotification(query.Result))
} }
func GetAlertNotificationByUID(c *m.ReqContext) Response { func GetAlertNotificationByUID(c *models.ReqContext) Response {
query := &m.GetAlertNotificationsWithUidQuery{ query := &models.GetAlertNotificationsWithUidQuery{
OrgId: c.OrgId, OrgId: c.OrgId,
Uid: c.Params("uid"), Uid: c.Params("uid"),
} }
...@@ -244,7 +268,7 @@ func GetAlertNotificationByUID(c *m.ReqContext) Response { ...@@ -244,7 +268,7 @@ func GetAlertNotificationByUID(c *m.ReqContext) Response {
return JSON(200, dtos.NewAlertNotification(query.Result)) return JSON(200, dtos.NewAlertNotification(query.Result))
} }
func CreateAlertNotification(c *m.ReqContext, cmd m.CreateAlertNotificationCommand) Response { func CreateAlertNotification(c *models.ReqContext, cmd models.CreateAlertNotificationCommand) Response {
cmd.OrgId = c.OrgId cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil { if err := bus.Dispatch(&cmd); err != nil {
...@@ -254,7 +278,7 @@ func CreateAlertNotification(c *m.ReqContext, cmd m.CreateAlertNotificationComma ...@@ -254,7 +278,7 @@ func CreateAlertNotification(c *m.ReqContext, cmd m.CreateAlertNotificationComma
return JSON(200, dtos.NewAlertNotification(cmd.Result)) return JSON(200, dtos.NewAlertNotification(cmd.Result))
} }
func UpdateAlertNotification(c *m.ReqContext, cmd m.UpdateAlertNotificationCommand) Response { func UpdateAlertNotification(c *models.ReqContext, cmd models.UpdateAlertNotificationCommand) Response {
cmd.OrgId = c.OrgId cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil { if err := bus.Dispatch(&cmd); err != nil {
...@@ -268,7 +292,7 @@ func UpdateAlertNotification(c *m.ReqContext, cmd m.UpdateAlertNotificationComma ...@@ -268,7 +292,7 @@ func UpdateAlertNotification(c *m.ReqContext, cmd m.UpdateAlertNotificationComma
return JSON(200, dtos.NewAlertNotification(cmd.Result)) return JSON(200, dtos.NewAlertNotification(cmd.Result))
} }
func UpdateAlertNotificationByUID(c *m.ReqContext, cmd m.UpdateAlertNotificationWithUidCommand) Response { func UpdateAlertNotificationByUID(c *models.ReqContext, cmd models.UpdateAlertNotificationWithUidCommand) Response {
cmd.OrgId = c.OrgId cmd.OrgId = c.OrgId
cmd.Uid = c.Params("uid") cmd.Uid = c.Params("uid")
...@@ -283,8 +307,8 @@ func UpdateAlertNotificationByUID(c *m.ReqContext, cmd m.UpdateAlertNotification ...@@ -283,8 +307,8 @@ func UpdateAlertNotificationByUID(c *m.ReqContext, cmd m.UpdateAlertNotification
return JSON(200, dtos.NewAlertNotification(cmd.Result)) return JSON(200, dtos.NewAlertNotification(cmd.Result))
} }
func DeleteAlertNotification(c *m.ReqContext) Response { func DeleteAlertNotification(c *models.ReqContext) Response {
cmd := m.DeleteAlertNotificationCommand{ cmd := models.DeleteAlertNotificationCommand{
OrgId: c.OrgId, OrgId: c.OrgId,
Id: c.ParamsInt64("notificationId"), Id: c.ParamsInt64("notificationId"),
} }
...@@ -296,8 +320,8 @@ func DeleteAlertNotification(c *m.ReqContext) Response { ...@@ -296,8 +320,8 @@ func DeleteAlertNotification(c *m.ReqContext) Response {
return Success("Notification deleted") return Success("Notification deleted")
} }
func DeleteAlertNotificationByUID(c *m.ReqContext) Response { func DeleteAlertNotificationByUID(c *models.ReqContext) Response {
cmd := m.DeleteAlertNotificationWithUidCommand{ cmd := models.DeleteAlertNotificationWithUidCommand{
OrgId: c.OrgId, OrgId: c.OrgId,
Uid: c.Params("uid"), Uid: c.Params("uid"),
} }
...@@ -310,7 +334,7 @@ func DeleteAlertNotificationByUID(c *m.ReqContext) Response { ...@@ -310,7 +334,7 @@ func DeleteAlertNotificationByUID(c *m.ReqContext) Response {
} }
//POST /api/alert-notifications/test //POST /api/alert-notifications/test
func NotificationTest(c *m.ReqContext, dto dtos.NotificationTestCommand) Response { func NotificationTest(c *models.ReqContext, dto dtos.NotificationTestCommand) Response {
cmd := &alerting.NotificationTestCommand{ cmd := &alerting.NotificationTestCommand{
Name: dto.Name, Name: dto.Name,
Type: dto.Type, Type: dto.Type,
...@@ -318,7 +342,7 @@ func NotificationTest(c *m.ReqContext, dto dtos.NotificationTestCommand) Respons ...@@ -318,7 +342,7 @@ func NotificationTest(c *m.ReqContext, dto dtos.NotificationTestCommand) Respons
} }
if err := bus.Dispatch(cmd); err != nil { if err := bus.Dispatch(cmd); err != nil {
if err == m.ErrSmtpNotEnabled { if err == models.ErrSmtpNotEnabled {
return Error(412, err.Error(), err) return Error(412, err.Error(), err)
} }
return Error(500, "Failed to send alert notifications", err) return Error(500, "Failed to send alert notifications", err)
...@@ -328,10 +352,10 @@ func NotificationTest(c *m.ReqContext, dto dtos.NotificationTestCommand) Respons ...@@ -328,10 +352,10 @@ func NotificationTest(c *m.ReqContext, dto dtos.NotificationTestCommand) Respons
} }
//POST /api/alerts/:alertId/pause //POST /api/alerts/:alertId/pause
func PauseAlert(c *m.ReqContext, dto dtos.PauseAlertCommand) Response { func PauseAlert(c *models.ReqContext, dto dtos.PauseAlertCommand) Response {
alertID := c.ParamsInt64("alertId") alertID := c.ParamsInt64("alertId")
query := m.GetAlertByIdQuery{Id: alertID} query := models.GetAlertByIdQuery{Id: alertID}
if err := bus.Dispatch(&query); err != nil { if err := bus.Dispatch(&query); err != nil {
return Error(500, "Get Alert failed", err) return Error(500, "Get Alert failed", err)
...@@ -346,7 +370,7 @@ func PauseAlert(c *m.ReqContext, dto dtos.PauseAlertCommand) Response { ...@@ -346,7 +370,7 @@ func PauseAlert(c *m.ReqContext, dto dtos.PauseAlertCommand) Response {
return Error(403, "Access denied to this dashboard and alert", nil) return Error(403, "Access denied to this dashboard and alert", nil)
} }
cmd := m.PauseAlertCommand{ cmd := models.PauseAlertCommand{
OrgId: c.OrgId, OrgId: c.OrgId,
AlertIds: []int64{alertID}, AlertIds: []int64{alertID},
Paused: dto.Paused, Paused: dto.Paused,
...@@ -356,10 +380,10 @@ func PauseAlert(c *m.ReqContext, dto dtos.PauseAlertCommand) Response { ...@@ -356,10 +380,10 @@ func PauseAlert(c *m.ReqContext, dto dtos.PauseAlertCommand) Response {
return Error(500, "", err) return Error(500, "", err)
} }
var response m.AlertStateType = m.AlertStateUnknown var response models.AlertStateType = models.AlertStateUnknown
pausedState := "un-paused" pausedState := "un-paused"
if cmd.Paused { if cmd.Paused {
response = m.AlertStatePaused response = models.AlertStatePaused
pausedState = "paused" pausedState = "paused"
} }
...@@ -373,8 +397,8 @@ func PauseAlert(c *m.ReqContext, dto dtos.PauseAlertCommand) Response { ...@@ -373,8 +397,8 @@ func PauseAlert(c *m.ReqContext, dto dtos.PauseAlertCommand) Response {
} }
//POST /api/admin/pause-all-alerts //POST /api/admin/pause-all-alerts
func PauseAllAlerts(c *m.ReqContext, dto dtos.PauseAllAlertsCommand) Response { func PauseAllAlerts(c *models.ReqContext, dto dtos.PauseAllAlertsCommand) Response {
updateCmd := m.PauseAllAlertCommand{ updateCmd := models.PauseAllAlertCommand{
Paused: dto.Paused, Paused: dto.Paused,
} }
...@@ -382,10 +406,10 @@ func PauseAllAlerts(c *m.ReqContext, dto dtos.PauseAllAlertsCommand) Response { ...@@ -382,10 +406,10 @@ func PauseAllAlerts(c *m.ReqContext, dto dtos.PauseAllAlertsCommand) Response {
return Error(500, "Failed to pause alerts", err) return Error(500, "Failed to pause alerts", err)
} }
var response m.AlertStateType = m.AlertStatePending var response models.AlertStateType = models.AlertStatePending
pausedState := "un paused" pausedState := "un paused"
if updateCmd.Paused { if updateCmd.Paused {
response = m.AlertStatePaused response = models.AlertStatePaused
pausedState = "paused" pausedState = "paused"
} }
......
...@@ -5,7 +5,7 @@ import ( ...@@ -5,7 +5,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
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/services/search"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
...@@ -14,24 +14,24 @@ import ( ...@@ -14,24 +14,24 @@ import (
func TestAlertingApiEndpoint(t *testing.T) { func TestAlertingApiEndpoint(t *testing.T) {
Convey("Given an alert in a dashboard with an acl", t, func() { Convey("Given an alert in a dashboard with an acl", t, func() {
singleAlert := &m.Alert{Id: 1, DashboardId: 1, Name: "singlealert"} singleAlert := &models.Alert{Id: 1, DashboardId: 1, Name: "singlealert"}
bus.AddHandler("test", func(query *m.GetAlertByIdQuery) error { bus.AddHandler("test", func(query *models.GetAlertByIdQuery) error {
query.Result = singleAlert query.Result = singleAlert
return nil return nil
}) })
viewerRole := m.ROLE_VIEWER viewerRole := models.ROLE_VIEWER
editorRole := m.ROLE_EDITOR editorRole := models.ROLE_EDITOR
aclMockResp := []*m.DashboardAclInfoDTO{} aclMockResp := []*models.DashboardAclInfoDTO{}
bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error { bus.AddHandler("test", func(query *models.GetDashboardAclInfoListQuery) error {
query.Result = aclMockResp query.Result = aclMockResp
return nil return nil
}) })
bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error { bus.AddHandler("test", func(query *models.GetTeamsByUserQuery) error {
query.Result = []*m.TeamDTO{} query.Result = []*models.TeamDTO{}
return nil return nil
}) })
...@@ -41,7 +41,7 @@ func TestAlertingApiEndpoint(t *testing.T) { ...@@ -41,7 +41,7 @@ func TestAlertingApiEndpoint(t *testing.T) {
AlertId: 1, AlertId: 1,
Paused: true, Paused: true,
} }
postAlertScenario("When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause", m.ROLE_EDITOR, cmd, func(sc *scenarioContext) { postAlertScenario("When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause", models.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
CallPauseAlert(sc) CallPauseAlert(sc)
So(sc.resp.Code, ShouldEqual, 403) So(sc.resp.Code, ShouldEqual, 403)
}) })
...@@ -49,9 +49,9 @@ func TestAlertingApiEndpoint(t *testing.T) { ...@@ -49,9 +49,9 @@ func TestAlertingApiEndpoint(t *testing.T) {
}) })
Convey("When user is editor and dashboard has default ACL", func() { Convey("When user is editor and dashboard has default ACL", func() {
aclMockResp = []*m.DashboardAclInfoDTO{ aclMockResp = []*models.DashboardAclInfoDTO{
{Role: &viewerRole, Permission: m.PERMISSION_VIEW}, {Role: &viewerRole, Permission: models.PERMISSION_VIEW},
{Role: &editorRole, Permission: m.PERMISSION_EDIT}, {Role: &editorRole, Permission: models.PERMISSION_EDIT},
} }
Convey("Should be able to pause the alert", func() { Convey("Should be able to pause the alert", func() {
...@@ -59,22 +59,22 @@ func TestAlertingApiEndpoint(t *testing.T) { ...@@ -59,22 +59,22 @@ func TestAlertingApiEndpoint(t *testing.T) {
AlertId: 1, AlertId: 1,
Paused: true, Paused: true,
} }
postAlertScenario("When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause", m.ROLE_EDITOR, cmd, func(sc *scenarioContext) { postAlertScenario("When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause", models.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
CallPauseAlert(sc) CallPauseAlert(sc)
So(sc.resp.Code, ShouldEqual, 200) So(sc.resp.Code, ShouldEqual, 200)
}) })
}) })
}) })
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alerts?dashboardId=1", "/api/alerts", m.ROLE_EDITOR, func(sc *scenarioContext) { loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alerts?dashboardId=1", "/api/alerts", models.ROLE_EDITOR, func(sc *scenarioContext) {
var searchQuery *search.Query var searchQuery *search.Query
bus.AddHandler("test", func(query *search.Query) error { bus.AddHandler("test", func(query *search.Query) error {
searchQuery = query searchQuery = query
return nil return nil
}) })
var getAlertsQuery *m.GetAlertsQuery var getAlertsQuery *models.GetAlertsQuery
bus.AddHandler("test", func(query *m.GetAlertsQuery) error { bus.AddHandler("test", func(query *models.GetAlertsQuery) error {
getAlertsQuery = query getAlertsQuery = query
return nil return nil
}) })
...@@ -86,7 +86,7 @@ func TestAlertingApiEndpoint(t *testing.T) { ...@@ -86,7 +86,7 @@ func TestAlertingApiEndpoint(t *testing.T) {
So(getAlertsQuery, ShouldNotBeNil) So(getAlertsQuery, ShouldNotBeNil)
}) })
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alerts?dashboardId=1&dashboardId=2&folderId=3&dashboardTag=abc&dashboardQuery=dbQuery&limit=5&query=alertQuery", "/api/alerts", m.ROLE_EDITOR, func(sc *scenarioContext) { loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alerts?dashboardId=1&dashboardId=2&folderId=3&dashboardTag=abc&dashboardQuery=dbQuery&limit=5&query=alertQuery", "/api/alerts", models.ROLE_EDITOR, func(sc *scenarioContext) {
var searchQuery *search.Query var searchQuery *search.Query
bus.AddHandler("test", func(query *search.Query) error { bus.AddHandler("test", func(query *search.Query) error {
searchQuery = query searchQuery = query
...@@ -97,8 +97,8 @@ func TestAlertingApiEndpoint(t *testing.T) { ...@@ -97,8 +97,8 @@ func TestAlertingApiEndpoint(t *testing.T) {
return nil return nil
}) })
var getAlertsQuery *m.GetAlertsQuery var getAlertsQuery *models.GetAlertsQuery
bus.AddHandler("test", func(query *m.GetAlertsQuery) error { bus.AddHandler("test", func(query *models.GetAlertsQuery) error {
getAlertsQuery = query getAlertsQuery = query
return nil return nil
}) })
...@@ -120,7 +120,7 @@ func TestAlertingApiEndpoint(t *testing.T) { ...@@ -120,7 +120,7 @@ func TestAlertingApiEndpoint(t *testing.T) {
So(getAlertsQuery.Query, ShouldEqual, "alertQuery") So(getAlertsQuery.Query, ShouldEqual, "alertQuery")
}) })
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alert-notifications/1", "/alert-notifications/:notificationId", m.ROLE_ADMIN, func(sc *scenarioContext) { loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alert-notifications/1", "/alert-notifications/:notificationId", models.ROLE_ADMIN, func(sc *scenarioContext) {
sc.handlerFunc = GetAlertNotificationByID sc.handlerFunc = GetAlertNotificationByID
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec() sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 404) So(sc.resp.Code, ShouldEqual, 404)
...@@ -129,19 +129,19 @@ func TestAlertingApiEndpoint(t *testing.T) { ...@@ -129,19 +129,19 @@ func TestAlertingApiEndpoint(t *testing.T) {
} }
func CallPauseAlert(sc *scenarioContext) { func CallPauseAlert(sc *scenarioContext) {
bus.AddHandler("test", func(cmd *m.PauseAlertCommand) error { bus.AddHandler("test", func(cmd *models.PauseAlertCommand) error {
return nil return nil
}) })
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec() sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
} }
func postAlertScenario(desc string, url string, routePattern string, role m.RoleType, cmd dtos.PauseAlertCommand, fn scenarioFunc) { func postAlertScenario(desc string, url string, routePattern string, role models.RoleType, cmd dtos.PauseAlertCommand, fn scenarioFunc) {
Convey(desc+" "+url, func() { Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers() defer bus.ClearBusHandlers()
sc := setupScenarioContext(url) sc := setupScenarioContext(url)
sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c sc.context = c
sc.context.UserId = TestUserID sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID sc.context.OrgId = TestOrgID
......
...@@ -183,6 +183,7 @@ func (hs *HTTPServer) registerRoutes() { ...@@ -183,6 +183,7 @@ func (hs *HTTPServer) registerRoutes() {
apiRoute.Group("/org", func(orgRoute routing.RouteRegister) { apiRoute.Group("/org", func(orgRoute routing.RouteRegister) {
orgRoute.Put("/", bind(dtos.UpdateOrgForm{}), Wrap(UpdateOrgCurrent)) orgRoute.Put("/", bind(dtos.UpdateOrgForm{}), Wrap(UpdateOrgCurrent))
orgRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), Wrap(UpdateOrgAddressCurrent)) orgRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), Wrap(UpdateOrgAddressCurrent))
orgRoute.Get("/users", Wrap(GetOrgUsersForCurrentOrg))
orgRoute.Post("/users", quota("user"), bind(models.AddOrgUserCommand{}), Wrap(AddOrgUserToCurrentOrg)) orgRoute.Post("/users", quota("user"), bind(models.AddOrgUserCommand{}), Wrap(AddOrgUserToCurrentOrg))
orgRoute.Patch("/users/:userId", bind(models.UpdateOrgUserCommand{}), Wrap(UpdateOrgUserForCurrentOrg)) orgRoute.Patch("/users/:userId", bind(models.UpdateOrgUserCommand{}), Wrap(UpdateOrgUserForCurrentOrg))
orgRoute.Delete("/users/:userId", Wrap(RemoveOrgUserForCurrentOrg)) orgRoute.Delete("/users/:userId", Wrap(RemoveOrgUserForCurrentOrg))
...@@ -199,7 +200,7 @@ func (hs *HTTPServer) registerRoutes() { ...@@ -199,7 +200,7 @@ func (hs *HTTPServer) registerRoutes() {
// current org without requirement of user to be org admin // current org without requirement of user to be org admin
apiRoute.Group("/org", func(orgRoute routing.RouteRegister) { apiRoute.Group("/org", func(orgRoute routing.RouteRegister) {
orgRoute.Get("/users", Wrap(GetOrgUsersForCurrentOrg)) orgRoute.Get("/users/lookup", Wrap(GetOrgUsersForCurrentOrgLookup))
}) })
// create new org // create new org
...@@ -343,10 +344,10 @@ func (hs *HTTPServer) registerRoutes() { ...@@ -343,10 +344,10 @@ func (hs *HTTPServer) registerRoutes() {
alertsRoute.Get("/states-for-dashboard", Wrap(GetAlertStatesForDashboard)) alertsRoute.Get("/states-for-dashboard", Wrap(GetAlertStatesForDashboard))
}) })
apiRoute.Get("/alert-notifications", Wrap(GetAlertNotifications)) apiRoute.Get("/alert-notifiers", reqEditorRole, Wrap(GetAlertNotifiers))
apiRoute.Get("/alert-notifiers", Wrap(GetAlertNotifiers))
apiRoute.Group("/alert-notifications", func(alertNotifications routing.RouteRegister) { apiRoute.Group("/alert-notifications", func(alertNotifications routing.RouteRegister) {
alertNotifications.Get("/", Wrap(GetAlertNotifications))
alertNotifications.Post("/test", bind(dtos.NotificationTestCommand{}), Wrap(NotificationTest)) alertNotifications.Post("/test", bind(dtos.NotificationTestCommand{}), Wrap(NotificationTest))
alertNotifications.Post("/", bind(models.CreateAlertNotificationCommand{}), Wrap(CreateAlertNotification)) alertNotifications.Post("/", bind(models.CreateAlertNotificationCommand{}), Wrap(CreateAlertNotification))
alertNotifications.Put("/:notificationId", bind(models.UpdateAlertNotificationCommand{}), Wrap(UpdateAlertNotification)) alertNotifications.Put("/:notificationId", bind(models.UpdateAlertNotificationCommand{}), Wrap(UpdateAlertNotification))
...@@ -357,6 +358,11 @@ func (hs *HTTPServer) registerRoutes() { ...@@ -357,6 +358,11 @@ func (hs *HTTPServer) registerRoutes() {
alertNotifications.Delete("/uid/:uid", Wrap(DeleteAlertNotificationByUID)) alertNotifications.Delete("/uid/:uid", Wrap(DeleteAlertNotificationByUID))
}, reqEditorRole) }, reqEditorRole)
// alert notifications without requirement of user to be org editor
apiRoute.Group("/alert-notifications", func(orgRoute routing.RouteRegister) {
orgRoute.Get("/lookup", Wrap(GetAlertNotificationLookup))
})
apiRoute.Get("/annotations", Wrap(GetAnnotations)) apiRoute.Get("/annotations", Wrap(GetAnnotations))
apiRoute.Post("/annotations/mass-delete", reqOrgAdmin, bind(dtos.DeleteAnnotationsCmd{}), Wrap(DeleteAnnotations)) apiRoute.Post("/annotations/mass-delete", reqOrgAdmin, bind(dtos.DeleteAnnotationsCmd{}), Wrap(DeleteAnnotations))
......
...@@ -77,6 +77,24 @@ type AlertNotification struct { ...@@ -77,6 +77,24 @@ type AlertNotification struct {
Settings *simplejson.Json `json:"settings"` Settings *simplejson.Json `json:"settings"`
} }
func NewAlertNotificationLookup(notification *models.AlertNotification) *AlertNotificationLookup {
return &AlertNotificationLookup{
Id: notification.Id,
Uid: notification.Uid,
Name: notification.Name,
Type: notification.Type,
IsDefault: notification.IsDefault,
}
}
type AlertNotificationLookup struct {
Id int64 `json:"id"`
Uid string `json:"uid"`
Name string `json:"name"`
Type string `json:"type"`
IsDefault bool `json:"isDefault"`
}
type AlertTestCommand struct { type AlertTestCommand struct {
Dashboard *simplejson.Json `json:"dashboard" binding:"Required"` Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
PanelId int64 `json:"panelId" binding:"Required"` PanelId int64 `json:"panelId" binding:"Required"`
......
...@@ -50,3 +50,9 @@ type ResetUserPasswordForm struct { ...@@ -50,3 +50,9 @@ type ResetUserPasswordForm struct {
NewPassword string `json:"newPassword"` NewPassword string `json:"newPassword"`
ConfirmPassword string `json:"confirmPassword"` ConfirmPassword string `json:"confirmPassword"`
} }
type UserLookupDTO struct {
UserID int64 `json:"userId"`
Login string `json:"login"`
AvatarURL string `json:"avatarUrl"`
}
...@@ -3,27 +3,27 @@ package api ...@@ -3,27 +3,27 @@ package api
import ( import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
) )
// POST /api/org/users // POST /api/org/users
func AddOrgUserToCurrentOrg(c *m.ReqContext, cmd m.AddOrgUserCommand) Response { func AddOrgUserToCurrentOrg(c *models.ReqContext, cmd models.AddOrgUserCommand) Response {
cmd.OrgId = c.OrgId cmd.OrgId = c.OrgId
return addOrgUserHelper(cmd) return addOrgUserHelper(cmd)
} }
// POST /api/orgs/:orgId/users // POST /api/orgs/:orgId/users
func AddOrgUser(c *m.ReqContext, cmd m.AddOrgUserCommand) Response { func AddOrgUser(c *models.ReqContext, cmd models.AddOrgUserCommand) Response {
cmd.OrgId = c.ParamsInt64(":orgId") cmd.OrgId = c.ParamsInt64(":orgId")
return addOrgUserHelper(cmd) return addOrgUserHelper(cmd)
} }
func addOrgUserHelper(cmd m.AddOrgUserCommand) Response { func addOrgUserHelper(cmd models.AddOrgUserCommand) Response {
if !cmd.Role.IsValid() { if !cmd.Role.IsValid() {
return Error(400, "Invalid role specified", nil) return Error(400, "Invalid role specified", nil)
} }
userQuery := m.GetUserByLoginQuery{LoginOrEmail: cmd.LoginOrEmail} userQuery := models.GetUserByLoginQuery{LoginOrEmail: cmd.LoginOrEmail}
err := bus.Dispatch(&userQuery) err := bus.Dispatch(&userQuery)
if err != nil { if err != nil {
return Error(404, "User not found", nil) return Error(404, "User not found", nil)
...@@ -34,7 +34,7 @@ func addOrgUserHelper(cmd m.AddOrgUserCommand) Response { ...@@ -34,7 +34,7 @@ func addOrgUserHelper(cmd m.AddOrgUserCommand) Response {
cmd.UserId = userToAdd.Id cmd.UserId = userToAdd.Id
if err := bus.Dispatch(&cmd); err != nil { if err := bus.Dispatch(&cmd); err != nil {
if err == m.ErrOrgUserAlreadyAdded { if err == models.ErrOrgUserAlreadyAdded {
return Error(409, "User is already member of this organization", nil) return Error(409, "User is already member of this organization", nil)
} }
return Error(500, "Could not add user to organization", err) return Error(500, "Could not add user to organization", err)
...@@ -44,54 +44,115 @@ func addOrgUserHelper(cmd m.AddOrgUserCommand) Response { ...@@ -44,54 +44,115 @@ func addOrgUserHelper(cmd m.AddOrgUserCommand) Response {
} }
// GET /api/org/users // GET /api/org/users
func GetOrgUsersForCurrentOrg(c *m.ReqContext) Response { func GetOrgUsersForCurrentOrg(c *models.ReqContext) Response {
return getOrgUsersHelper(c.OrgId, c.Query("query"), c.QueryInt("limit")) result, err := getOrgUsersHelper(c.OrgId, c.Query("query"), c.QueryInt("limit"))
if err != nil {
return Error(500, "Failed to get users for current organization", err)
}
return JSON(200, result)
}
// GET /api/org/users/lookup
func GetOrgUsersForCurrentOrgLookup(c *models.ReqContext) Response {
isAdmin, err := isOrgAdminFolderAdminOrTeamAdmin(c)
if err != nil {
return Error(500, "Failed to get users for current organization", err)
}
if !isAdmin {
return Error(403, "Permission denied", nil)
}
orgUsers, err := getOrgUsersHelper(c.OrgId, c.Query("query"), c.QueryInt("limit"))
if err != nil {
return Error(500, "Failed to get users for current organization", err)
}
result := make([]*dtos.UserLookupDTO, 0)
for _, u := range orgUsers {
result = append(result, &dtos.UserLookupDTO{
UserID: u.UserId,
Login: u.Login,
AvatarURL: u.AvatarUrl,
})
}
return JSON(200, result)
}
func isOrgAdminFolderAdminOrTeamAdmin(c *models.ReqContext) (bool, error) {
if c.OrgRole == models.ROLE_ADMIN {
return true, nil
}
hasAdminPermissionInFoldersQuery := models.HasAdminPermissionInFoldersQuery{SignedInUser: c.SignedInUser}
if err := bus.Dispatch(&hasAdminPermissionInFoldersQuery); err != nil {
return false, err
}
if hasAdminPermissionInFoldersQuery.Result {
return true, nil
}
isAdminOfTeamsQuery := models.IsAdminOfTeamsQuery{SignedInUser: c.SignedInUser}
if err := bus.Dispatch(&isAdminOfTeamsQuery); err != nil {
return false, err
}
return isAdminOfTeamsQuery.Result, nil
} }
// GET /api/orgs/:orgId/users // GET /api/orgs/:orgId/users
func GetOrgUsers(c *m.ReqContext) Response { func GetOrgUsers(c *models.ReqContext) Response {
return getOrgUsersHelper(c.ParamsInt64(":orgId"), "", 0) result, err := getOrgUsersHelper(c.ParamsInt64(":orgId"), "", 0)
if err != nil {
return Error(500, "Failed to get users for organization", err)
}
return JSON(200, result)
} }
func getOrgUsersHelper(orgID int64, query string, limit int) Response { func getOrgUsersHelper(orgID int64, query string, limit int) ([]*models.OrgUserDTO, error) {
q := m.GetOrgUsersQuery{ q := models.GetOrgUsersQuery{
OrgId: orgID, OrgId: orgID,
Query: query, Query: query,
Limit: limit, Limit: limit,
} }
if err := bus.Dispatch(&q); err != nil { if err := bus.Dispatch(&q); err != nil {
return Error(500, "Failed to get account user", err) return nil, err
} }
for _, user := range q.Result { for _, user := range q.Result {
user.AvatarUrl = dtos.GetGravatarUrl(user.Email) user.AvatarUrl = dtos.GetGravatarUrl(user.Email)
} }
return JSON(200, q.Result) return q.Result, nil
} }
// PATCH /api/org/users/:userId // PATCH /api/org/users/:userId
func UpdateOrgUserForCurrentOrg(c *m.ReqContext, cmd m.UpdateOrgUserCommand) Response { func UpdateOrgUserForCurrentOrg(c *models.ReqContext, cmd models.UpdateOrgUserCommand) Response {
cmd.OrgId = c.OrgId cmd.OrgId = c.OrgId
cmd.UserId = c.ParamsInt64(":userId") cmd.UserId = c.ParamsInt64(":userId")
return updateOrgUserHelper(cmd) return updateOrgUserHelper(cmd)
} }
// PATCH /api/orgs/:orgId/users/:userId // PATCH /api/orgs/:orgId/users/:userId
func UpdateOrgUser(c *m.ReqContext, cmd m.UpdateOrgUserCommand) Response { func UpdateOrgUser(c *models.ReqContext, cmd models.UpdateOrgUserCommand) Response {
cmd.OrgId = c.ParamsInt64(":orgId") cmd.OrgId = c.ParamsInt64(":orgId")
cmd.UserId = c.ParamsInt64(":userId") cmd.UserId = c.ParamsInt64(":userId")
return updateOrgUserHelper(cmd) return updateOrgUserHelper(cmd)
} }
func updateOrgUserHelper(cmd m.UpdateOrgUserCommand) Response { func updateOrgUserHelper(cmd models.UpdateOrgUserCommand) Response {
if !cmd.Role.IsValid() { if !cmd.Role.IsValid() {
return Error(400, "Invalid role specified", nil) return Error(400, "Invalid role specified", nil)
} }
if err := bus.Dispatch(&cmd); err != nil { if err := bus.Dispatch(&cmd); err != nil {
if err == m.ErrLastOrgAdmin { if err == models.ErrLastOrgAdmin {
return Error(400, "Cannot change role so that there is no organization admin left", nil) return Error(400, "Cannot change role so that there is no organization admin left", nil)
} }
return Error(500, "Failed update org user", err) return Error(500, "Failed update org user", err)
...@@ -101,8 +162,8 @@ func updateOrgUserHelper(cmd m.UpdateOrgUserCommand) Response { ...@@ -101,8 +162,8 @@ func updateOrgUserHelper(cmd m.UpdateOrgUserCommand) Response {
} }
// DELETE /api/org/users/:userId // DELETE /api/org/users/:userId
func RemoveOrgUserForCurrentOrg(c *m.ReqContext) Response { func RemoveOrgUserForCurrentOrg(c *models.ReqContext) Response {
return removeOrgUserHelper(&m.RemoveOrgUserCommand{ return removeOrgUserHelper(&models.RemoveOrgUserCommand{
UserId: c.ParamsInt64(":userId"), UserId: c.ParamsInt64(":userId"),
OrgId: c.OrgId, OrgId: c.OrgId,
ShouldDeleteOrphanedUser: true, ShouldDeleteOrphanedUser: true,
...@@ -110,16 +171,16 @@ func RemoveOrgUserForCurrentOrg(c *m.ReqContext) Response { ...@@ -110,16 +171,16 @@ func RemoveOrgUserForCurrentOrg(c *m.ReqContext) Response {
} }
// DELETE /api/orgs/:orgId/users/:userId // DELETE /api/orgs/:orgId/users/:userId
func RemoveOrgUser(c *m.ReqContext) Response { func RemoveOrgUser(c *models.ReqContext) Response {
return removeOrgUserHelper(&m.RemoveOrgUserCommand{ return removeOrgUserHelper(&models.RemoveOrgUserCommand{
UserId: c.ParamsInt64(":userId"), UserId: c.ParamsInt64(":userId"),
OrgId: c.ParamsInt64(":orgId"), OrgId: c.ParamsInt64(":orgId"),
}) })
} }
func removeOrgUserHelper(cmd *m.RemoveOrgUserCommand) Response { func removeOrgUserHelper(cmd *models.RemoveOrgUserCommand) Response {
if err := bus.Dispatch(cmd); err != nil { if err := bus.Dispatch(cmd); err != nil {
if err == m.ErrLastOrgAdmin { if err == models.ErrLastOrgAdmin {
return Error(400, "Cannot remove last organization admin", nil) return Error(400, "Cannot remove last organization admin", nil)
} }
return Error(500, "Failed to remove user from organization", err) return Error(500, "Failed to remove user from organization", err)
......
...@@ -98,3 +98,8 @@ type HasEditPermissionInFoldersQuery struct { ...@@ -98,3 +98,8 @@ type HasEditPermissionInFoldersQuery struct {
SignedInUser *SignedInUser SignedInUser *SignedInUser
Result bool Result bool
} }
type HasAdminPermissionInFoldersQuery struct {
SignedInUser *SignedInUser
Result bool
}
...@@ -88,3 +88,8 @@ type SearchTeamQueryResult struct { ...@@ -88,3 +88,8 @@ type SearchTeamQueryResult struct {
Page int `json:"page"` Page int `json:"page"`
PerPage int `json:"perPage"` PerPage int `json:"perPage"`
} }
type IsAdminOfTeamsQuery struct {
SignedInUser *SignedInUser
Result bool
}
...@@ -6,7 +6,7 @@ import ( ...@@ -6,7 +6,7 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/metrics" "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/services/search"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
...@@ -25,17 +25,18 @@ func init() { ...@@ -25,17 +25,18 @@ func init() {
bus.AddHandler("sql", GetDashboardsBySlug) bus.AddHandler("sql", GetDashboardsBySlug)
bus.AddHandler("sql", ValidateDashboardBeforeSave) bus.AddHandler("sql", ValidateDashboardBeforeSave)
bus.AddHandler("sql", HasEditPermissionInFolders) bus.AddHandler("sql", HasEditPermissionInFolders)
bus.AddHandler("sql", HasAdminPermissionInFolders)
} }
var generateNewUid func() string = util.GenerateShortUID 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 inTransaction(func(sess *DBSession) error {
return saveDashboard(sess, cmd) return saveDashboard(sess, cmd)
}) })
} }
func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error { func saveDashboard(sess *DBSession, cmd *models.SaveDashboardCommand) error {
dash := cmd.GetDashboardModel() dash := cmd.GetDashboardModel()
userId := cmd.UserId userId := cmd.UserId
...@@ -45,13 +46,13 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error { ...@@ -45,13 +46,13 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error {
} }
if dash.Id > 0 { 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) dashWithIdExists, err := sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existing)
if err != nil { if err != nil {
return err return err
} }
if !dashWithIdExists { if !dashWithIdExists {
return m.ErrDashboardNotFound return models.ErrDashboardNotFound
} }
// check for is someone else has written in between // check for is someone else has written in between
...@@ -59,13 +60,13 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error { ...@@ -59,13 +60,13 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error {
if cmd.Overwrite { if cmd.Overwrite {
dash.SetVersion(existing.Version) dash.SetVersion(existing.Version)
} else { } else {
return m.ErrDashboardVersionMismatch return models.ErrDashboardVersionMismatch
} }
} }
// do not allow plugin dashboard updates without overwrite flag // do not allow plugin dashboard updates without overwrite flag
if existing.PluginId != "" && !cmd.Overwrite { 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 { ...@@ -108,10 +109,10 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error {
} }
if affectedRows == 0 { if affectedRows == 0 {
return m.ErrDashboardNotFound return models.ErrDashboardNotFound
} }
dashVersion := &m.DashboardVersion{ dashVersion := &models.DashboardVersion{
DashboardId: dash.Id, DashboardId: dash.Id,
ParentVersion: parentVersion, ParentVersion: parentVersion,
RestoredFrom: cmd.RestoredFrom, RestoredFrom: cmd.RestoredFrom,
...@@ -126,7 +127,7 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error { ...@@ -126,7 +127,7 @@ func saveDashboard(sess *DBSession, cmd *m.SaveDashboardCommand) error {
if affectedRows, err = sess.Insert(dashVersion); err != nil { if affectedRows, err = sess.Insert(dashVersion); err != nil {
return err return err
} else if affectedRows == 0 { } else if affectedRows == 0 {
return m.ErrDashboardNotFound return models.ErrDashboardNotFound
} }
// delete existing tags // delete existing tags
...@@ -154,7 +155,7 @@ func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) { ...@@ -154,7 +155,7 @@ func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
uid := generateNewUid() 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 { if err != nil {
return "", err return "", err
} }
...@@ -164,17 +165,17 @@ func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) { ...@@ -164,17 +165,17 @@ func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
} }
} }
return "", m.ErrDashboardFailedGenerateUniqueUid return "", models.ErrDashboardFailedGenerateUniqueUid
} }
func GetDashboard(query *m.GetDashboardQuery) error { func GetDashboard(query *models.GetDashboardQuery) error {
dashboard := m.Dashboard{Slug: query.Slug, OrgId: query.OrgId, Id: query.Id, Uid: query.Uid} dashboard := models.Dashboard{Slug: query.Slug, OrgId: query.OrgId, Id: query.Id, Uid: query.Uid}
has, err := x.Get(&dashboard) has, err := x.Get(&dashboard)
if err != nil { if err != nil {
return err return err
} else if !has { } else if !has {
return m.ErrDashboardNotFound return models.ErrDashboardNotFound
} }
dashboard.SetId(dashboard.Id) dashboard.SetId(dashboard.Id)
...@@ -262,7 +263,7 @@ func makeQueryResult(query *search.FindPersistedDashboardsQuery, res []Dashboard ...@@ -262,7 +263,7 @@ func makeQueryResult(query *search.FindPersistedDashboardsQuery, res []Dashboard
Uid: item.Uid, Uid: item.Uid,
Title: item.Title, Title: item.Title,
Uri: "db/" + item.Slug, 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), Type: getHitType(item),
FolderId: item.FolderId, FolderId: item.FolderId,
FolderUid: item.FolderUid, FolderUid: item.FolderUid,
...@@ -271,7 +272,7 @@ func makeQueryResult(query *search.FindPersistedDashboardsQuery, res []Dashboard ...@@ -271,7 +272,7 @@ func makeQueryResult(query *search.FindPersistedDashboardsQuery, res []Dashboard
} }
if item.FolderId > 0 { 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) query.Result = append(query.Result, hit)
...@@ -283,7 +284,7 @@ func makeQueryResult(query *search.FindPersistedDashboardsQuery, res []Dashboard ...@@ -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 sql := `SELECT
COUNT(*) as count, COUNT(*) as count,
term term
...@@ -293,20 +294,20 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error { ...@@ -293,20 +294,20 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
GROUP BY term GROUP BY term
ORDER BY term` ORDER BY term`
query.Result = make([]*m.DashboardTagCloudItem, 0) query.Result = make([]*models.DashboardTagCloudItem, 0)
sess := x.SQL(sql, query.OrgId) sess := x.SQL(sql, query.OrgId)
err := sess.Find(&query.Result) err := sess.Find(&query.Result)
return err return err
} }
func DeleteDashboard(cmd *m.DeleteDashboardCommand) error { func DeleteDashboard(cmd *models.DeleteDashboardCommand) error {
return inTransaction(func(sess *DBSession) 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) has, err := sess.Get(&dashboard)
if err != nil { if err != nil {
return err return err
} else if !has { } else if !has {
return m.ErrDashboardNotFound return models.ErrDashboardNotFound
} }
deletes := []string{ deletes := []string{
...@@ -354,12 +355,12 @@ func DeleteDashboard(cmd *m.DeleteDashboardCommand) error { ...@@ -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 { 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) err := x.In("id", query.DashboardIds).Find(&dashboards)
query.Result = dashboards query.Result = dashboards
...@@ -368,18 +369,18 @@ func GetDashboards(query *m.GetDashboardsQuery) error { ...@@ -368,18 +369,18 @@ func GetDashboards(query *m.GetDashboardsQuery) error {
// GetDashboardPermissionsForUser returns the maximum permission the specified user has for a dashboard(s) // 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 // 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 { if len(query.DashboardIds) == 0 {
return m.ErrCommandValidationFailed return models.ErrCommandValidationFailed
} }
if query.OrgRole == m.ROLE_ADMIN { if query.OrgRole == models.ROLE_ADMIN {
var permissions = make([]*m.DashboardPermissionForUser, 0) var permissions = make([]*models.DashboardPermissionForUser, 0)
for _, d := range query.DashboardIds { for _, d := range query.DashboardIds {
permissions = append(permissions, &m.DashboardPermissionForUser{ permissions = append(permissions, &models.DashboardPermissionForUser{
DashboardId: d, DashboardId: d,
Permission: m.PERMISSION_ADMIN, Permission: models.PERMISSION_ADMIN,
PermissionName: m.PERMISSION_ADMIN.String(), PermissionName: models.PERMISSION_ADMIN.String(),
}) })
} }
query.Result = permissions query.Result = permissions
...@@ -436,8 +437,8 @@ func GetDashboardPermissionsForUser(query *m.GetDashboardPermissionsForUserQuery ...@@ -436,8 +437,8 @@ func GetDashboardPermissionsForUser(query *m.GetDashboardPermissionsForUserQuery
return err return err
} }
func GetDashboardsByPluginId(query *m.GetDashboardsByPluginIdQuery) error { func GetDashboardsByPluginId(query *models.GetDashboardsByPluginIdQuery) error {
var dashboards = make([]*m.Dashboard, 0) var dashboards = make([]*models.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)
err := x.Where(whereExpr, query.OrgId, query.PluginId).Find(&dashboards) err := x.Where(whereExpr, query.OrgId, query.PluginId).Find(&dashboards)
...@@ -449,7 +450,7 @@ type DashboardSlugDTO struct { ...@@ -449,7 +450,7 @@ type DashboardSlugDTO struct {
Slug string Slug string
} }
func GetDashboardSlugById(query *m.GetDashboardSlugByIdQuery) error { func GetDashboardSlugById(query *models.GetDashboardSlugByIdQuery) error {
var rawSql = `SELECT slug from dashboard WHERE Id=?` var rawSql = `SELECT slug from dashboard WHERE Id=?`
var slug = DashboardSlugDTO{} var slug = DashboardSlugDTO{}
...@@ -458,15 +459,15 @@ func GetDashboardSlugById(query *m.GetDashboardSlugByIdQuery) error { ...@@ -458,15 +459,15 @@ func GetDashboardSlugById(query *m.GetDashboardSlugByIdQuery) error {
if err != nil { if err != nil {
return err return err
} else if !exists { } else if !exists {
return m.ErrDashboardNotFound return models.ErrDashboardNotFound
} }
query.Result = slug.Slug query.Result = slug.Slug
return nil return nil
} }
func GetDashboardsBySlug(query *m.GetDashboardsBySlugQuery) error { func GetDashboardsBySlug(query *models.GetDashboardsBySlugQuery) error {
var dashboards []*m.Dashboard var dashboards []*models.Dashboard
if err := x.Where("org_id=? AND slug=?", query.OrgId, query.Slug).Find(&dashboards); err != nil { if err := x.Where("org_id=? AND slug=?", query.OrgId, query.Slug).Find(&dashboards); err != nil {
return err return err
...@@ -476,28 +477,28 @@ func GetDashboardsBySlug(query *m.GetDashboardsBySlugQuery) error { ...@@ -476,28 +477,28 @@ func GetDashboardsBySlug(query *m.GetDashboardsBySlugQuery) error {
return nil return nil
} }
func GetDashboardUIDById(query *m.GetDashboardRefByIdQuery) error { func GetDashboardUIDById(query *models.GetDashboardRefByIdQuery) error {
var rawSql = `SELECT uid, slug from dashboard WHERE Id=?` var rawSql = `SELECT uid, slug from dashboard WHERE Id=?`
us := &m.DashboardRef{} us := &models.DashboardRef{}
exists, err := x.SQL(rawSql, query.Id).Get(us) exists, err := x.SQL(rawSql, query.Id).Get(us)
if err != nil { if err != nil {
return err return err
} else if !exists { } else if !exists {
return m.ErrDashboardNotFound return models.ErrDashboardNotFound
} }
query.Result = us query.Result = us
return nil return nil
} }
func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDashboardBeforeSaveCommand) (err error) { func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *models.ValidateDashboardBeforeSaveCommand) (err error) {
dash := cmd.Dashboard dash := cmd.Dashboard
dashWithIdExists := false dashWithIdExists := false
var existingById m.Dashboard var existingById models.Dashboard
if dash.Id > 0 { if dash.Id > 0 {
dashWithIdExists, err = sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existingById) 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 ...@@ -506,7 +507,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
} }
if !dashWithIdExists { if !dashWithIdExists {
return m.ErrDashboardNotFound return models.ErrDashboardNotFound
} }
if dash.Uid == "" { if dash.Uid == "" {
...@@ -515,7 +516,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash ...@@ -515,7 +516,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
} }
dashWithUidExists := false dashWithUidExists := false
var existingByUid m.Dashboard var existingByUid models.Dashboard
if dash.Uid != "" { if dash.Uid != "" {
dashWithUidExists, err = sess.Where("org_id=? AND uid=?", dash.OrgId, dash.Uid).Get(&existingByUid) 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 ...@@ -525,14 +526,14 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
} }
if dash.FolderId > 0 { 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) folderExists, folderErr := sess.Where("org_id=? AND id=? AND is_folder=?", dash.OrgId, dash.FolderId, dialect.BooleanStr(true)).Get(&existingFolder)
if folderErr != nil { if folderErr != nil {
return folderErr return folderErr
} }
if !folderExists { if !folderExists {
return m.ErrDashboardFolderNotFound return models.ErrDashboardFolderNotFound
} }
} }
...@@ -541,7 +542,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash ...@@ -541,7 +542,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
} }
if dashWithIdExists && dashWithUidExists && existingById.Id != existingByUid.Id { if dashWithIdExists && dashWithUidExists && existingById.Id != existingByUid.Id {
return m.ErrDashboardWithSameUIDExists return models.ErrDashboardWithSameUIDExists
} }
existing := existingById existing := existingById
...@@ -558,7 +559,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash ...@@ -558,7 +559,7 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
if (existing.IsFolder && !dash.IsFolder) || if (existing.IsFolder && !dash.IsFolder) ||
(!existing.IsFolder && dash.IsFolder) { (!existing.IsFolder && dash.IsFolder) {
return m.ErrDashboardTypeMismatch return models.ErrDashboardTypeMismatch
} }
if !dash.IsFolder && dash.FolderId != existing.FolderId { if !dash.IsFolder && dash.FolderId != existing.FolderId {
...@@ -570,21 +571,21 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash ...@@ -570,21 +571,21 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *m.ValidateDash
if cmd.Overwrite { if cmd.Overwrite {
dash.SetVersion(existing.Version) dash.SetVersion(existing.Version)
} else { } else {
return m.ErrDashboardVersionMismatch return models.ErrDashboardVersionMismatch
} }
} }
// do not allow plugin dashboard updates without overwrite flag // do not allow plugin dashboard updates without overwrite flag
if existing.PluginId != "" && !cmd.Overwrite { if existing.PluginId != "" && !cmd.Overwrite {
return m.UpdatePluginDashboardError{PluginId: existing.PluginId} return models.UpdatePluginDashboardError{PluginId: existing.PluginId}
} }
return nil return nil
} }
func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *m.ValidateDashboardBeforeSaveCommand) error { func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *models.ValidateDashboardBeforeSaveCommand) error {
dash := cmd.Dashboard 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) 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 { if err != nil {
...@@ -593,11 +594,11 @@ func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *m.ValidateDashbo ...@@ -593,11 +594,11 @@ func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *m.ValidateDashbo
if exists && dash.Id != existing.Id { if exists && dash.Id != existing.Id {
if existing.IsFolder && !dash.IsFolder { if existing.IsFolder && !dash.IsFolder {
return m.ErrDashboardWithSameNameAsFolder return models.ErrDashboardWithSameNameAsFolder
} }
if !existing.IsFolder && dash.IsFolder { if !existing.IsFolder && dash.IsFolder {
return m.ErrDashboardFolderWithSameNameAsDashboard return models.ErrDashboardFolderWithSameNameAsDashboard
} }
if !dash.IsFolder && (dash.FolderId != existing.FolderId || dash.Id == 0) { if !dash.IsFolder && (dash.FolderId != existing.FolderId || dash.Id == 0) {
...@@ -609,15 +610,15 @@ func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *m.ValidateDashbo ...@@ -609,15 +610,15 @@ func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *m.ValidateDashbo
dash.SetUid(existing.Uid) dash.SetUid(existing.Uid)
dash.SetVersion(existing.Version) dash.SetVersion(existing.Version)
} else { } else {
return m.ErrDashboardWithSameNameInFolderExists return models.ErrDashboardWithSameNameInFolderExists
} }
} }
return nil return nil
} }
func ValidateDashboardBeforeSave(cmd *m.ValidateDashboardBeforeSaveCommand) (err error) { func ValidateDashboardBeforeSave(cmd *models.ValidateDashboardBeforeSaveCommand) (err error) {
cmd.Result = &m.ValidateDashboardBeforeSaveResult{} cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
return inTransaction(func(sess *DBSession) error { return inTransaction(func(sess *DBSession) error {
if err = getExistingDashboardByIdOrUidForUpdate(sess, cmd); err != nil { if err = getExistingDashboardByIdOrUidForUpdate(sess, cmd); err != nil {
return err return err
...@@ -631,15 +632,39 @@ func ValidateDashboardBeforeSave(cmd *m.ValidateDashboardBeforeSaveCommand) (err ...@@ -631,15 +632,39 @@ func ValidateDashboardBeforeSave(cmd *m.ValidateDashboardBeforeSaveCommand) (err
}) })
} }
func HasEditPermissionInFolders(query *m.HasEditPermissionInFoldersQuery) error { func HasEditPermissionInFolders(query *models.HasEditPermissionInFoldersQuery) error {
if query.SignedInUser.HasRole(m.ROLE_EDITOR) { if query.SignedInUser.HasRole(models.ROLE_EDITOR) {
query.Result = true query.Result = true
return nil return nil
} }
builder := &SqlBuilder{} 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.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 { type folderCount struct {
Count int64 Count int64
......
...@@ -5,7 +5,7 @@ import ( ...@@ -5,7 +5,7 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
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/services/search"
) )
...@@ -24,7 +24,7 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -24,7 +24,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("and no acls are set", func() { Convey("and no acls are set", func() {
Convey("should return all dashboards", func() { Convey("should return all dashboards", func() {
query := &search.FindPersistedDashboardsQuery{ query := &search.FindPersistedDashboardsQuery{
SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER},
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder.Id, dashInRoot.Id}, DashboardIds: []int64{folder.Id, dashInRoot.Id},
} }
...@@ -38,11 +38,11 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -38,11 +38,11 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("and acl is set for dashboard folder", func() { Convey("and acl is set for dashboard folder", func() {
var otherUser int64 = 999 var otherUser int64 = 999
testHelperUpdateDashboardAcl(folder.Id, m.DashboardAcl{DashboardId: folder.Id, OrgId: 1, UserId: otherUser, Permission: m.PERMISSION_EDIT}) testHelperUpdateDashboardAcl(folder.Id, models.DashboardAcl{DashboardId: folder.Id, OrgId: 1, UserId: otherUser, Permission: models.PERMISSION_EDIT})
Convey("should not return folder", func() { Convey("should not return folder", func() {
query := &search.FindPersistedDashboardsQuery{ query := &search.FindPersistedDashboardsQuery{
SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER},
OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id}, OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id},
} }
err := SearchDashboards(query) err := SearchDashboards(query)
...@@ -53,11 +53,11 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -53,11 +53,11 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("when the user is given permission", func() { Convey("when the user is given permission", func() {
testHelperUpdateDashboardAcl(folder.Id, m.DashboardAcl{DashboardId: folder.Id, OrgId: 1, UserId: currentUser.Id, Permission: m.PERMISSION_EDIT}) testHelperUpdateDashboardAcl(folder.Id, models.DashboardAcl{DashboardId: folder.Id, OrgId: 1, UserId: currentUser.Id, Permission: models.PERMISSION_EDIT})
Convey("should be able to access folder", func() { Convey("should be able to access folder", func() {
query := &search.FindPersistedDashboardsQuery{ query := &search.FindPersistedDashboardsQuery{
SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER},
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder.Id, dashInRoot.Id}, DashboardIds: []int64{folder.Id, dashInRoot.Id},
} }
...@@ -72,10 +72,10 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -72,10 +72,10 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("when the user is an admin", func() { Convey("when the user is an admin", func() {
Convey("should be able to access folder", func() { Convey("should be able to access folder", func() {
query := &search.FindPersistedDashboardsQuery{ query := &search.FindPersistedDashboardsQuery{
SignedInUser: &m.SignedInUser{ SignedInUser: &models.SignedInUser{
UserId: currentUser.Id, UserId: currentUser.Id,
OrgId: 1, OrgId: 1,
OrgRole: m.ROLE_ADMIN, OrgRole: models.ROLE_ADMIN,
}, },
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder.Id, dashInRoot.Id}, DashboardIds: []int64{folder.Id, dashInRoot.Id},
...@@ -92,10 +92,10 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -92,10 +92,10 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("and acl is set for dashboard child and folder has all permissions removed", func() { Convey("and acl is set for dashboard child and folder has all permissions removed", func() {
var otherUser int64 = 999 var otherUser int64 = 999
testHelperUpdateDashboardAcl(folder.Id) testHelperUpdateDashboardAcl(folder.Id)
testHelperUpdateDashboardAcl(childDash.Id, m.DashboardAcl{DashboardId: folder.Id, OrgId: 1, UserId: otherUser, Permission: m.PERMISSION_EDIT}) testHelperUpdateDashboardAcl(childDash.Id, models.DashboardAcl{DashboardId: folder.Id, OrgId: 1, UserId: otherUser, Permission: models.PERMISSION_EDIT})
Convey("should not return folder or child", func() { Convey("should not return folder or child", func() {
query := &search.FindPersistedDashboardsQuery{SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, OrgId: 1, DashboardIds: []int64{folder.Id, childDash.Id, dashInRoot.Id}} query := &search.FindPersistedDashboardsQuery{SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER}, OrgId: 1, DashboardIds: []int64{folder.Id, childDash.Id, dashInRoot.Id}}
err := SearchDashboards(query) err := SearchDashboards(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 1) So(len(query.Result), ShouldEqual, 1)
...@@ -103,10 +103,10 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -103,10 +103,10 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("when the user is given permission to child", func() { Convey("when the user is given permission to child", func() {
testHelperUpdateDashboardAcl(childDash.Id, m.DashboardAcl{DashboardId: childDash.Id, OrgId: 1, UserId: currentUser.Id, Permission: m.PERMISSION_EDIT}) testHelperUpdateDashboardAcl(childDash.Id, models.DashboardAcl{DashboardId: childDash.Id, OrgId: 1, UserId: currentUser.Id, Permission: models.PERMISSION_EDIT})
Convey("should be able to search for child dashboard but not folder", func() { Convey("should be able to search for child dashboard but not folder", func() {
query := &search.FindPersistedDashboardsQuery{SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, OrgId: 1, DashboardIds: []int64{folder.Id, childDash.Id, dashInRoot.Id}} query := &search.FindPersistedDashboardsQuery{SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER}, OrgId: 1, DashboardIds: []int64{folder.Id, childDash.Id, dashInRoot.Id}}
err := SearchDashboards(query) err := SearchDashboards(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2) So(len(query.Result), ShouldEqual, 2)
...@@ -118,10 +118,10 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -118,10 +118,10 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("when the user is an admin", func() { Convey("when the user is an admin", func() {
Convey("should be able to search for child dash and folder", func() { Convey("should be able to search for child dash and folder", func() {
query := &search.FindPersistedDashboardsQuery{ query := &search.FindPersistedDashboardsQuery{
SignedInUser: &m.SignedInUser{ SignedInUser: &models.SignedInUser{
UserId: currentUser.Id, UserId: currentUser.Id,
OrgId: 1, OrgId: 1,
OrgRole: m.ROLE_ADMIN, OrgRole: models.ROLE_ADMIN,
}, },
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder.Id, dashInRoot.Id, childDash.Id}, DashboardIds: []int64{folder.Id, dashInRoot.Id, childDash.Id},
...@@ -149,7 +149,7 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -149,7 +149,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("and one folder is expanded, the other collapsed", func() { Convey("and one folder is expanded, the other collapsed", func() {
Convey("should return dashboards in root and expanded folder", func() { Convey("should return dashboards in root and expanded folder", func() {
query := &search.FindPersistedDashboardsQuery{FolderIds: []int64{rootFolderId, folder1.Id}, SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, OrgId: 1} query := &search.FindPersistedDashboardsQuery{FolderIds: []int64{rootFolderId, folder1.Id}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER}, OrgId: 1}
err := SearchDashboards(query) err := SearchDashboards(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 4) So(len(query.Result), ShouldEqual, 4)
...@@ -162,14 +162,14 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -162,14 +162,14 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("and acl is set for one dashboard folder", func() { Convey("and acl is set for one dashboard folder", func() {
var otherUser int64 = 999 var otherUser int64 = 999
testHelperUpdateDashboardAcl(folder1.Id, m.DashboardAcl{DashboardId: folder1.Id, OrgId: 1, UserId: otherUser, Permission: m.PERMISSION_EDIT}) testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{DashboardId: folder1.Id, OrgId: 1, UserId: otherUser, Permission: models.PERMISSION_EDIT})
Convey("and a dashboard is moved from folder without acl to the folder with an acl", func() { Convey("and a dashboard is moved from folder without acl to the folder with an acl", func() {
moveDashboard(1, childDash2.Data, folder1.Id) moveDashboard(1, childDash2.Data, folder1.Id)
Convey("should not return folder with acl or its children", func() { Convey("should not return folder with acl or its children", func() {
query := &search.FindPersistedDashboardsQuery{ query := &search.FindPersistedDashboardsQuery{
SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER},
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder1.Id, childDash1.Id, childDash2.Id, dashInRoot.Id}, DashboardIds: []int64{folder1.Id, childDash1.Id, childDash2.Id, dashInRoot.Id},
} }
...@@ -184,7 +184,7 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -184,7 +184,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("should return folder without acl and its children", func() { Convey("should return folder without acl and its children", func() {
query := &search.FindPersistedDashboardsQuery{ query := &search.FindPersistedDashboardsQuery{
SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER},
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder2.Id, childDash1.Id, childDash2.Id, dashInRoot.Id}, DashboardIds: []int64{folder2.Id, childDash1.Id, childDash2.Id, dashInRoot.Id},
} }
...@@ -199,12 +199,12 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -199,12 +199,12 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("and a dashboard with an acl is moved to the folder without an acl", func() { Convey("and a dashboard with an acl is moved to the folder without an acl", func() {
testHelperUpdateDashboardAcl(childDash1.Id, m.DashboardAcl{DashboardId: childDash1.Id, OrgId: 1, UserId: otherUser, Permission: m.PERMISSION_EDIT}) testHelperUpdateDashboardAcl(childDash1.Id, models.DashboardAcl{DashboardId: childDash1.Id, OrgId: 1, UserId: otherUser, Permission: models.PERMISSION_EDIT})
moveDashboard(1, childDash1.Data, folder2.Id) moveDashboard(1, childDash1.Data, folder2.Id)
Convey("should return folder without acl but not the dashboard with acl", func() { Convey("should return folder without acl but not the dashboard with acl", func() {
query := &search.FindPersistedDashboardsQuery{ query := &search.FindPersistedDashboardsQuery{
SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER},
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder2.Id, childDash1.Id, childDash2.Id, dashInRoot.Id}, DashboardIds: []int64{folder2.Id, childDash1.Id, childDash2.Id, dashInRoot.Id},
} }
...@@ -233,8 +233,8 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -233,8 +233,8 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("Should have write access to all dashboard folders in their org", func() { Convey("Should have write access to all dashboard folders in their org", func() {
query := search.FindPersistedDashboardsQuery{ query := search.FindPersistedDashboardsQuery{
OrgId: 1, OrgId: 1,
SignedInUser: &m.SignedInUser{UserId: adminUser.Id, OrgRole: m.ROLE_ADMIN, OrgId: 1}, SignedInUser: &models.SignedInUser{UserId: adminUser.Id, OrgRole: models.ROLE_ADMIN, OrgId: 1},
Permission: m.PERMISSION_VIEW, Permission: models.PERMISSION_VIEW,
Type: "dash-folder", Type: "dash-folder",
} }
...@@ -247,11 +247,11 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -247,11 +247,11 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("should have write access to all folders and dashboards", func() { Convey("should have write access to all folders and dashboards", func() {
query := m.GetDashboardPermissionsForUserQuery{ query := models.GetDashboardPermissionsForUserQuery{
DashboardIds: []int64{folder1.Id, folder2.Id}, DashboardIds: []int64{folder1.Id, folder2.Id},
OrgId: 1, OrgId: 1,
UserId: adminUser.Id, UserId: adminUser.Id,
OrgRole: m.ROLE_ADMIN, OrgRole: models.ROLE_ADMIN,
} }
err := GetDashboardPermissionsForUser(&query) err := GetDashboardPermissionsForUser(&query)
...@@ -259,26 +259,35 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -259,26 +259,35 @@ func TestDashboardFolderDataAccess(t *testing.T) {
So(len(query.Result), ShouldEqual, 2) So(len(query.Result), ShouldEqual, 2)
So(query.Result[0].DashboardId, ShouldEqual, folder1.Id) So(query.Result[0].DashboardId, ShouldEqual, folder1.Id)
So(query.Result[0].Permission, ShouldEqual, m.PERMISSION_ADMIN) So(query.Result[0].Permission, ShouldEqual, models.PERMISSION_ADMIN)
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, models.PERMISSION_ADMIN)
}) })
Convey("should have edit permission in folders", func() { Convey("should have edit permission in folders", func() {
query := &m.HasEditPermissionInFoldersQuery{ query := &models.HasEditPermissionInFoldersQuery{
SignedInUser: &m.SignedInUser{UserId: adminUser.Id, OrgId: 1, OrgRole: m.ROLE_ADMIN}, SignedInUser: &models.SignedInUser{UserId: adminUser.Id, OrgId: 1, OrgRole: models.ROLE_ADMIN},
} }
err := HasEditPermissionInFolders(query) err := HasEditPermissionInFolders(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(query.Result, ShouldBeTrue) So(query.Result, ShouldBeTrue)
}) })
Convey("should have admin permission in folders", func() {
query := &models.HasAdminPermissionInFoldersQuery{
SignedInUser: &models.SignedInUser{UserId: adminUser.Id, OrgId: 1, OrgRole: models.ROLE_ADMIN},
}
err := HasAdminPermissionInFolders(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeTrue)
})
}) })
Convey("Editor users", func() { Convey("Editor users", func() {
query := search.FindPersistedDashboardsQuery{ query := search.FindPersistedDashboardsQuery{
OrgId: 1, OrgId: 1,
SignedInUser: &m.SignedInUser{UserId: editorUser.Id, OrgRole: m.ROLE_EDITOR, OrgId: 1}, SignedInUser: &models.SignedInUser{UserId: editorUser.Id, OrgRole: models.ROLE_EDITOR, OrgId: 1},
Permission: m.PERMISSION_EDIT, Permission: models.PERMISSION_EDIT,
} }
Convey("Should have write access to all dashboard folders with default ACL", func() { Convey("Should have write access to all dashboard folders with default ACL", func() {
...@@ -291,11 +300,11 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -291,11 +300,11 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("should have edit access to folders with default ACL", func() { Convey("should have edit access to folders with default ACL", func() {
query := m.GetDashboardPermissionsForUserQuery{ query := models.GetDashboardPermissionsForUserQuery{
DashboardIds: []int64{folder1.Id, folder2.Id}, DashboardIds: []int64{folder1.Id, folder2.Id},
OrgId: 1, OrgId: 1,
UserId: editorUser.Id, UserId: editorUser.Id,
OrgRole: m.ROLE_EDITOR, OrgRole: models.ROLE_EDITOR,
} }
err := GetDashboardPermissionsForUser(&query) err := GetDashboardPermissionsForUser(&query)
...@@ -303,13 +312,13 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -303,13 +312,13 @@ func TestDashboardFolderDataAccess(t *testing.T) {
So(len(query.Result), ShouldEqual, 2) So(len(query.Result), ShouldEqual, 2)
So(query.Result[0].DashboardId, ShouldEqual, folder1.Id) So(query.Result[0].DashboardId, ShouldEqual, folder1.Id)
So(query.Result[0].Permission, ShouldEqual, m.PERMISSION_EDIT) So(query.Result[0].Permission, ShouldEqual, models.PERMISSION_EDIT)
So(query.Result[1].DashboardId, ShouldEqual, folder2.Id) So(query.Result[1].DashboardId, ShouldEqual, folder2.Id)
So(query.Result[1].Permission, ShouldEqual, m.PERMISSION_EDIT) So(query.Result[1].Permission, ShouldEqual, models.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() {
testHelperUpdateDashboardAcl(folder1.Id, m.DashboardAcl{DashboardId: folder1.Id, OrgId: 1, UserId: editorUser.Id, Permission: m.PERMISSION_VIEW}) testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{DashboardId: folder1.Id, OrgId: 1, UserId: editorUser.Id, Permission: models.PERMISSION_VIEW})
err := SearchDashboards(&query) err := SearchDashboards(&query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
...@@ -319,20 +328,29 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -319,20 +328,29 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("should have edit permission in folders", func() { Convey("should have edit permission in folders", func() {
query := &m.HasEditPermissionInFoldersQuery{ query := &models.HasEditPermissionInFoldersQuery{
SignedInUser: &m.SignedInUser{UserId: editorUser.Id, OrgId: 1, OrgRole: m.ROLE_EDITOR}, SignedInUser: &models.SignedInUser{UserId: editorUser.Id, OrgId: 1, OrgRole: models.ROLE_EDITOR},
} }
err := HasEditPermissionInFolders(query) err := HasEditPermissionInFolders(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(query.Result, ShouldBeTrue) So(query.Result, ShouldBeTrue)
}) })
Convey("should not have admin permission in folders", func() {
query := &models.HasAdminPermissionInFoldersQuery{
SignedInUser: &models.SignedInUser{UserId: adminUser.Id, OrgId: 1, OrgRole: models.ROLE_EDITOR},
}
err := HasAdminPermissionInFolders(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeFalse)
})
}) })
Convey("Viewer users", func() { Convey("Viewer users", func() {
query := search.FindPersistedDashboardsQuery{ query := search.FindPersistedDashboardsQuery{
OrgId: 1, OrgId: 1,
SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgRole: m.ROLE_VIEWER, OrgId: 1}, SignedInUser: &models.SignedInUser{UserId: viewerUser.Id, OrgRole: models.ROLE_VIEWER, OrgId: 1},
Permission: m.PERMISSION_EDIT, Permission: models.PERMISSION_EDIT,
} }
Convey("Should have no write access to any dashboard folders with default ACL", func() { Convey("Should have no write access to any dashboard folders with default ACL", func() {
...@@ -343,11 +361,11 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -343,11 +361,11 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("should have view access to folders with default ACL", func() { Convey("should have view access to folders with default ACL", func() {
query := m.GetDashboardPermissionsForUserQuery{ query := models.GetDashboardPermissionsForUserQuery{
DashboardIds: []int64{folder1.Id, folder2.Id}, DashboardIds: []int64{folder1.Id, folder2.Id},
OrgId: 1, OrgId: 1,
UserId: viewerUser.Id, UserId: viewerUser.Id,
OrgRole: m.ROLE_VIEWER, OrgRole: models.ROLE_VIEWER,
} }
err := GetDashboardPermissionsForUser(&query) err := GetDashboardPermissionsForUser(&query)
...@@ -355,13 +373,13 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -355,13 +373,13 @@ func TestDashboardFolderDataAccess(t *testing.T) {
So(len(query.Result), ShouldEqual, 2) So(len(query.Result), ShouldEqual, 2)
So(query.Result[0].DashboardId, ShouldEqual, folder1.Id) So(query.Result[0].DashboardId, ShouldEqual, folder1.Id)
So(query.Result[0].Permission, ShouldEqual, m.PERMISSION_VIEW) So(query.Result[0].Permission, ShouldEqual, models.PERMISSION_VIEW)
So(query.Result[1].DashboardId, ShouldEqual, folder2.Id) So(query.Result[1].DashboardId, ShouldEqual, folder2.Id)
So(query.Result[1].Permission, ShouldEqual, m.PERMISSION_VIEW) So(query.Result[1].Permission, ShouldEqual, models.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() {
testHelperUpdateDashboardAcl(folder1.Id, m.DashboardAcl{DashboardId: folder1.Id, OrgId: 1, UserId: viewerUser.Id, Permission: m.PERMISSION_EDIT}) testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{DashboardId: folder1.Id, OrgId: 1, UserId: viewerUser.Id, Permission: models.PERMISSION_EDIT})
err := SearchDashboards(&query) err := SearchDashboards(&query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
...@@ -371,20 +389,29 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -371,20 +389,29 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("should not have edit permission in folders", func() { Convey("should not have edit permission in folders", func() {
query := &m.HasEditPermissionInFoldersQuery{ query := &models.HasEditPermissionInFoldersQuery{
SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, SignedInUser: &models.SignedInUser{UserId: viewerUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER},
} }
err := HasEditPermissionInFolders(query) err := HasEditPermissionInFolders(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(query.Result, ShouldBeFalse) So(query.Result, ShouldBeFalse)
}) })
Convey("should not have admin permission in folders", func() {
query := &models.HasAdminPermissionInFoldersQuery{
SignedInUser: &models.SignedInUser{UserId: adminUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER},
}
err := HasAdminPermissionInFolders(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() { 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}) testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{DashboardId: folder1.Id, OrgId: 1, UserId: viewerUser.Id, Permission: models.PERMISSION_ADMIN})
Convey("should have edit permission in folders", func() { Convey("should have edit permission in folders", func() {
query := &m.HasEditPermissionInFoldersQuery{ query := &models.HasEditPermissionInFoldersQuery{
SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, SignedInUser: &models.SignedInUser{UserId: viewerUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER},
} }
err := HasEditPermissionInFolders(query) err := HasEditPermissionInFolders(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
...@@ -393,11 +420,11 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -393,11 +420,11 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("and edit permission is given for user with org role viewer in one dashboard folder", func() { 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}) testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{DashboardId: folder1.Id, OrgId: 1, UserId: viewerUser.Id, Permission: models.PERMISSION_EDIT})
Convey("should have edit permission in folders", func() { Convey("should have edit permission in folders", func() {
query := &m.HasEditPermissionInFoldersQuery{ query := &models.HasEditPermissionInFoldersQuery{
SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgId: 1, OrgRole: m.ROLE_VIEWER}, SignedInUser: &models.SignedInUser{UserId: viewerUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER},
} }
err := HasEditPermissionInFolders(query) err := HasEditPermissionInFolders(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
......
...@@ -5,7 +5,7 @@ import ( ...@@ -5,7 +5,7 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
) )
type Test struct { type Test struct {
...@@ -17,11 +17,11 @@ func TestDataAccess(t *testing.T) { ...@@ -17,11 +17,11 @@ func TestDataAccess(t *testing.T) {
Convey("Testing DB", t, func() { Convey("Testing DB", t, func() {
InitTestDB(t) InitTestDB(t)
Convey("Can add datasource", func() { Convey("Can add datasource", func() {
err := AddDataSource(&m.AddDataSourceCommand{ err := AddDataSource(&models.AddDataSourceCommand{
OrgId: 10, OrgId: 10,
Name: "laban", Name: "laban",
Type: m.DS_INFLUXDB, Type: models.DS_INFLUXDB,
Access: m.DS_ACCESS_DIRECT, Access: models.DS_ACCESS_DIRECT,
Url: "http://test", Url: "http://test",
Database: "site", Database: "site",
ReadOnly: true, ReadOnly: true,
...@@ -29,7 +29,7 @@ func TestDataAccess(t *testing.T) { ...@@ -29,7 +29,7 @@ func TestDataAccess(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
query := m.GetDataSourcesQuery{OrgId: 10} query := models.GetDataSourcesQuery{OrgId: 10}
err = GetDataSources(&query) err = GetDataSources(&query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
...@@ -43,28 +43,28 @@ func TestDataAccess(t *testing.T) { ...@@ -43,28 +43,28 @@ func TestDataAccess(t *testing.T) {
}) })
Convey("Given a datasource", func() { Convey("Given a datasource", func() {
err := AddDataSource(&m.AddDataSourceCommand{ err := AddDataSource(&models.AddDataSourceCommand{
OrgId: 10, OrgId: 10,
Name: "nisse", Name: "nisse",
Type: m.DS_GRAPHITE, Type: models.DS_GRAPHITE,
Access: m.DS_ACCESS_DIRECT, Access: models.DS_ACCESS_DIRECT,
Url: "http://test", Url: "http://test",
}) })
So(err, ShouldBeNil) So(err, ShouldBeNil)
query := m.GetDataSourcesQuery{OrgId: 10} query := models.GetDataSourcesQuery{OrgId: 10}
err = GetDataSources(&query) err = GetDataSources(&query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
ds := query.Result[0] ds := query.Result[0]
Convey(" updated ", func() { Convey(" updated ", func() {
cmd := &m.UpdateDataSourceCommand{ cmd := &models.UpdateDataSourceCommand{
Id: ds.Id, Id: ds.Id,
OrgId: 10, OrgId: 10,
Name: "nisse", Name: "nisse",
Type: m.DS_GRAPHITE, Type: models.DS_GRAPHITE,
Access: m.DS_ACCESS_PROXY, Access: models.DS_ACCESS_PROXY,
Url: "http://test", Url: "http://test",
Version: ds.Version, Version: ds.Version,
} }
...@@ -75,27 +75,27 @@ func TestDataAccess(t *testing.T) { ...@@ -75,27 +75,27 @@ func TestDataAccess(t *testing.T) {
}) })
Convey("when someone else updated between read and update", func() { Convey("when someone else updated between read and update", func() {
query := m.GetDataSourcesQuery{OrgId: 10} query := models.GetDataSourcesQuery{OrgId: 10}
err = GetDataSources(&query) err = GetDataSources(&query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
ds := query.Result[0] ds := query.Result[0]
intendedUpdate := &m.UpdateDataSourceCommand{ intendedUpdate := &models.UpdateDataSourceCommand{
Id: ds.Id, Id: ds.Id,
OrgId: 10, OrgId: 10,
Name: "nisse", Name: "nisse",
Type: m.DS_GRAPHITE, Type: models.DS_GRAPHITE,
Access: m.DS_ACCESS_PROXY, Access: models.DS_ACCESS_PROXY,
Url: "http://test", Url: "http://test",
Version: ds.Version, Version: ds.Version,
} }
updateFromOtherUser := &m.UpdateDataSourceCommand{ updateFromOtherUser := &models.UpdateDataSourceCommand{
Id: ds.Id, Id: ds.Id,
OrgId: 10, OrgId: 10,
Name: "nisse", Name: "nisse",
Type: m.DS_GRAPHITE, Type: models.DS_GRAPHITE,
Access: m.DS_ACCESS_PROXY, Access: models.DS_ACCESS_PROXY,
Url: "http://test", Url: "http://test",
Version: ds.Version, Version: ds.Version,
} }
...@@ -108,12 +108,12 @@ func TestDataAccess(t *testing.T) { ...@@ -108,12 +108,12 @@ func TestDataAccess(t *testing.T) {
}) })
Convey("updating datasource without version", func() { Convey("updating datasource without version", func() {
cmd := &m.UpdateDataSourceCommand{ cmd := &models.UpdateDataSourceCommand{
Id: ds.Id, Id: ds.Id,
OrgId: 10, OrgId: 10,
Name: "nisse", Name: "nisse",
Type: m.DS_GRAPHITE, Type: models.DS_GRAPHITE,
Access: m.DS_ACCESS_PROXY, Access: models.DS_ACCESS_PROXY,
Url: "http://test", Url: "http://test",
} }
...@@ -124,12 +124,12 @@ func TestDataAccess(t *testing.T) { ...@@ -124,12 +124,12 @@ func TestDataAccess(t *testing.T) {
}) })
Convey("updating datasource without higher version", func() { Convey("updating datasource without higher version", func() {
cmd := &m.UpdateDataSourceCommand{ cmd := &models.UpdateDataSourceCommand{
Id: ds.Id, Id: ds.Id,
OrgId: 10, OrgId: 10,
Name: "nisse", Name: "nisse",
Type: m.DS_GRAPHITE, Type: models.DS_GRAPHITE,
Access: m.DS_ACCESS_PROXY, Access: models.DS_ACCESS_PROXY,
Url: "http://test", Url: "http://test",
Version: 90000, Version: 90000,
} }
...@@ -142,7 +142,7 @@ func TestDataAccess(t *testing.T) { ...@@ -142,7 +142,7 @@ func TestDataAccess(t *testing.T) {
}) })
Convey("Can delete datasource by id", func() { Convey("Can delete datasource by id", func() {
err := DeleteDataSourceById(&m.DeleteDataSourceByIdCommand{Id: ds.Id, OrgId: ds.OrgId}) err := DeleteDataSourceById(&models.DeleteDataSourceByIdCommand{Id: ds.Id, OrgId: ds.OrgId})
So(err, ShouldBeNil) So(err, ShouldBeNil)
GetDataSources(&query) GetDataSources(&query)
...@@ -150,7 +150,7 @@ func TestDataAccess(t *testing.T) { ...@@ -150,7 +150,7 @@ func TestDataAccess(t *testing.T) {
}) })
Convey("Can delete datasource by name", func() { Convey("Can delete datasource by name", func() {
err := DeleteDataSourceByName(&m.DeleteDataSourceByNameCommand{Name: ds.Name, OrgId: ds.OrgId}) err := DeleteDataSourceByName(&models.DeleteDataSourceByNameCommand{Name: ds.Name, OrgId: ds.OrgId})
So(err, ShouldBeNil) So(err, ShouldBeNil)
GetDataSources(&query) GetDataSources(&query)
...@@ -158,7 +158,7 @@ func TestDataAccess(t *testing.T) { ...@@ -158,7 +158,7 @@ func TestDataAccess(t *testing.T) {
}) })
Convey("Can not delete datasource with wrong orgId", func() { Convey("Can not delete datasource with wrong orgId", func() {
err := DeleteDataSourceById(&m.DeleteDataSourceByIdCommand{Id: ds.Id, OrgId: 123123}) err := DeleteDataSourceById(&models.DeleteDataSourceByIdCommand{Id: ds.Id, OrgId: 123123})
So(err, ShouldBeNil) So(err, ShouldBeNil)
GetDataSources(&query) GetDataSources(&query)
......
...@@ -6,7 +6,7 @@ import ( ...@@ -6,7 +6,7 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
) )
func init() { func init() {
...@@ -21,6 +21,7 @@ func init() { ...@@ -21,6 +21,7 @@ func init() {
bus.AddHandler("sql", UpdateTeamMember) bus.AddHandler("sql", UpdateTeamMember)
bus.AddHandler("sql", RemoveTeamMember) bus.AddHandler("sql", RemoveTeamMember)
bus.AddHandler("sql", GetTeamMembers) bus.AddHandler("sql", GetTeamMembers)
bus.AddHandler("sql", IsAdminOfTeams)
} }
func getTeamSearchSqlBase() string { func getTeamSearchSqlBase() string {
...@@ -45,16 +46,16 @@ func getTeamSelectSqlBase() string { ...@@ -45,16 +46,16 @@ func getTeamSelectSqlBase() string {
FROM team as team ` FROM team as team `
} }
func CreateTeam(cmd *m.CreateTeamCommand) error { func CreateTeam(cmd *models.CreateTeamCommand) error {
return inTransaction(func(sess *DBSession) error { return inTransaction(func(sess *DBSession) error {
if isNameTaken, err := isTeamNameTaken(cmd.OrgId, cmd.Name, 0, sess); err != nil { if isNameTaken, err := isTeamNameTaken(cmd.OrgId, cmd.Name, 0, sess); err != nil {
return err return err
} else if isNameTaken { } else if isNameTaken {
return m.ErrTeamNameTaken return models.ErrTeamNameTaken
} }
team := m.Team{ team := models.Team{
Name: cmd.Name, Name: cmd.Name,
Email: cmd.Email, Email: cmd.Email,
OrgId: cmd.OrgId, OrgId: cmd.OrgId,
...@@ -70,16 +71,16 @@ func CreateTeam(cmd *m.CreateTeamCommand) error { ...@@ -70,16 +71,16 @@ func CreateTeam(cmd *m.CreateTeamCommand) error {
}) })
} }
func UpdateTeam(cmd *m.UpdateTeamCommand) error { func UpdateTeam(cmd *models.UpdateTeamCommand) error {
return inTransaction(func(sess *DBSession) error { return inTransaction(func(sess *DBSession) error {
if isNameTaken, err := isTeamNameTaken(cmd.OrgId, cmd.Name, cmd.Id, sess); err != nil { if isNameTaken, err := isTeamNameTaken(cmd.OrgId, cmd.Name, cmd.Id, sess); err != nil {
return err return err
} else if isNameTaken { } else if isNameTaken {
return m.ErrTeamNameTaken return models.ErrTeamNameTaken
} }
team := m.Team{ team := models.Team{
Name: cmd.Name, Name: cmd.Name,
Email: cmd.Email, Email: cmd.Email,
Updated: time.Now(), Updated: time.Now(),
...@@ -94,7 +95,7 @@ func UpdateTeam(cmd *m.UpdateTeamCommand) error { ...@@ -94,7 +95,7 @@ func UpdateTeam(cmd *m.UpdateTeamCommand) error {
} }
if affectedRows == 0 { if affectedRows == 0 {
return m.ErrTeamNotFound return models.ErrTeamNotFound
} }
return nil return nil
...@@ -102,7 +103,7 @@ func UpdateTeam(cmd *m.UpdateTeamCommand) error { ...@@ -102,7 +103,7 @@ func UpdateTeam(cmd *m.UpdateTeamCommand) error {
} }
// DeleteTeam will delete a team, its member and any permissions connected to the team // DeleteTeam will delete a team, its member and any permissions connected to the team
func DeleteTeam(cmd *m.DeleteTeamCommand) error { func DeleteTeam(cmd *models.DeleteTeamCommand) error {
return inTransaction(func(sess *DBSession) error { return inTransaction(func(sess *DBSession) error {
if _, err := teamExists(cmd.OrgId, cmd.Id, sess); err != nil { if _, err := teamExists(cmd.OrgId, cmd.Id, sess); err != nil {
return err return err
...@@ -128,14 +129,14 @@ func teamExists(orgId int64, teamId int64, sess *DBSession) (bool, error) { ...@@ -128,14 +129,14 @@ func teamExists(orgId int64, teamId int64, sess *DBSession) (bool, error) {
if res, err := sess.Query("SELECT 1 from team WHERE org_id=? and id=?", orgId, teamId); err != nil { if res, err := sess.Query("SELECT 1 from team WHERE org_id=? and id=?", orgId, teamId); err != nil {
return false, err return false, err
} else if len(res) != 1 { } else if len(res) != 1 {
return false, m.ErrTeamNotFound return false, models.ErrTeamNotFound
} }
return true, nil return true, nil
} }
func isTeamNameTaken(orgId int64, name string, existingId int64, sess *DBSession) (bool, error) { func isTeamNameTaken(orgId int64, name string, existingId int64, sess *DBSession) (bool, error) {
var team m.Team var team models.Team
exists, err := sess.Where("org_id=? and name=?", orgId, name).Get(&team) exists, err := sess.Where("org_id=? and name=?", orgId, name).Get(&team)
if err != nil { if err != nil {
...@@ -149,9 +150,9 @@ func isTeamNameTaken(orgId int64, name string, existingId int64, sess *DBSession ...@@ -149,9 +150,9 @@ func isTeamNameTaken(orgId int64, name string, existingId int64, sess *DBSession
return false, nil return false, nil
} }
func SearchTeams(query *m.SearchTeamsQuery) error { func SearchTeams(query *models.SearchTeamsQuery) error {
query.Result = m.SearchTeamQueryResult{ query.Result = models.SearchTeamQueryResult{
Teams: make([]*m.TeamDTO, 0), Teams: make([]*models.TeamDTO, 0),
} }
queryWithWildcards := "%" + query.Query + "%" queryWithWildcards := "%" + query.Query + "%"
...@@ -189,7 +190,7 @@ func SearchTeams(query *m.SearchTeamsQuery) error { ...@@ -189,7 +190,7 @@ func SearchTeams(query *m.SearchTeamsQuery) error {
return err return err
} }
team := m.Team{} team := models.Team{}
countSess := x.Table("team") countSess := x.Table("team")
if query.Query != "" { if query.Query != "" {
countSess.Where(`name `+dialect.LikeStr()+` ?`, queryWithWildcards) countSess.Where(`name `+dialect.LikeStr()+` ?`, queryWithWildcards)
...@@ -205,13 +206,13 @@ func SearchTeams(query *m.SearchTeamsQuery) error { ...@@ -205,13 +206,13 @@ func SearchTeams(query *m.SearchTeamsQuery) error {
return err return err
} }
func GetTeamById(query *m.GetTeamByIdQuery) error { func GetTeamById(query *models.GetTeamByIdQuery) error {
var sql bytes.Buffer var sql bytes.Buffer
sql.WriteString(getTeamSelectSqlBase()) sql.WriteString(getTeamSelectSqlBase())
sql.WriteString(` WHERE team.org_id = ? and team.id = ?`) sql.WriteString(` WHERE team.org_id = ? and team.id = ?`)
var team m.TeamDTO var team models.TeamDTO
exists, err := x.SQL(sql.String(), query.OrgId, query.Id).Get(&team) exists, err := x.SQL(sql.String(), query.OrgId, query.Id).Get(&team)
if err != nil { if err != nil {
...@@ -219,7 +220,7 @@ func GetTeamById(query *m.GetTeamByIdQuery) error { ...@@ -219,7 +220,7 @@ func GetTeamById(query *m.GetTeamByIdQuery) error {
} }
if !exists { if !exists {
return m.ErrTeamNotFound return models.ErrTeamNotFound
} }
query.Result = &team query.Result = &team
...@@ -227,8 +228,8 @@ func GetTeamById(query *m.GetTeamByIdQuery) error { ...@@ -227,8 +228,8 @@ func GetTeamById(query *m.GetTeamByIdQuery) error {
} }
// GetTeamsByUser is used by the Guardian when checking a users' permissions // GetTeamsByUser is used by the Guardian when checking a users' permissions
func GetTeamsByUser(query *m.GetTeamsByUserQuery) error { func GetTeamsByUser(query *models.GetTeamsByUserQuery) error {
query.Result = make([]*m.TeamDTO, 0) query.Result = make([]*models.TeamDTO, 0)
var sql bytes.Buffer var sql bytes.Buffer
...@@ -241,19 +242,19 @@ func GetTeamsByUser(query *m.GetTeamsByUserQuery) error { ...@@ -241,19 +242,19 @@ func GetTeamsByUser(query *m.GetTeamsByUserQuery) error {
} }
// AddTeamMember adds a user to a team // AddTeamMember adds a user to a team
func AddTeamMember(cmd *m.AddTeamMemberCommand) error { func AddTeamMember(cmd *models.AddTeamMemberCommand) error {
return inTransaction(func(sess *DBSession) error { return inTransaction(func(sess *DBSession) error {
if res, err := sess.Query("SELECT 1 from team_member WHERE org_id=? and team_id=? and user_id=?", cmd.OrgId, cmd.TeamId, cmd.UserId); err != nil { if res, err := sess.Query("SELECT 1 from team_member WHERE org_id=? and team_id=? and user_id=?", cmd.OrgId, cmd.TeamId, cmd.UserId); err != nil {
return err return err
} else if len(res) == 1 { } else if len(res) == 1 {
return m.ErrTeamMemberAlreadyAdded return models.ErrTeamMemberAlreadyAdded
} }
if _, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil { if _, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil {
return err return err
} }
entity := m.TeamMember{ entity := models.TeamMember{
OrgId: cmd.OrgId, OrgId: cmd.OrgId,
TeamId: cmd.TeamId, TeamId: cmd.TeamId,
UserId: cmd.UserId, UserId: cmd.UserId,
...@@ -268,23 +269,23 @@ func AddTeamMember(cmd *m.AddTeamMemberCommand) error { ...@@ -268,23 +269,23 @@ func AddTeamMember(cmd *m.AddTeamMemberCommand) error {
}) })
} }
func getTeamMember(sess *DBSession, orgId int64, teamId int64, userId int64) (m.TeamMember, error) { func getTeamMember(sess *DBSession, orgId int64, teamId int64, userId int64) (models.TeamMember, error) {
rawSql := `SELECT * FROM team_member WHERE org_id=? and team_id=? and user_id=?` rawSql := `SELECT * FROM team_member WHERE org_id=? and team_id=? and user_id=?`
var member m.TeamMember var member models.TeamMember
exists, err := sess.SQL(rawSql, orgId, teamId, userId).Get(&member) exists, err := sess.SQL(rawSql, orgId, teamId, userId).Get(&member)
if err != nil { if err != nil {
return member, err return member, err
} }
if !exists { if !exists {
return member, m.ErrTeamMemberNotFound return member, models.ErrTeamMemberNotFound
} }
return member, nil return member, nil
} }
// UpdateTeamMember updates a team member // UpdateTeamMember updates a team member
func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { func UpdateTeamMember(cmd *models.UpdateTeamMemberCommand) error {
return inTransaction(func(sess *DBSession) error { return inTransaction(func(sess *DBSession) error {
member, err := getTeamMember(sess, cmd.OrgId, cmd.TeamId, cmd.UserId) member, err := getTeamMember(sess, cmd.OrgId, cmd.TeamId, cmd.UserId)
if err != nil { if err != nil {
...@@ -298,7 +299,7 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { ...@@ -298,7 +299,7 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error {
} }
} }
if cmd.Permission != m.PERMISSION_ADMIN { // make sure we don't get invalid permission levels in store if cmd.Permission != models.PERMISSION_ADMIN { // make sure we don't get invalid permission levels in store
cmd.Permission = 0 cmd.Permission = 0
} }
...@@ -310,7 +311,7 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error { ...@@ -310,7 +311,7 @@ func UpdateTeamMember(cmd *m.UpdateTeamMemberCommand) error {
} }
// RemoveTeamMember removes a member from a team // RemoveTeamMember removes a member from a team
func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error { func RemoveTeamMember(cmd *models.RemoveTeamMemberCommand) error {
return inTransaction(func(sess *DBSession) error { return inTransaction(func(sess *DBSession) error {
if _, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil { if _, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil {
return err return err
...@@ -330,7 +331,7 @@ func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error { ...@@ -330,7 +331,7 @@ func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error {
} }
rows, err := res.RowsAffected() rows, err := res.RowsAffected()
if rows == 0 { if rows == 0 {
return m.ErrTeamMemberNotFound return models.ErrTeamMemberNotFound
} }
return err return err
...@@ -340,7 +341,7 @@ func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error { ...@@ -340,7 +341,7 @@ func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error {
func isLastAdmin(sess *DBSession, orgId int64, teamId int64, userId int64) (bool, error) { func isLastAdmin(sess *DBSession, orgId int64, teamId int64, userId int64) (bool, error) {
rawSql := "SELECT user_id FROM team_member WHERE org_id=? and team_id=? and permission=?" rawSql := "SELECT user_id FROM team_member WHERE org_id=? and team_id=? and permission=?"
userIds := []*int64{} userIds := []*int64{}
err := sess.SQL(rawSql, orgId, teamId, m.PERMISSION_ADMIN).Find(&userIds) err := sess.SQL(rawSql, orgId, teamId, models.PERMISSION_ADMIN).Find(&userIds)
if err != nil { if err != nil {
return false, err return false, err
} }
...@@ -354,15 +355,15 @@ func isLastAdmin(sess *DBSession, orgId int64, teamId int64, userId int64) (bool ...@@ -354,15 +355,15 @@ func isLastAdmin(sess *DBSession, orgId int64, teamId int64, userId int64) (bool
} }
if isAdmin && len(userIds) == 1 { if isAdmin && len(userIds) == 1 {
return true, m.ErrLastTeamAdmin return true, models.ErrLastTeamAdmin
} }
return false, err return false, err
} }
// GetTeamMembers return a list of members for the specified team // GetTeamMembers return a list of members for the specified team
func GetTeamMembers(query *m.GetTeamMembersQuery) error { func GetTeamMembers(query *models.GetTeamMembersQuery) error {
query.Result = make([]*m.TeamMemberDTO, 0) query.Result = make([]*models.TeamMemberDTO, 0)
sess := x.Table("team_member") sess := x.Table("team_member")
sess.Join("INNER", x.Dialect().Quote("user"), fmt.Sprintf("team_member.user_id=%s.id", x.Dialect().Quote("user"))) sess.Join("INNER", x.Dialect().Quote("user"), fmt.Sprintf("team_member.user_id=%s.id", x.Dialect().Quote("user")))
...@@ -392,3 +393,21 @@ func GetTeamMembers(query *m.GetTeamMembersQuery) error { ...@@ -392,3 +393,21 @@ func GetTeamMembers(query *m.GetTeamMembersQuery) error {
err := sess.Find(&query.Result) err := sess.Find(&query.Result)
return err return err
} }
func IsAdminOfTeams(query *models.IsAdminOfTeamsQuery) error {
builder := &SqlBuilder{}
builder.Write("SELECT COUNT(team.id) AS count FROM team INNER JOIN team_member ON team_member.team_id = team.id WHERE team.org_id = ? AND team_member.user_id = ? AND team_member.permission = ?", query.SignedInUser.OrgId, query.SignedInUser.UserId, models.PERMISSION_ADMIN)
type teamCount struct {
Count int64
}
resp := make([]*teamCount, 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
}
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
) )
func TestTeamCommandsAndQueries(t *testing.T) { func TestTeamCommandsAndQueries(t *testing.T) {
...@@ -18,7 +18,7 @@ func TestTeamCommandsAndQueries(t *testing.T) { ...@@ -18,7 +18,7 @@ func TestTeamCommandsAndQueries(t *testing.T) {
Convey("Given saved users and two teams", func() { Convey("Given saved users and two teams", func() {
var userIds []int64 var userIds []int64
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
userCmd := &m.CreateUserCommand{ userCmd := &models.CreateUserCommand{
Email: fmt.Sprint("user", i, "@test.com"), Email: fmt.Sprint("user", i, "@test.com"),
Name: fmt.Sprint("user", i), Name: fmt.Sprint("user", i),
Login: fmt.Sprint("loginuser", i), Login: fmt.Sprint("loginuser", i),
...@@ -29,8 +29,8 @@ func TestTeamCommandsAndQueries(t *testing.T) { ...@@ -29,8 +29,8 @@ func TestTeamCommandsAndQueries(t *testing.T) {
} }
var testOrgId int64 = 1 var testOrgId int64 = 1
group1 := m.CreateTeamCommand{OrgId: testOrgId, Name: "group1 name", Email: "test1@test.com"} group1 := models.CreateTeamCommand{OrgId: testOrgId, Name: "group1 name", Email: "test1@test.com"}
group2 := m.CreateTeamCommand{OrgId: testOrgId, Name: "group2 name", Email: "test2@test.com"} group2 := models.CreateTeamCommand{OrgId: testOrgId, Name: "group2 name", Email: "test2@test.com"}
err := CreateTeam(&group1) err := CreateTeam(&group1)
So(err, ShouldBeNil) So(err, ShouldBeNil)
...@@ -38,7 +38,7 @@ func TestTeamCommandsAndQueries(t *testing.T) { ...@@ -38,7 +38,7 @@ func TestTeamCommandsAndQueries(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
Convey("Should be able to create teams and add users", func() { Convey("Should be able to create teams and add users", func() {
query := &m.SearchTeamsQuery{OrgId: testOrgId, Name: "group1 name", Page: 1, Limit: 10} query := &models.SearchTeamsQuery{OrgId: testOrgId, Name: "group1 name", Page: 1, Limit: 10}
err = SearchTeams(query) err = SearchTeams(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(query.Page, ShouldEqual, 1) So(query.Page, ShouldEqual, 1)
...@@ -48,12 +48,12 @@ func TestTeamCommandsAndQueries(t *testing.T) { ...@@ -48,12 +48,12 @@ func TestTeamCommandsAndQueries(t *testing.T) {
So(team1.Email, ShouldEqual, "test1@test.com") So(team1.Email, ShouldEqual, "test1@test.com")
So(team1.OrgId, ShouldEqual, testOrgId) So(team1.OrgId, ShouldEqual, testOrgId)
err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userIds[0]}) err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userIds[0]})
So(err, ShouldBeNil) So(err, ShouldBeNil)
err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userIds[1], External: true}) err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userIds[1], External: true})
So(err, ShouldBeNil) So(err, ShouldBeNil)
q1 := &m.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id} q1 := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id}
err = GetTeamMembers(q1) err = GetTeamMembers(q1)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(q1.Result, ShouldHaveLength, 2) So(q1.Result, ShouldHaveLength, 2)
...@@ -65,7 +65,7 @@ func TestTeamCommandsAndQueries(t *testing.T) { ...@@ -65,7 +65,7 @@ func TestTeamCommandsAndQueries(t *testing.T) {
So(q1.Result[1].OrgId, ShouldEqual, testOrgId) So(q1.Result[1].OrgId, ShouldEqual, testOrgId)
So(q1.Result[1].External, ShouldEqual, true) So(q1.Result[1].External, ShouldEqual, true)
q2 := &m.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id, External: true} q2 := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id, External: true}
err = GetTeamMembers(q2) err = GetTeamMembers(q2)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(q2.Result, ShouldHaveLength, 1) So(q2.Result, ShouldHaveLength, 1)
...@@ -77,20 +77,20 @@ func TestTeamCommandsAndQueries(t *testing.T) { ...@@ -77,20 +77,20 @@ func TestTeamCommandsAndQueries(t *testing.T) {
Convey("Should return latest auth module for users when getting team members", func() { Convey("Should return latest auth module for users when getting team members", func() {
userId := userIds[1] userId := userIds[1]
err := SetAuthInfo(&m.SetAuthInfoCommand{UserId: userId, AuthModule: "oauth_github", AuthId: "1234567"}) err := SetAuthInfo(&models.SetAuthInfoCommand{UserId: userId, AuthModule: "oauth_github", AuthId: "1234567"})
So(err, ShouldBeNil) So(err, ShouldBeNil)
teamQuery := &m.SearchTeamsQuery{OrgId: testOrgId, Name: "group1 name", Page: 1, Limit: 10} teamQuery := &models.SearchTeamsQuery{OrgId: testOrgId, Name: "group1 name", Page: 1, Limit: 10}
err = SearchTeams(teamQuery) err = SearchTeams(teamQuery)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(teamQuery.Page, ShouldEqual, 1) So(teamQuery.Page, ShouldEqual, 1)
team1 := teamQuery.Result.Teams[0] team1 := teamQuery.Result.Teams[0]
err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userId, External: true}) err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userId, External: true})
So(err, ShouldBeNil) So(err, ShouldBeNil)
memberQuery := &m.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id, External: true} memberQuery := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id, External: true}
err = GetTeamMembers(memberQuery) err = GetTeamMembers(memberQuery)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(memberQuery.Result, ShouldHaveLength, 1) So(memberQuery.Result, ShouldHaveLength, 1)
...@@ -104,44 +104,44 @@ func TestTeamCommandsAndQueries(t *testing.T) { ...@@ -104,44 +104,44 @@ func TestTeamCommandsAndQueries(t *testing.T) {
Convey("Should be able to update users in a team", func() { Convey("Should be able to update users in a team", func() {
userId := userIds[0] userId := userIds[0]
team := group1.Result team := group1.Result
addMemberCmd := m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team.Id, UserId: userId} addMemberCmd := models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team.Id, UserId: userId}
err = AddTeamMember(&addMemberCmd) err = AddTeamMember(&addMemberCmd)
So(err, ShouldBeNil) So(err, ShouldBeNil)
qBeforeUpdate := &m.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id} qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
err = GetTeamMembers(qBeforeUpdate) err = GetTeamMembers(qBeforeUpdate)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0) So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0)
err = UpdateTeamMember(&m.UpdateTeamMemberCommand{ err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
UserId: userId, UserId: userId,
OrgId: testOrgId, OrgId: testOrgId,
TeamId: team.Id, TeamId: team.Id,
Permission: m.PERMISSION_ADMIN, Permission: models.PERMISSION_ADMIN,
}) })
So(err, ShouldBeNil) So(err, ShouldBeNil)
qAfterUpdate := &m.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id} qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
err = GetTeamMembers(qAfterUpdate) err = GetTeamMembers(qAfterUpdate)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(qAfterUpdate.Result[0].Permission, ShouldEqual, m.PERMISSION_ADMIN) So(qAfterUpdate.Result[0].Permission, ShouldEqual, models.PERMISSION_ADMIN)
}) })
Convey("Should default to member permission level when updating a user with invalid permission level", func() { Convey("Should default to member permission level when updating a user with invalid permission level", func() {
userID := userIds[0] userID := userIds[0]
team := group1.Result team := group1.Result
addMemberCmd := m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team.Id, UserId: userID} addMemberCmd := models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team.Id, UserId: userID}
err = AddTeamMember(&addMemberCmd) err = AddTeamMember(&addMemberCmd)
So(err, ShouldBeNil) So(err, ShouldBeNil)
qBeforeUpdate := &m.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id} qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
err = GetTeamMembers(qBeforeUpdate) err = GetTeamMembers(qBeforeUpdate)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0) So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0)
invalidPermissionLevel := m.PERMISSION_EDIT invalidPermissionLevel := models.PERMISSION_EDIT
err = UpdateTeamMember(&m.UpdateTeamMemberCommand{ err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
UserId: userID, UserId: userID,
OrgId: testOrgId, OrgId: testOrgId,
TeamId: team.Id, TeamId: team.Id,
...@@ -150,31 +150,31 @@ func TestTeamCommandsAndQueries(t *testing.T) { ...@@ -150,31 +150,31 @@ func TestTeamCommandsAndQueries(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
qAfterUpdate := &m.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id} qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
err = GetTeamMembers(qAfterUpdate) err = GetTeamMembers(qAfterUpdate)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(qAfterUpdate.Result[0].Permission, ShouldEqual, 0) So(qAfterUpdate.Result[0].Permission, ShouldEqual, 0)
}) })
Convey("Shouldn't be able to update a user not in the team.", func() { Convey("Shouldn't be able to update a user not in the team.", func() {
err = UpdateTeamMember(&m.UpdateTeamMemberCommand{ err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
UserId: 1, UserId: 1,
OrgId: testOrgId, OrgId: testOrgId,
TeamId: group1.Result.Id, TeamId: group1.Result.Id,
Permission: m.PERMISSION_ADMIN, Permission: models.PERMISSION_ADMIN,
}) })
So(err, ShouldEqual, m.ErrTeamMemberNotFound) So(err, ShouldEqual, models.ErrTeamMemberNotFound)
}) })
Convey("Should be able to search for teams", func() { Convey("Should be able to search for teams", func() {
query := &m.SearchTeamsQuery{OrgId: testOrgId, Query: "group", Page: 1} query := &models.SearchTeamsQuery{OrgId: testOrgId, Query: "group", Page: 1}
err = SearchTeams(query) err = SearchTeams(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result.Teams), ShouldEqual, 2) So(len(query.Result.Teams), ShouldEqual, 2)
So(query.Result.TotalCount, ShouldEqual, 2) So(query.Result.TotalCount, ShouldEqual, 2)
query2 := &m.SearchTeamsQuery{OrgId: testOrgId, Query: ""} query2 := &models.SearchTeamsQuery{OrgId: testOrgId, Query: ""}
err = SearchTeams(query2) err = SearchTeams(query2)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query2.Result.Teams), ShouldEqual, 2) So(len(query2.Result.Teams), ShouldEqual, 2)
...@@ -182,10 +182,10 @@ func TestTeamCommandsAndQueries(t *testing.T) { ...@@ -182,10 +182,10 @@ func TestTeamCommandsAndQueries(t *testing.T) {
Convey("Should be able to return all teams a user is member of", func() { Convey("Should be able to return all teams a user is member of", func() {
groupId := group2.Result.Id groupId := group2.Result.Id
err := AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[0]}) err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[0]})
So(err, ShouldBeNil) So(err, ShouldBeNil)
query := &m.GetTeamsByUserQuery{OrgId: testOrgId, UserId: userIds[0]} query := &models.GetTeamsByUserQuery{OrgId: testOrgId, UserId: userIds[0]}
err = GetTeamsByUser(query) err = GetTeamsByUser(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 1) So(len(query.Result), ShouldEqual, 1)
...@@ -194,66 +194,84 @@ func TestTeamCommandsAndQueries(t *testing.T) { ...@@ -194,66 +194,84 @@ func TestTeamCommandsAndQueries(t *testing.T) {
}) })
Convey("Should be able to remove users from a group", func() { Convey("Should be able to remove users from a group", func() {
err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0]}) err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0]})
So(err, ShouldBeNil) So(err, ShouldBeNil)
err = RemoveTeamMember(&m.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0]}) err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0]})
So(err, ShouldBeNil) So(err, ShouldBeNil)
q2 := &m.GetTeamMembersQuery{OrgId: testOrgId, TeamId: group1.Result.Id} q2 := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: group1.Result.Id}
err = GetTeamMembers(q2) err = GetTeamMembers(q2)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(q2.Result), ShouldEqual, 0) So(len(q2.Result), ShouldEqual, 0)
}) })
Convey("When ProtectLastAdmin is set to true", func() { Convey("When ProtectLastAdmin is set to true", func() {
err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: m.PERMISSION_ADMIN}) err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: models.PERMISSION_ADMIN})
So(err, ShouldBeNil) So(err, ShouldBeNil)
Convey("A user should not be able to remove the last admin", func() { Convey("A user should not be able to remove the last admin", func() {
err = RemoveTeamMember(&m.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true}) err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true})
So(err, ShouldEqual, m.ErrLastTeamAdmin) So(err, ShouldEqual, models.ErrLastTeamAdmin)
}) })
Convey("A user should be able to remove an admin if there are other admins", func() { Convey("A user should be able to remove an admin if there are other admins", func() {
AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: m.PERMISSION_ADMIN}) AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: models.PERMISSION_ADMIN})
err = RemoveTeamMember(&m.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true}) err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true})
So(err, ShouldEqual, nil) So(err, ShouldEqual, nil)
}) })
Convey("A user should not be able to remove the admin permission for the last admin", func() { Convey("A user should not be able to remove the admin permission for the last admin", func() {
err = UpdateTeamMember(&m.UpdateTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true}) err = UpdateTeamMember(&models.UpdateTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true})
So(err, ShouldEqual, m.ErrLastTeamAdmin) So(err, ShouldEqual, models.ErrLastTeamAdmin)
}) })
Convey("A user should be able to remove the admin permission if there are other admins", func() { Convey("A user should be able to remove the admin permission if there are other admins", func() {
AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: m.PERMISSION_ADMIN}) AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: models.PERMISSION_ADMIN})
err = UpdateTeamMember(&m.UpdateTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true}) err = UpdateTeamMember(&models.UpdateTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true})
So(err, ShouldEqual, nil) So(err, ShouldEqual, nil)
}) })
}) })
Convey("Should be able to remove a group with users and permissions", func() { Convey("Should be able to remove a group with users and permissions", func() {
groupId := group2.Result.Id groupId := group2.Result.Id
err := AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[1]}) err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[1]})
So(err, ShouldBeNil) So(err, ShouldBeNil)
err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[2]}) err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[2]})
So(err, ShouldBeNil) So(err, ShouldBeNil)
err = testHelperUpdateDashboardAcl(1, m.DashboardAcl{DashboardId: 1, OrgId: testOrgId, Permission: m.PERMISSION_EDIT, TeamId: groupId}) err = testHelperUpdateDashboardAcl(1, models.DashboardAcl{DashboardId: 1, OrgId: testOrgId, Permission: models.PERMISSION_EDIT, TeamId: groupId})
So(err, ShouldBeNil) So(err, ShouldBeNil)
err = DeleteTeam(&m.DeleteTeamCommand{OrgId: testOrgId, Id: groupId}) err = DeleteTeam(&models.DeleteTeamCommand{OrgId: testOrgId, Id: groupId})
So(err, ShouldBeNil) So(err, ShouldBeNil)
query := &m.GetTeamByIdQuery{OrgId: testOrgId, Id: groupId} query := &models.GetTeamByIdQuery{OrgId: testOrgId, Id: groupId}
err = GetTeamById(query) err = GetTeamById(query)
So(err, ShouldEqual, m.ErrTeamNotFound) So(err, ShouldEqual, models.ErrTeamNotFound)
permQuery := &m.GetDashboardAclInfoListQuery{DashboardId: 1, OrgId: testOrgId} permQuery := &models.GetDashboardAclInfoListQuery{DashboardId: 1, OrgId: testOrgId}
err = GetDashboardAclInfoList(permQuery) err = GetDashboardAclInfoList(permQuery)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(permQuery.Result), ShouldEqual, 0) So(len(permQuery.Result), ShouldEqual, 0)
}) })
Convey("Should be able to return if user is admin of teams or not", func() {
groupId := group2.Result.Id
err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[0]})
So(err, ShouldBeNil)
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[1], Permission: models.PERMISSION_ADMIN})
So(err, ShouldBeNil)
query := &models.IsAdminOfTeamsQuery{SignedInUser: &models.SignedInUser{OrgId: testOrgId, UserId: userIds[0]}}
err = IsAdminOfTeams(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeFalse)
query = &models.IsAdminOfTeamsQuery{SignedInUser: &models.SignedInUser{OrgId: testOrgId, UserId: userIds[1]}}
err = IsAdminOfTeams(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeTrue)
})
}) })
}) })
} }
...@@ -44,12 +44,12 @@ export class UserPicker extends Component<Props, State> { ...@@ -44,12 +44,12 @@ export class UserPicker extends Component<Props, State> {
} }
return backendSrv return backendSrv
.get(`/api/org/users?query=${query}&limit=10`) .get(`/api/org/users/lookup?query=${query}&limit=10`)
.then((result: any) => { .then((result: any) => {
return result.map((user: any) => ({ return result.map((user: any) => ({
id: user.userId, id: user.userId,
value: user.userId, value: user.userId,
label: user.login === user.email ? user.login : `${user.login} - ${user.email}`, label: user.login,
imgUrl: user.avatarUrl, imgUrl: user.avatarUrl,
login: user.login, login: user.login,
})); }));
......
...@@ -71,7 +71,7 @@ export class AlertTabCtrl { ...@@ -71,7 +71,7 @@ export class AlertTabCtrl {
this.alertNotifications = []; this.alertNotifications = [];
this.alertHistory = []; this.alertHistory = [];
return this.backendSrv.get('/api/alert-notifications').then((res: any) => { return this.backendSrv.get('/api/alert-notifications/lookup').then((res: any) => {
this.notifications = res; this.notifications = res;
this.initModel(); this.initModel();
......
...@@ -79,7 +79,7 @@ export class GettingStarted extends PureComponent<PanelProps, State> { ...@@ -79,7 +79,7 @@ export class GettingStarted extends PureComponent<PanelProps, State> {
href: 'org/users?gettingstarted', href: 'org/users?gettingstarted',
check: () => { check: () => {
return getBackendSrv() return getBackendSrv()
.get('/api/org/users') .get('/api/org/users/lookup')
.then((res: any) => { .then((res: any) => {
return res.length > 1; return res.length > 1;
}); });
......
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