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"
} }
] ]
``` ```
......
...@@ -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
}
...@@ -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
}
...@@ -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