소스 검색

Organization: You can now update the organization user role directly (without removing and readding the organization user). Closes #1899

Torkel Ödegaard 10 년 전
부모
커밋
e771d8e944

+ 3 - 0
CHANGELOG.md

@@ -4,6 +4,9 @@
 - [Issue #1144](https://github.com/grafana/grafana/issues/1144). Templating: You can now select multiple template variables values at the same time.
 - [Issue #1888](https://github.com/grafana/grafana/issues/1144). Templating: Repeat panel or row for each selected template variable value
 
+**User or Organization admin**
+- [Issue #1899](https://github.com/grafana/grafana/issues/1899). Organization: You can now update the organization user role directly (without removing and readding the organization user).
+
 **Backend**
 - [Issue #1905](https://github.com/grafana/grafana/issues/1905). Github OAuth: You can now configure a Github team membership requirement, thx @dewski
 - [Issue #1891](https://github.com/grafana/grafana/issues/1891). Security: New config option to disable the use of gravatar for profile images

+ 1 - 0
pkg/api/api.go

@@ -71,6 +71,7 @@ func Register(r *macaron.Macaron) {
 			r.Put("/", bind(m.UpdateOrgCommand{}), UpdateOrg)
 			r.Post("/users", bind(m.AddOrgUserCommand{}), AddOrgUser)
 			r.Get("/users", GetOrgUsers)
+			r.Patch("/users/:id", bind(m.UpdateOrgUserCommand{}), UpdateOrgUser)
 			r.Delete("/users/:id", RemoveOrgUser)
 		}, reqAccountAdmin)
 

+ 17 - 0
pkg/api/org_users.go

@@ -48,6 +48,23 @@ func GetOrgUsers(c *middleware.Context) {
 	c.JSON(200, query.Result)
 }
 
+func UpdateOrgUser(c *middleware.Context, cmd m.UpdateOrgUserCommand) {
+	if !cmd.Role.IsValid() {
+		c.JsonApiErr(400, "Invalid role specified", nil)
+		return
+	}
+
+	cmd.UserId = c.ParamsInt64(":id")
+	cmd.OrgId = c.OrgId
+
+	if err := bus.Dispatch(&cmd); err != nil {
+		c.JsonApiErr(500, "Failed update org user", err)
+		return
+	}
+
+	c.JsonOK("Organization user updated")
+}
+
 func RemoveOrgUser(c *middleware.Context) {
 	userId := c.ParamsInt64(":id")
 

+ 9 - 0
pkg/models/org_user.go

@@ -9,6 +9,7 @@ import (
 var (
 	ErrInvalidRoleType = errors.New("Invalid role type")
 	ErrLastOrgAdmin    = errors.New("Cannot remove last organization admin")
+	ErrOrgUserNotFound = errors.New("Cannot find the organization user")
 )
 
 type RoleType string
@@ -24,6 +25,7 @@ func (r RoleType) IsValid() bool {
 }
 
 type OrgUser struct {
+	Id      int64
 	OrgId   int64
 	UserId  int64
 	Role    RoleType
@@ -47,6 +49,13 @@ type AddOrgUserCommand struct {
 	UserId int64 `json:"-"`
 }
 
+type UpdateOrgUserCommand struct {
+	Role RoleType `json:"role" binding:"Required"`
+
+	OrgId  int64 `json:"-"`
+	UserId int64 `json:"-"`
+}
+
 // ----------------------
 // QUERIES
 

+ 13 - 0
pkg/services/sqlstore/org_test.go

@@ -80,6 +80,19 @@ func TestAccountDataAccess(t *testing.T) {
 					So(err, ShouldBeNil)
 				})
 
+				Convey("Can update org user role", func() {
+					updateCmd := m.UpdateOrgUserCommand{OrgId: ac1.OrgId, UserId: ac2.Id, Role: m.ROLE_ADMIN}
+					err = UpdateOrgUser(&updateCmd)
+					So(err, ShouldBeNil)
+
+					orgUsersQuery := m.GetOrgUsersQuery{OrgId: ac1.OrgId}
+					err = GetOrgUsers(&orgUsersQuery)
+					So(err, ShouldBeNil)
+
+					So(orgUsersQuery.Result[1].Role, ShouldEqual, m.ROLE_ADMIN)
+
+				})
+
 				Convey("Can get logged in user projection", func() {
 					query := m.GetSignedInUserQuery{UserId: ac2.Id}
 					err := GetSignedInUser(&query)

+ 20 - 0
pkg/services/sqlstore/org_users.go

@@ -14,6 +14,7 @@ func init() {
 	bus.AddHandler("sql", AddOrgUser)
 	bus.AddHandler("sql", RemoveOrgUser)
 	bus.AddHandler("sql", GetOrgUsers)
+	bus.AddHandler("sql", UpdateOrgUser)
 }
 
 func AddOrgUser(cmd *m.AddOrgUserCommand) error {
@@ -32,6 +33,25 @@ func AddOrgUser(cmd *m.AddOrgUserCommand) error {
 	})
 }
 
+func UpdateOrgUser(cmd *m.UpdateOrgUserCommand) error {
+	return inTransaction(func(sess *xorm.Session) error {
+		var orgUser m.OrgUser
+		exists, err := sess.Where("org_id=? AND user_id=?", cmd.OrgId, cmd.UserId).Get(&orgUser)
+		if err != nil {
+			return err
+		}
+
+		if !exists {
+			return m.ErrOrgUserNotFound
+		}
+
+		orgUser.Role = cmd.Role
+		orgUser.Updated = time.Now()
+		_, err = sess.Id(orgUser.Id).Update(&orgUser)
+		return err
+	})
+}
+
 func GetOrgUsers(query *m.GetOrgUsersQuery) error {
 	query.Result = make([]*m.OrgUserDTO, 0)
 	sess := x.Table("org_user")

+ 4 - 0
public/app/features/org/orgUsersCtrl.js

@@ -23,6 +23,10 @@ function (angular) {
       });
     };
 
+    $scope.updateOrgUser = function(user) {
+      backendSrv.patch('/api/org/users/' + user.userId, user);
+    };
+
     $scope.removeUser = function(user) {
       backendSrv.delete('/api/org/users/' + user.userId).then($scope.get);
     };

+ 3 - 2
public/app/features/org/partials/orgUsers.html

@@ -35,7 +35,7 @@
 
 		<br>
 
-		<table class="grafana-options-table">
+		<table class="grafana-options-table form-inline">
 			<tr>
 				<th>Login</th>
 				<th>Email</th>
@@ -46,7 +46,8 @@
 				<td>{{user.login}}</td>
 				<td>{{user.email}}</td>
 				<td>
-					{{user.role}}
+					<select type="text" ng-model="user.role" class="input-small" ng-options="f for f in ['Viewer', 'Editor', 'Admin']" ng-change="updateOrgUser(user)">
+					</select>
 				</td>
 				<td style="width: 1%">
 					<a ng-click="removeUser(user)" class="btn btn-danger btn-mini">

+ 4 - 0
public/app/services/backendSrv.js

@@ -23,6 +23,10 @@ function (angular, _, config) {
       return this.request({ method: 'POST', url: url, data: data });
     };
 
+    this.patch = function(url, data) {
+      return this.request({ method: 'PATCH', url: url, data: data });
+    };
+
     this.put = function(url, data) {
       return this.request({ method: 'PUT', url: url, data: data });
     };