Преглед изворни кода

teams: editors can't remove the last admin from a team.

Leonard Gram пре 6 година
родитељ
комит
21d3d27452

+ 1 - 1
pkg/api/api.go

@@ -160,7 +160,7 @@ func (hs *HTTPServer) registerRoutes() {
 			teamsRoute.Get("/:teamId/members", Wrap(GetTeamMembers))
 			teamsRoute.Get("/:teamId/members", Wrap(GetTeamMembers))
 			teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), Wrap(AddTeamMember))
 			teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), Wrap(AddTeamMember))
 			teamsRoute.Put("/:teamId/members/:userId", bind(m.UpdateTeamMemberCommand{}), Wrap(UpdateTeamMember))
 			teamsRoute.Put("/:teamId/members/:userId", bind(m.UpdateTeamMemberCommand{}), Wrap(UpdateTeamMember))
-			teamsRoute.Delete("/:teamId/members/:userId", Wrap(RemoveTeamMember))
+			teamsRoute.Delete("/:teamId/members/:userId", Wrap(hs.RemoveTeamMember))
 			teamsRoute.Get("/:teamId/preferences", Wrap(GetTeamPreferences))
 			teamsRoute.Get("/:teamId/preferences", Wrap(GetTeamPreferences))
 			teamsRoute.Put("/:teamId/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateTeamPreferences))
 			teamsRoute.Put("/:teamId/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateTeamPreferences))
 		}, reqAdminOrEditorCanAdmin)
 		}, reqAdminOrEditorCanAdmin)

+ 7 - 2
pkg/api/team_members.go

@@ -81,7 +81,7 @@ func UpdateTeamMember(c *m.ReqContext, cmd m.UpdateTeamMemberCommand) Response {
 }
 }
 
 
 // DELETE /api/teams/:teamId/members/:userId
 // DELETE /api/teams/:teamId/members/:userId
-func RemoveTeamMember(c *m.ReqContext) Response {
+func (hs *HTTPServer) RemoveTeamMember(c *m.ReqContext) Response {
 	orgId := c.OrgId
 	orgId := c.OrgId
 	teamId := c.ParamsInt64(":teamId")
 	teamId := c.ParamsInt64(":teamId")
 	userId := c.ParamsInt64(":userId")
 	userId := c.ParamsInt64(":userId")
@@ -90,7 +90,12 @@ func RemoveTeamMember(c *m.ReqContext) Response {
 		return Error(403, "Not allowed to remove team member", err)
 		return Error(403, "Not allowed to remove team member", err)
 	}
 	}
 
 
-	if err := bus.Dispatch(&m.RemoveTeamMemberCommand{OrgId: orgId, TeamId: teamId, UserId: userId}); err != nil {
+	protectLastAdmin := false
+	if c.OrgRole == m.ROLE_EDITOR {
+		protectLastAdmin = true
+	}
+
+	if err := bus.Dispatch(&m.RemoveTeamMemberCommand{OrgId: orgId, TeamId: teamId, UserId: userId, ProtectLastAdmin: protectLastAdmin}); err != nil {
 		if err == m.ErrTeamNotFound {
 		if err == m.ErrTeamNotFound {
 			return Error(404, "Team not found", nil)
 			return Error(404, "Team not found", nil)
 		}
 		}

+ 1 - 0
pkg/models/team.go

@@ -10,6 +10,7 @@ var (
 	ErrTeamNotFound                         = errors.New("Team not found")
 	ErrTeamNotFound                         = errors.New("Team not found")
 	ErrTeamNameTaken                        = errors.New("Team name is taken")
 	ErrTeamNameTaken                        = errors.New("Team name is taken")
 	ErrTeamMemberNotFound                   = errors.New("Team member not found")
 	ErrTeamMemberNotFound                   = errors.New("Team member not found")
+	ErrLastTeamAdmin                        = errors.New("Not allowed to remove last admin")
 	ErrNotAllowedToUpdateTeam               = errors.New("User not allowed to update team")
 	ErrNotAllowedToUpdateTeam               = errors.New("User not allowed to update team")
 	ErrNotAllowedToUpdateTeamInDifferentOrg = errors.New("User not allowed to update team in another org")
 	ErrNotAllowedToUpdateTeamInDifferentOrg = errors.New("User not allowed to update team in another org")
 )
 )

+ 4 - 3
pkg/models/team_member.go

@@ -42,9 +42,10 @@ type UpdateTeamMemberCommand struct {
 }
 }
 
 
 type RemoveTeamMemberCommand struct {
 type RemoveTeamMemberCommand struct {
-	OrgId  int64 `json:"-"`
-	UserId int64
-	TeamId int64
+	OrgId            int64 `json:"-"`
+	UserId           int64
+	TeamId           int64
+	ProtectLastAdmin bool `json:"-"`
 }
 }
 
 
 // ----------------------
 // ----------------------

+ 35 - 0
pkg/services/sqlstore/team.go

@@ -285,6 +285,18 @@ func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error {
 			return err
 			return err
 		}
 		}
 
 
+		if cmd.ProtectLastAdmin {
+			lastAdmin, err := isLastAdmin(sess, cmd.OrgId, cmd.TeamId, cmd.UserId)
+			if err != nil {
+				return err
+			}
+
+			if lastAdmin {
+				return m.ErrLastTeamAdmin
+			}
+
+		}
+
 		var rawSql = "DELETE FROM team_member WHERE org_id=? and team_id=? and user_id=?"
 		var rawSql = "DELETE FROM team_member WHERE org_id=? and team_id=? and user_id=?"
 		res, err := sess.Exec(rawSql, cmd.OrgId, cmd.TeamId, cmd.UserId)
 		res, err := sess.Exec(rawSql, cmd.OrgId, cmd.TeamId, cmd.UserId)
 		if err != nil {
 		if err != nil {
@@ -299,6 +311,29 @@ 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)
+	if err != nil {
+		return false, err
+	}
+
+	isAdmin := false
+	for _, adminId := range userIds {
+		if userId == *adminId {
+			isAdmin = true
+			break
+		}
+	}
+
+	if isAdmin && len(userIds) == 1 {
+		return true, nil
+	}
+
+	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 *m.GetTeamMembersQuery) error {
 	query.Result = make([]*m.TeamMemberDTO, 0)
 	query.Result = make([]*m.TeamMemberDTO, 0)

+ 17 - 0
pkg/services/sqlstore/team_test.go

@@ -152,6 +152,23 @@ func TestTeamCommandsAndQueries(t *testing.T) {
 				So(len(q2.Result), ShouldEqual, 0)
 				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: int64(m.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)
+				})
+
+				Convey("A user should be able to remove an admin if there are other admins", func() {
+					err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: int64(m.PERMISSION_ADMIN)})
+					err = RemoveTeamMember(&m.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true})
+					So(err, ShouldEqual, nil)
+				})
+
+			})
+
 			Convey("Should be able to remove a group with users and permissions", func() {
 			Convey("Should be able to remove a group with users and permissions", func() {
 				groupId := group2.Result.Id
 				groupId := group2.Result.Id
 				err := AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[1]})
 				err := AddTeamMember(&m.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[1]})