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