mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-22206] Add PATCH channel moderations (PUT /moderations/patch) (#13845)
* MM-22205 Add get channel moderations endpoint
* MM-22206 Add patch channel moderations endpoint
Add api tests for patch channel moderations
* MM-22205 Ensure ordered permissions returned and create struct ChannelModeratedRoles
* MM-22206 Use structs instead of map
* MM-22206 Add test cases for GetChannelModeratedPermissions
* MM-22206 Add tests for ChannelModeratedPermissionsChangedByPatch
* MM-22206 Use NewBool instead of defining booleans
* MM-22206 Tie Channel Mentions to Create Posts when building Channel Moderations
* Revert "MM-22206 Tie Channel Mentions to Create Posts when building Channel Moderations"
This reverts commit a0bfc95f17.
* MM-22206 Review changes
Modify GetSchemeRolesForChannel to return named variables
Change calls to SessionHasPermissionToChannel to SessionHasPermissionTo
Add a CreateChannelScheme method
Add a DeleteChannelScheme method
Move GetChannelModeratedPermissions to Role model
* Fix lint
* Add ChannelModeration methods to App interface
* MM-22206 Rename method to GetTeamSchemeChannelRoles
* MM-22206 Check CHANNEL_MODERATED_PERMISSIONS_MAP for existing permission before iterating through it
* Modify wording to higherScoped to match #13813
* MM-22206 Delete channel scheme between tests
* MM-22206 Fix tests
* Actually patch role
* MM-22206 Shadow declaration of err
This commit is contained in:
@@ -45,6 +45,7 @@ type Routes struct {
|
||||
ChannelMembers *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/members'
|
||||
ChannelMember *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/members/{user_id:[A-Za-z0-9]+}'
|
||||
ChannelMembersForUser *mux.Router // 'api/v4/users/{user_id:[A-Za-z0-9]+}/teams/{team_id:[A-Za-z0-9]+}/channels/members'
|
||||
ChannelModerations *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/moderations'
|
||||
|
||||
Posts *mux.Router // 'api/v4/posts'
|
||||
Post *mux.Router // 'api/v4/posts/{post_id:[A-Za-z0-9]+}'
|
||||
@@ -156,6 +157,7 @@ func Init(configservice configservice.ConfigService, globalOptionsFunc app.AppOp
|
||||
api.BaseRoutes.ChannelMembers = api.BaseRoutes.Channel.PathPrefix("/members").Subrouter()
|
||||
api.BaseRoutes.ChannelMember = api.BaseRoutes.ChannelMembers.PathPrefix("/{user_id:[A-Za-z0-9]+}").Subrouter()
|
||||
api.BaseRoutes.ChannelMembersForUser = api.BaseRoutes.User.PathPrefix("/teams/{team_id:[A-Za-z0-9]+}/channels/members").Subrouter()
|
||||
api.BaseRoutes.ChannelModerations = api.BaseRoutes.Channel.PathPrefix("/moderations").Subrouter()
|
||||
|
||||
api.BaseRoutes.Posts = api.BaseRoutes.ApiRoot.PathPrefix("/posts").Subrouter()
|
||||
api.BaseRoutes.Post = api.BaseRoutes.Posts.PathPrefix("/{post_id:[A-Za-z0-9]+}").Subrouter()
|
||||
|
||||
@@ -1009,3 +1009,25 @@ func (me *TestHelper) AddPermissionToRole(permission string, roleName string) {
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) SetupTeamScheme() *model.Scheme {
|
||||
return me.SetupScheme(model.SCHEME_SCOPE_TEAM)
|
||||
}
|
||||
|
||||
func (me *TestHelper) SetupChannelScheme() *model.Scheme {
|
||||
return me.SetupScheme(model.SCHEME_SCOPE_CHANNEL)
|
||||
}
|
||||
|
||||
func (me *TestHelper) SetupScheme(scope string) *model.Scheme {
|
||||
scheme := model.Scheme{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Scope: scope,
|
||||
}
|
||||
|
||||
if scheme, err := me.App.CreateScheme(&scheme); err == nil {
|
||||
return scheme
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ func (api *API) InitChannel() {
|
||||
api.BaseRoutes.Channel.Handle("/pinned", api.ApiSessionRequired(getPinnedPosts)).Methods("GET")
|
||||
api.BaseRoutes.Channel.Handle("/timezones", api.ApiSessionRequired(getChannelMembersTimezones)).Methods("GET")
|
||||
api.BaseRoutes.Channel.Handle("/members_minus_group_members", api.ApiSessionRequired(channelMembersMinusGroupMembers)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.ChannelForUser.Handle("/unread", api.ApiSessionRequired(getChannelUnread)).Methods("GET")
|
||||
|
||||
api.BaseRoutes.ChannelByName.Handle("", api.ApiSessionRequired(getChannelByName)).Methods("GET")
|
||||
@@ -57,6 +58,9 @@ func (api *API) InitChannel() {
|
||||
api.BaseRoutes.ChannelMember.Handle("/roles", api.ApiSessionRequired(updateChannelMemberRoles)).Methods("PUT")
|
||||
api.BaseRoutes.ChannelMember.Handle("/schemeRoles", api.ApiSessionRequired(updateChannelMemberSchemeRoles)).Methods("PUT")
|
||||
api.BaseRoutes.ChannelMember.Handle("/notify_props", api.ApiSessionRequired(updateChannelMemberNotifyProps)).Methods("PUT")
|
||||
|
||||
api.BaseRoutes.ChannelModerations.Handle("", api.ApiSessionRequired(getChannelModerations)).Methods("GET")
|
||||
api.BaseRoutes.ChannelModerations.Handle("/patch", api.ApiSessionRequired(patchChannelModerations)).Methods("PUT")
|
||||
}
|
||||
|
||||
func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -1453,7 +1457,7 @@ func updateChannelScheme(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(*c.App.Session(), c.Params.ChannelId, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
@@ -1535,3 +1539,78 @@ func channelMembersMinusGroupMembers(c *Context, w http.ResponseWriter, r *http.
|
||||
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func getChannelModerations(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if c.App.License() == nil {
|
||||
c.Err = model.NewAppError("Api4.GetChannelModerations", "api.channel.get_channel_moderations.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
c.RequireChannelId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
channel, err := c.App.GetChannel(c.Params.ChannelId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
channelModerations, err := c.App.GetChannelModerationsForChannel(channel)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
b, marshalErr := json.Marshal(channelModerations)
|
||||
if marshalErr != nil {
|
||||
c.Err = model.NewAppError("Api4.getChannelModerations", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func patchChannelModerations(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if c.App.License() == nil {
|
||||
c.Err = model.NewAppError("Api4.patchChannelModerations", "api.channel.patch_channel_moderations.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
c.RequireChannelId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
channel, err := c.App.GetChannel(c.Params.ChannelId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
channelModerationsPatch := model.ChannelModerationsPatchFromJson(r.Body)
|
||||
channelModerations, err := c.App.PatchChannelModerationsForChannel(channel, channelModerationsPatch)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
b, marshalErr := json.Marshal(channelModerations)
|
||||
if marshalErr != nil {
|
||||
c.Err = model.NewAppError("Api4.patchChannelModerations", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
@@ -3001,3 +3001,227 @@ func TestChannelMembersMinusGroupMembers(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChannelModerations(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
channel := th.BasicChannel
|
||||
team := th.BasicTeam
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
t.Run("Errors without a license", func(t *testing.T) {
|
||||
_, res := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
|
||||
require.Equal(t, "api.channel.get_channel_moderations.license.error", res.Error.Id)
|
||||
})
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense())
|
||||
|
||||
t.Run("Errors as a non sysadmin", func(t *testing.T) {
|
||||
_, res := th.Client.GetChannelModerations(channel.Id, "")
|
||||
require.Equal(t, "api.context.permissions.app_error", res.Error.Id)
|
||||
})
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense())
|
||||
|
||||
t.Run("Returns default moderations with default roles", func(t *testing.T) {
|
||||
moderations, res := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
|
||||
require.Nil(t, res.Error)
|
||||
require.Equal(t, len(moderations), 4)
|
||||
for _, moderation := range moderations {
|
||||
if moderation.Name == "manage_members" {
|
||||
require.Empty(t, moderation.Roles.Guests)
|
||||
} else {
|
||||
require.Equal(t, moderation.Roles.Guests.Value, true)
|
||||
require.Equal(t, moderation.Roles.Guests.Enabled, true)
|
||||
}
|
||||
|
||||
require.Equal(t, moderation.Roles.Members.Value, true)
|
||||
require.Equal(t, moderation.Roles.Members.Enabled, true)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Returns value false and enabled false for permissions that are not present in higher scoped scheme when no channel scheme present", func(t *testing.T) {
|
||||
scheme := th.SetupTeamScheme()
|
||||
team.SchemeId = &scheme.Id
|
||||
_, err := th.App.UpdateTeamScheme(team)
|
||||
require.Nil(t, err)
|
||||
|
||||
th.RemovePermissionFromRole(model.PERMISSION_CREATE_POST.Id, scheme.DefaultChannelGuestRole)
|
||||
defer th.AddPermissionToRole(model.PERMISSION_CREATE_POST.Id, scheme.DefaultChannelGuestRole)
|
||||
|
||||
moderations, res := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
|
||||
require.Nil(t, res.Error)
|
||||
for _, moderation := range moderations {
|
||||
if moderation.Name == model.PERMISSION_CREATE_POST.Id {
|
||||
require.Equal(t, moderation.Roles.Members.Value, true)
|
||||
require.Equal(t, moderation.Roles.Members.Enabled, true)
|
||||
require.Equal(t, moderation.Roles.Guests.Value, false)
|
||||
require.Equal(t, moderation.Roles.Guests.Enabled, false)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Returns value false and enabled true for permissions that are not present in channel scheme but present in team scheme", func(t *testing.T) {
|
||||
scheme := th.SetupChannelScheme()
|
||||
channel.SchemeId = &scheme.Id
|
||||
_, err := th.App.UpdateChannelScheme(channel)
|
||||
require.Nil(t, err)
|
||||
|
||||
th.RemovePermissionFromRole(model.PERMISSION_CREATE_POST.Id, scheme.DefaultChannelGuestRole)
|
||||
defer th.AddPermissionToRole(model.PERMISSION_CREATE_POST.Id, scheme.DefaultChannelGuestRole)
|
||||
|
||||
moderations, res := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
|
||||
require.Nil(t, res.Error)
|
||||
for _, moderation := range moderations {
|
||||
if moderation.Name == model.PERMISSION_CREATE_POST.Id {
|
||||
require.Equal(t, moderation.Roles.Members.Value, true)
|
||||
require.Equal(t, moderation.Roles.Members.Enabled, true)
|
||||
require.Equal(t, moderation.Roles.Guests.Value, false)
|
||||
require.Equal(t, moderation.Roles.Guests.Enabled, true)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Returns value false and enabled false for permissions that are not present in channel & team scheme", func(t *testing.T) {
|
||||
teamScheme := th.SetupTeamScheme()
|
||||
team.SchemeId = &teamScheme.Id
|
||||
th.App.UpdateTeamScheme(team)
|
||||
|
||||
scheme := th.SetupChannelScheme()
|
||||
channel.SchemeId = &scheme.Id
|
||||
th.App.UpdateChannelScheme(channel)
|
||||
|
||||
th.RemovePermissionFromRole(model.PERMISSION_CREATE_POST.Id, scheme.DefaultChannelGuestRole)
|
||||
th.RemovePermissionFromRole(model.PERMISSION_CREATE_POST.Id, teamScheme.DefaultChannelGuestRole)
|
||||
|
||||
defer th.AddPermissionToRole(model.PERMISSION_CREATE_POST.Id, scheme.DefaultChannelGuestRole)
|
||||
defer th.AddPermissionToRole(model.PERMISSION_CREATE_POST.Id, teamScheme.DefaultChannelGuestRole)
|
||||
|
||||
moderations, res := th.SystemAdminClient.GetChannelModerations(channel.Id, "")
|
||||
require.Nil(t, res.Error)
|
||||
for _, moderation := range moderations {
|
||||
if moderation.Name == model.PERMISSION_CREATE_POST.Id {
|
||||
require.Equal(t, moderation.Roles.Members.Value, true)
|
||||
require.Equal(t, moderation.Roles.Members.Enabled, true)
|
||||
require.Equal(t, moderation.Roles.Guests.Value, false)
|
||||
require.Equal(t, moderation.Roles.Guests.Enabled, false)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPatchChannelModerations(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
channel := th.BasicChannel
|
||||
|
||||
emptyPatch := []*model.ChannelModerationPatch{}
|
||||
|
||||
createPosts := model.CHANNEL_MODERATED_PERMISSIONS[0]
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
t.Run("Errors without a license", func(t *testing.T) {
|
||||
_, res := th.SystemAdminClient.PatchChannelModerations(channel.Id, emptyPatch)
|
||||
require.Equal(t, "api.channel.patch_channel_moderations.license.error", res.Error.Id)
|
||||
})
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense())
|
||||
|
||||
t.Run("Errors as a non sysadmin", func(t *testing.T) {
|
||||
_, res := th.Client.PatchChannelModerations(channel.Id, emptyPatch)
|
||||
require.Equal(t, "api.context.permissions.app_error", res.Error.Id)
|
||||
})
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense())
|
||||
|
||||
t.Run("Returns default moderations with empty patch", func(t *testing.T) {
|
||||
moderations, res := th.SystemAdminClient.PatchChannelModerations(channel.Id, emptyPatch)
|
||||
require.Nil(t, res.Error)
|
||||
require.Equal(t, len(moderations), 4)
|
||||
for _, moderation := range moderations {
|
||||
if moderation.Name == "manage_members" {
|
||||
require.Empty(t, moderation.Roles.Guests)
|
||||
} else {
|
||||
require.Equal(t, moderation.Roles.Guests.Value, true)
|
||||
require.Equal(t, moderation.Roles.Guests.Enabled, true)
|
||||
}
|
||||
|
||||
require.Equal(t, moderation.Roles.Members.Value, true)
|
||||
require.Equal(t, moderation.Roles.Members.Enabled, true)
|
||||
}
|
||||
|
||||
require.Nil(t, channel.SchemeId)
|
||||
})
|
||||
|
||||
t.Run("Creates a scheme and returns the updated channel moderations when patching an existing permission", func(t *testing.T) {
|
||||
patch := []*model.ChannelModerationPatch{
|
||||
{
|
||||
Name: &createPosts,
|
||||
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(false)},
|
||||
},
|
||||
}
|
||||
|
||||
moderations, res := th.SystemAdminClient.PatchChannelModerations(channel.Id, patch)
|
||||
require.Nil(t, res.Error)
|
||||
require.Equal(t, len(moderations), 4)
|
||||
for _, moderation := range moderations {
|
||||
if moderation.Name == "manage_members" {
|
||||
require.Empty(t, moderation.Roles.Guests)
|
||||
} else {
|
||||
require.Equal(t, moderation.Roles.Guests.Value, true)
|
||||
require.Equal(t, moderation.Roles.Guests.Enabled, true)
|
||||
}
|
||||
|
||||
if moderation.Name == createPosts {
|
||||
require.Equal(t, moderation.Roles.Members.Value, false)
|
||||
require.Equal(t, moderation.Roles.Members.Enabled, true)
|
||||
} else {
|
||||
require.Equal(t, moderation.Roles.Members.Value, true)
|
||||
require.Equal(t, moderation.Roles.Members.Enabled, true)
|
||||
}
|
||||
}
|
||||
channel, _ = th.App.GetChannel(channel.Id)
|
||||
require.NotNil(t, channel.SchemeId)
|
||||
})
|
||||
|
||||
t.Run("Removes the existing scheme when moderated permissions are set back to higher scoped values", func(t *testing.T) {
|
||||
channel, _ = th.App.GetChannel(channel.Id)
|
||||
schemeId := channel.SchemeId
|
||||
|
||||
scheme, _ := th.App.GetScheme(*schemeId)
|
||||
require.Equal(t, scheme.DeleteAt, int64(0))
|
||||
|
||||
patch := []*model.ChannelModerationPatch{
|
||||
{
|
||||
Name: &createPosts,
|
||||
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
|
||||
},
|
||||
}
|
||||
|
||||
moderations, res := th.SystemAdminClient.PatchChannelModerations(channel.Id, patch)
|
||||
require.Nil(t, res.Error)
|
||||
require.Equal(t, len(moderations), 4)
|
||||
for _, moderation := range moderations {
|
||||
if moderation.Name == "manage_members" {
|
||||
require.Empty(t, moderation.Roles.Guests)
|
||||
} else {
|
||||
require.Equal(t, moderation.Roles.Guests.Value, true)
|
||||
require.Equal(t, moderation.Roles.Guests.Enabled, true)
|
||||
}
|
||||
|
||||
require.Equal(t, moderation.Roles.Members.Value, true)
|
||||
require.Equal(t, moderation.Roles.Members.Enabled, true)
|
||||
}
|
||||
|
||||
channel, _ = th.App.GetChannel(channel.Id)
|
||||
require.Nil(t, channel.SchemeId)
|
||||
|
||||
scheme, _ = th.App.GetScheme(*schemeId)
|
||||
require.NotEqual(t, scheme.DeleteAt, int64(0))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user