From 4e160c78abbc2a33ed9a329c0a62b1bfb7f411b6 Mon Sep 17 00:00:00 2001 From: George Goldberg Date: Fri, 13 Jan 2017 13:52:32 +0000 Subject: PLT-5050 (Server). API to update channel member roles. (#5043) Implements API reference proposal mattermost-api-reference:#66. --- api/channel.go | 47 ++++++++++++++++++++++++++++++++ api/channel_test.go | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++ model/authorization.go | 11 +++++++- model/client.go | 23 ++++++++++++++++ 4 files changed, 153 insertions(+), 1 deletion(-) diff --git a/api/channel.go b/api/channel.go index ae92ab618..cc63edd07 100644 --- a/api/channel.go +++ b/api/channel.go @@ -45,6 +45,7 @@ func InitChannel() { BaseRoutes.NeedChannel.Handle("/delete", ApiUserRequired(deleteChannel)).Methods("POST") BaseRoutes.NeedChannel.Handle("/add", ApiUserRequired(addMember)).Methods("POST") BaseRoutes.NeedChannel.Handle("/remove", ApiUserRequired(removeMember)).Methods("POST") + BaseRoutes.NeedChannel.Handle("/update_member_roles", ApiUserRequired(updateChannelMemberRoles)).Methods("POST") } func createChannel(c *Context, w http.ResponseWriter, r *http.Request) { @@ -1320,3 +1321,49 @@ func getChannelMembersByIds(c *Context, w http.ResponseWriter, r *http.Request) return } } + +func updateChannelMemberRoles(c *Context, w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + channelId := params["channel_id"] + + props := model.MapFromJson(r.Body) + + userId := props["user_id"] + if len(userId) != 26 { + c.SetInvalidParam("updateChannelMemberRoles", "user_id") + return + } + + mchan := Srv.Store.Channel().GetMember(channelId, userId) + + newRoles := props["new_roles"] + if !(model.IsValidUserRoles(newRoles)) { + c.SetInvalidParam("updateChannelMemberRoles", "new_roles") + return + } + + if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_MANAGE_CHANNEL_ROLES) { + return + } + + var member model.ChannelMember + if result := <-mchan; result.Err != nil { + c.Err = result.Err + return + } else { + member = result.Data.(model.ChannelMember) + } + + member.Roles = newRoles + + if result := <-Srv.Store.Channel().UpdateMember(&member); result.Err != nil { + c.Err = result.Err + return + } + + InvalidateCacheForUser(userId) + + rdata := map[string]string{} + rdata["status"] = "ok" + w.Write([]byte(model.MapToJson(rdata))) +} diff --git a/api/channel_test.go b/api/channel_test.go index 25fd885ca..8bfa0e896 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -1920,3 +1920,76 @@ func TestGetChannelMembersByIds(t *testing.T) { t.Fatal("should have errored - empty user ids") } } + +func TestUpdateChannelRoles(t *testing.T) { + th := Setup().InitSystemAdmin().InitBasic() + th.SystemAdminClient.SetTeamId(th.BasicTeam.Id) + LinkUserToTeam(th.SystemAdminUser, th.BasicTeam) + + const CHANNEL_ADMIN = "channel_admin channel_user" + const CHANNEL_MEMBER = "channel_user" + + // User 1 creates a channel, making them channel admin by default. + createChannel := model.Channel{ + DisplayName: "Test API Name", + Name: "a" + model.NewId() + "a", + Type: model.CHANNEL_OPEN, + TeamId: th.BasicTeam.Id, + } + + rchannel, err := th.BasicClient.CreateChannel(&createChannel) + if err != nil { + t.Fatal("Failed to create channel:", err) + } + channel := rchannel.Data.(*model.Channel) + + // User 1 adds User 2 to the channel, making them a channel member by default. + if _, err := th.BasicClient.AddChannelMember(channel.Id, th.BasicUser2.Id); err != nil { + t.Fatal("Failed to add user 2 to the channel:", err) + } + + // System Admin can demote User 1 (channel admin). + if data, meta := th.SystemAdminClient.UpdateChannelRoles(channel.Id, th.BasicUser.Id, CHANNEL_MEMBER); data == nil { + t.Fatal("System Admin failed to demote channel admin to channel member:", meta) + } + + // User 1 (channel_member) cannot promote user 2 (channel_member). + if data, meta := th.BasicClient.UpdateChannelRoles(channel.Id, th.BasicUser2.Id, CHANNEL_ADMIN); data != nil { + t.Fatal("Channel member should not be able to promote another channel member to channel admin:", meta) + } + + // System Admin can promote user 1 (channel member). + if data, meta := th.SystemAdminClient.UpdateChannelRoles(channel.Id, th.BasicUser.Id, CHANNEL_ADMIN); data == nil { + t.Fatal("System Admin failed to promote channel member to channel admin:", meta) + } + + // User 1 (channel_admin) can promote User 2 (channel member). + if data, meta := th.BasicClient.UpdateChannelRoles(channel.Id, th.BasicUser2.Id, CHANNEL_ADMIN); data == nil { + t.Fatal("Channel admin failed to promote channel member to channel admin:", meta) + } + + // User 1 (channel admin) can demote User 2 (channel admin). + if data, meta := th.BasicClient.UpdateChannelRoles(channel.Id, th.BasicUser2.Id, CHANNEL_MEMBER); data == nil { + t.Fatal("Channel admin failed to demote channel admin to channel member:", meta) + } + + // User 1 (channel admin) can demote itself. + if data, meta := th.BasicClient.UpdateChannelRoles(channel.Id, th.BasicUser.Id, CHANNEL_MEMBER); data == nil { + t.Fatal("Channel admin failed to demote itself to channel member:", meta) + } + + // Promote User2 again for next test. + if data, meta := th.SystemAdminClient.UpdateChannelRoles(channel.Id, th.BasicUser2.Id, CHANNEL_ADMIN); data == nil { + t.Fatal("System Admin failed to promote channel member to channel admin:", meta) + } + + // User 1 (channel member) cannot demote user 2 (channel admin). + if data, meta := th.BasicClient.UpdateChannelRoles(channel.Id, th.BasicUser2.Id, CHANNEL_MEMBER); data != nil { + t.Fatal("Channel member should not be able to demote another channel admin to channel member:", meta) + } + + // User 1 (channel member) cannot promote itself. + if data, meta := th.BasicClient.UpdateChannelRoles(channel.Id, th.BasicUser.Id, CHANNEL_ADMIN); data != nil { + t.Fatal("Channel member should not be able to promote itself to channel admin:", meta) + } +} diff --git a/model/authorization.go b/model/authorization.go index 75aebf55c..58fed5854 100644 --- a/model/authorization.go +++ b/model/authorization.go @@ -27,6 +27,7 @@ var PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS *Permission var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission var PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE *Permission var PERMISSION_MANAGE_ROLES *Permission +var PERMISSION_MANAGE_CHANNEL_ROLES *Permission var PERMISSION_CREATE_DIRECT_CHANNEL *Permission var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission @@ -123,6 +124,11 @@ func InitalizePermissions() { "authentication.permissions.manage_roles.name", "authentication.permissions.manage_roles.description", } + PERMISSION_MANAGE_CHANNEL_ROLES = &Permission{ + "manage_channel_roles", + "authentication.permissions.manage_channel_roles.name", + "authentication.permissions.manage_channel_roles.description", + } PERMISSION_MANAGE_SYSTEM = &Permission{ "manage_system", "authentication.permissions.manage_system.name", @@ -264,7 +270,9 @@ func InitalizeRoles() { "channel_admin", "authentication.roles.channel_admin.name", "authentication.roles.channel_admin.description", - []string{}, + []string{ + PERMISSION_MANAGE_CHANNEL_ROLES.Id, + }, } BuiltInRoles[ROLE_CHANNEL_ADMIN.Id] = ROLE_CHANNEL_ADMIN ROLE_CHANNEL_GUEST = &Role{ @@ -296,6 +304,7 @@ func InitalizeRoles() { PERMISSION_MANAGE_TEAM.Id, PERMISSION_IMPORT_TEAM.Id, PERMISSION_MANAGE_ROLES.Id, + PERMISSION_MANAGE_CHANNEL_ROLES.Id, PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id, PERMISSION_MANAGE_SLASH_COMMANDS.Id, PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id, diff --git a/model/client.go b/model/client.go index b5c22c6ca..c75121e97 100644 --- a/model/client.go +++ b/model/client.go @@ -2334,3 +2334,26 @@ func (c *Client) ListReactions(channelId string, postId string) ([]*Reaction, *A return ReactionsFromJson(r.Body), nil } } + +// Updates the user's roles in the channel by replacing them with the roles provided. +func (c *Client) UpdateChannelRoles(channelId string, userId string, roles string) (map[string]string, *ResponseMetadata) { + data := make(map[string]string) + data["new_roles"] = roles + data["user_id"] = userId + + if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/update_member_roles", MapToJson(data)); err != nil { + metadata := ResponseMetadata{Error: err} + if r != nil { + metadata.StatusCode = r.StatusCode + } + return nil, &metadata + } else { + defer closeBody(r) + return MapFromJson(r.Body), + &ResponseMetadata{ + StatusCode: r.StatusCode, + RequestId: r.Header.Get(HEADER_REQUEST_ID), + Etag: r.Header.Get(HEADER_ETAG_SERVER), + } + } +} -- cgit v1.2.3-1-g7c22