[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 a0bfc95f1732955c5ef5fc3e4b05ea8ab954acea. * 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
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
bfde6d1f3e
Коммит
79c786bc0c
@@ -45,6 +45,7 @@ type Routes struct {
|
|||||||
ChannelMembers *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/members'
|
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]+}'
|
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'
|
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'
|
Posts *mux.Router // 'api/v4/posts'
|
||||||
Post *mux.Router // 'api/v4/posts/{post_id:[A-Za-z0-9]+}'
|
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.ChannelMembers = api.BaseRoutes.Channel.PathPrefix("/members").Subrouter()
|
||||||
api.BaseRoutes.ChannelMember = api.BaseRoutes.ChannelMembers.PathPrefix("/{user_id:[A-Za-z0-9]+}").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.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.Posts = api.BaseRoutes.ApiRoot.PathPrefix("/posts").Subrouter()
|
||||||
api.BaseRoutes.Post = api.BaseRoutes.Posts.PathPrefix("/{post_id:[A-Za-z0-9]+}").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()
|
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("/pinned", api.ApiSessionRequired(getPinnedPosts)).Methods("GET")
|
||||||
api.BaseRoutes.Channel.Handle("/timezones", api.ApiSessionRequired(getChannelMembersTimezones)).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.Channel.Handle("/members_minus_group_members", api.ApiSessionRequired(channelMembersMinusGroupMembers)).Methods("GET")
|
||||||
|
|
||||||
api.BaseRoutes.ChannelForUser.Handle("/unread", api.ApiSessionRequired(getChannelUnread)).Methods("GET")
|
api.BaseRoutes.ChannelForUser.Handle("/unread", api.ApiSessionRequired(getChannelUnread)).Methods("GET")
|
||||||
|
|
||||||
api.BaseRoutes.ChannelByName.Handle("", api.ApiSessionRequired(getChannelByName)).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("/roles", api.ApiSessionRequired(updateChannelMemberRoles)).Methods("PUT")
|
||||||
api.BaseRoutes.ChannelMember.Handle("/schemeRoles", api.ApiSessionRequired(updateChannelMemberSchemeRoles)).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.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) {
|
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
|
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)
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1535,3 +1539,78 @@ func channelMembersMinusGroupMembers(c *Context, w http.ResponseWriter, r *http.
|
|||||||
|
|
||||||
w.Write(b)
|
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))
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -461,6 +461,7 @@ type AppIface interface {
|
|||||||
GetChannelMembersForUserWithPagination(teamId, userId string, page, perPage int) ([]*model.ChannelMember, *model.AppError)
|
GetChannelMembersForUserWithPagination(teamId, userId string, page, perPage int) ([]*model.ChannelMember, *model.AppError)
|
||||||
GetChannelMembersPage(channelId string, page, perPage int) (*model.ChannelMembers, *model.AppError)
|
GetChannelMembersPage(channelId string, page, perPage int) (*model.ChannelMembers, *model.AppError)
|
||||||
GetChannelMembersTimezones(channelId string) ([]string, *model.AppError)
|
GetChannelMembersTimezones(channelId string) ([]string, *model.AppError)
|
||||||
|
GetChannelModerationsForChannel(channel *model.Channel) ([]*model.ChannelModeration, *model.AppError)
|
||||||
GetChannelPinnedPostCount(channelId string) (int64, *model.AppError)
|
GetChannelPinnedPostCount(channelId string) (int64, *model.AppError)
|
||||||
GetChannelUnread(channelId, userId string) (*model.ChannelUnread, *model.AppError)
|
GetChannelUnread(channelId, userId string) (*model.ChannelUnread, *model.AppError)
|
||||||
GetChannelsByNames(channelNames []string, teamId string) ([]*model.Channel, *model.AppError)
|
GetChannelsByNames(channelNames []string, teamId string) ([]*model.Channel, *model.AppError)
|
||||||
@@ -715,6 +716,7 @@ type AppIface interface {
|
|||||||
OpenInteractiveDialog(request model.OpenDialogRequest) *model.AppError
|
OpenInteractiveDialog(request model.OpenDialogRequest) *model.AppError
|
||||||
OriginChecker() func(*http.Request) bool
|
OriginChecker() func(*http.Request) bool
|
||||||
PatchChannel(channel *model.Channel, patch *model.ChannelPatch, userId string) (*model.Channel, *model.AppError)
|
PatchChannel(channel *model.Channel, patch *model.ChannelPatch, userId string) (*model.Channel, *model.AppError)
|
||||||
|
PatchChannelModerationsForChannel(channel *model.Channel, channelModerationsPatch []*model.ChannelModerationPatch) ([]*model.ChannelModeration, *model.AppError)
|
||||||
PatchPost(postId string, patch *model.PostPatch) (*model.Post, *model.AppError)
|
PatchPost(postId string, patch *model.PostPatch) (*model.Post, *model.AppError)
|
||||||
PatchRole(role *model.Role, patch *model.RolePatch) (*model.Role, *model.AppError)
|
PatchRole(role *model.Role, patch *model.RolePatch) (*model.Role, *model.AppError)
|
||||||
PatchScheme(scheme *model.Scheme, patch *model.SchemePatch) (*model.Scheme, *model.AppError)
|
PatchScheme(scheme *model.Scheme, patch *model.SchemePatch) (*model.Scheme, *model.AppError)
|
||||||
|
|||||||
206
app/channel.go
206
app/channel.go
@@ -547,6 +547,34 @@ func (a *App) UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppE
|
|||||||
return channel, nil
|
return channel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateChannelScheme creates a new Scheme of scope channel and assigns it to the channel.
|
||||||
|
func (a *App) CreateChannelScheme(channel *model.Channel) (*model.Scheme, *model.AppError) {
|
||||||
|
scheme, err := a.CreateScheme(&model.Scheme{
|
||||||
|
Name: model.NewId(),
|
||||||
|
DisplayName: model.NewId(),
|
||||||
|
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.SchemeId = &scheme.Id
|
||||||
|
if _, err := a.UpdateChannelScheme(channel); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return scheme, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteChannelScheme deletes a channels scheme and sets its SchemeId to nil.
|
||||||
|
func (a *App) DeleteChannelScheme(channel *model.Channel) (*model.Channel, *model.AppError) {
|
||||||
|
if _, err := a.DeleteScheme(*channel.SchemeId); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
channel.SchemeId = nil
|
||||||
|
return a.UpdateChannelScheme(channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateChannelScheme saves the new SchemeId of the channel passed.
|
||||||
func (a *App) UpdateChannelScheme(channel *model.Channel) (*model.Channel, *model.AppError) {
|
func (a *App) UpdateChannelScheme(channel *model.Channel) (*model.Channel, *model.AppError) {
|
||||||
var oldChannel *model.Channel
|
var oldChannel *model.Channel
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
@@ -555,13 +583,7 @@ func (a *App) UpdateChannelScheme(channel *model.Channel) (*model.Channel, *mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldChannel.SchemeId = channel.SchemeId
|
oldChannel.SchemeId = channel.SchemeId
|
||||||
|
return a.UpdateChannel(oldChannel)
|
||||||
newChannel, err := a.UpdateChannel(oldChannel)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return newChannel, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) UpdateChannelPrivacy(oldChannel *model.Channel, user *model.User) (*model.Channel, *model.AppError) {
|
func (a *App) UpdateChannelPrivacy(oldChannel *model.Channel, user *model.User) (*model.Channel, *model.AppError) {
|
||||||
@@ -685,35 +707,185 @@ func (a *App) PatchChannel(channel *model.Channel, patch *model.ChannelPatch, us
|
|||||||
return channel, nil
|
return channel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetSchemeRolesForChannel(channelId string) (string, string, string, *model.AppError) {
|
// GetSchemeRolesForChannel Checks if a channel or its team has an override scheme for channel roles and returns the scheme roles or default channel roles.
|
||||||
|
func (a *App) GetSchemeRolesForChannel(channelId string) (guestRoleName, userRoleName, adminRoleName string, err *model.AppError) {
|
||||||
channel, err := a.GetChannel(channelId)
|
channel, err := a.GetChannel(channelId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", "", err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if channel.SchemeId != nil && len(*channel.SchemeId) != 0 {
|
if channel.SchemeId != nil && len(*channel.SchemeId) != 0 {
|
||||||
var scheme *model.Scheme
|
var scheme *model.Scheme
|
||||||
scheme, err = a.GetScheme(*channel.SchemeId)
|
scheme, err = a.GetScheme(*channel.SchemeId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", "", err
|
return
|
||||||
}
|
}
|
||||||
return scheme.DefaultChannelGuestRole, scheme.DefaultChannelUserRole, scheme.DefaultChannelAdminRole, nil
|
|
||||||
|
guestRoleName = scheme.DefaultChannelGuestRole
|
||||||
|
userRoleName = scheme.DefaultChannelUserRole
|
||||||
|
adminRoleName = scheme.DefaultChannelAdminRole
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
team, err := a.GetTeam(channel.TeamId)
|
return a.GetTeamSchemeChannelRoles(channel.TeamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTeamSchemeChannelRoles Checks if a team has an override scheme and returns the scheme channel role names or default channel role names.
|
||||||
|
func (a *App) GetTeamSchemeChannelRoles(teamId string) (guestRoleName, userRoleName, adminRoleName string, err *model.AppError) {
|
||||||
|
team, err := a.GetTeam(teamId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", "", err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if team.SchemeId != nil && len(*team.SchemeId) != 0 {
|
if team.SchemeId != nil && len(*team.SchemeId) != 0 {
|
||||||
scheme, err := a.GetScheme(*team.SchemeId)
|
var scheme *model.Scheme
|
||||||
|
scheme, err = a.GetScheme(*team.SchemeId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", "", err
|
return
|
||||||
}
|
}
|
||||||
return scheme.DefaultChannelGuestRole, scheme.DefaultChannelUserRole, scheme.DefaultChannelAdminRole, nil
|
|
||||||
|
guestRoleName = scheme.DefaultChannelGuestRole
|
||||||
|
userRoleName = scheme.DefaultChannelUserRole
|
||||||
|
adminRoleName = scheme.DefaultChannelAdminRole
|
||||||
|
} else {
|
||||||
|
guestRoleName = model.CHANNEL_GUEST_ROLE_ID
|
||||||
|
userRoleName = model.CHANNEL_USER_ROLE_ID
|
||||||
|
adminRoleName = model.CHANNEL_ADMIN_ROLE_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
return model.CHANNEL_GUEST_ROLE_ID, model.CHANNEL_USER_ROLE_ID, model.CHANNEL_ADMIN_ROLE_ID, nil
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PatchChannelModerationsForChannel Gets a channels ChannelModerations from either the higherScoped roles or from the channel scheme roles.
|
||||||
|
func (a *App) GetChannelModerationsForChannel(channel *model.Channel) ([]*model.ChannelModeration, *model.AppError) {
|
||||||
|
guestRoleName, memberRoleName, _, _ := a.GetSchemeRolesForChannel(channel.Id)
|
||||||
|
memberRole, err := a.GetRoleByName(memberRoleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
guestRole, err := a.GetRoleByName(guestRoleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
higherScopedGuestRoleName, higherScopedMemberRoleName, _, _ := a.GetTeamSchemeChannelRoles(channel.TeamId)
|
||||||
|
higherScopedMemberRole, err := a.GetRoleByName(higherScopedMemberRoleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
higherScopedGuestRole, err := a.GetRoleByName(higherScopedGuestRoleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildChannelModerations(memberRole, guestRole, higherScopedMemberRole, higherScopedGuestRole), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PatchChannelModerationsForChannel Updates a channels scheme roles based on a given ChannelModerationPatch, if the permissions match the higher scoped role the scheme is deleted.
|
||||||
|
func (a *App) PatchChannelModerationsForChannel(channel *model.Channel, channelModerationsPatch []*model.ChannelModerationPatch) ([]*model.ChannelModeration, *model.AppError) {
|
||||||
|
higherScopedGuestRoleName, higherScopedMemberRoleName, _, _ := a.GetTeamSchemeChannelRoles(channel.TeamId)
|
||||||
|
higherScopedMemberRole, err := a.GetRoleByName(higherScopedMemberRoleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
higherScopedGuestRole, err := a.GetRoleByName(higherScopedGuestRoleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
higherScopedMemberPermissions := higherScopedMemberRole.GetChannelModeratedPermissions()
|
||||||
|
higherScopedGuestPermissions := higherScopedGuestRole.GetChannelModeratedPermissions()
|
||||||
|
|
||||||
|
for _, moderationPatch := range channelModerationsPatch {
|
||||||
|
if moderationPatch.Roles.Members != nil && *moderationPatch.Roles.Members && !higherScopedMemberPermissions[*moderationPatch.Name] {
|
||||||
|
return nil, &model.AppError{Message: "Cannot add a permission that is restricted by the team or system permission scheme"}
|
||||||
|
}
|
||||||
|
if moderationPatch.Roles.Guests != nil && *moderationPatch.Roles.Guests && !higherScopedGuestPermissions[*moderationPatch.Name] {
|
||||||
|
return nil, &model.AppError{Message: "Cannot add a permission that is restricted by the team or system permission scheme"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel has no scheme so create one
|
||||||
|
if channel.SchemeId == nil || len(*channel.SchemeId) == 0 {
|
||||||
|
if _, err = a.CreateChannelScheme(channel); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guestRoleName, memberRoleName, _, _ := a.GetSchemeRolesForChannel(channel.Id)
|
||||||
|
memberRole, err := a.GetRoleByName(memberRoleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
guestRole, err := a.GetRoleByName(guestRoleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
memberRolePatch := memberRole.RolePatchFromChannelModerationsPatch(channelModerationsPatch, "members")
|
||||||
|
guestRolePatch := guestRole.RolePatchFromChannelModerationsPatch(channelModerationsPatch, "guests")
|
||||||
|
|
||||||
|
memberRolePermissionsUnmodified := len(model.ChannelModeratedPermissionsChangedByPatch(higherScopedMemberRole, memberRolePatch)) == 0
|
||||||
|
guestRolePermissionsUnmodified := len(model.ChannelModeratedPermissionsChangedByPatch(higherScopedGuestRole, guestRolePatch)) == 0
|
||||||
|
if memberRolePermissionsUnmodified && guestRolePermissionsUnmodified {
|
||||||
|
// The channel scheme matches the permissions of its higherScoped scheme so delete the scheme
|
||||||
|
if _, err = a.DeleteChannelScheme(channel); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
memberRole = higherScopedMemberRole
|
||||||
|
guestRole = higherScopedGuestRole
|
||||||
|
} else {
|
||||||
|
memberRole, err = a.PatchRole(memberRole, memberRolePatch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
guestRole, err = a.PatchRole(guestRole, guestRolePatch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildChannelModerations(memberRole, guestRole, higherScopedMemberRole, higherScopedGuestRole), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildChannelModerations(memberRole *model.Role, guestRole *model.Role, higherScopedMemberRole *model.Role, higherScopedGuestRole *model.Role) []*model.ChannelModeration {
|
||||||
|
memberPermissions := memberRole.GetChannelModeratedPermissions()
|
||||||
|
guestPermissions := guestRole.GetChannelModeratedPermissions()
|
||||||
|
higherScopedMemberPermissions := higherScopedMemberRole.GetChannelModeratedPermissions()
|
||||||
|
higherScopedGuestPermissions := higherScopedGuestRole.GetChannelModeratedPermissions()
|
||||||
|
|
||||||
|
var channelModerations []*model.ChannelModeration
|
||||||
|
for _, permissionKey := range model.CHANNEL_MODERATED_PERMISSIONS {
|
||||||
|
roles := &model.ChannelModeratedRoles{}
|
||||||
|
|
||||||
|
roles.Members = &model.ChannelModeratedRole{
|
||||||
|
Value: memberPermissions[permissionKey],
|
||||||
|
Enabled: higherScopedMemberPermissions[permissionKey],
|
||||||
|
}
|
||||||
|
|
||||||
|
if permissionKey == "manage_members" {
|
||||||
|
roles.Guests = nil
|
||||||
|
} else {
|
||||||
|
roles.Guests = &model.ChannelModeratedRole{
|
||||||
|
Value: guestPermissions[permissionKey],
|
||||||
|
Enabled: higherScopedGuestPermissions[permissionKey],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
moderation := &model.ChannelModeration{
|
||||||
|
Name: permissionKey,
|
||||||
|
Roles: roles,
|
||||||
|
}
|
||||||
|
|
||||||
|
channelModerations = append(channelModerations, moderation)
|
||||||
|
}
|
||||||
|
|
||||||
|
return channelModerations
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) UpdateChannelMemberRoles(channelId string, userId string, newRoles string) (*model.ChannelMember, *model.AppError) {
|
func (a *App) UpdateChannelMemberRoles(channelId string, userId string, newRoles string) (*model.ChannelMember, *model.AppError) {
|
||||||
|
|||||||
@@ -1301,3 +1301,350 @@ func TestRemoveUserFromChannel(t *testing.T) {
|
|||||||
err = th.App.RemoveUserFromChannel(botUser.Id, th.SystemAdminUser.Id, privateChannel)
|
err = th.App.RemoveUserFromChannel(botUser.Id, th.SystemAdminUser.Id, privateChannel)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPatchChannelModerationsForChannel(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||||
|
channel := th.BasicChannel
|
||||||
|
|
||||||
|
createPosts := model.CHANNEL_MODERATED_PERMISSIONS[0]
|
||||||
|
createReactions := model.CHANNEL_MODERATED_PERMISSIONS[1]
|
||||||
|
manageMembers := model.CHANNEL_MODERATED_PERMISSIONS[2]
|
||||||
|
channelMentions := model.CHANNEL_MODERATED_PERMISSIONS[3]
|
||||||
|
|
||||||
|
nonChannelModeratedPermission := model.PERMISSION_CREATE_BOT.Id
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
Name string
|
||||||
|
ChannelModerationsPatch []*model.ChannelModerationPatch
|
||||||
|
PermissionsModeratedByPatch map[string]*model.ChannelModeratedRoles
|
||||||
|
RevertChannelModerationsPatch []*model.ChannelModerationPatch
|
||||||
|
HigherScopedMemberPermissions []string
|
||||||
|
HigherScopedGuestPermissions []string
|
||||||
|
ShouldError bool
|
||||||
|
ShouldHaveNoChannelScheme bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "Removing create posts from members role",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createPosts,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(false)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
|
||||||
|
createPosts: {
|
||||||
|
Members: &model.ChannelModeratedRole{Value: false, Enabled: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createPosts,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Removing create reactions from members role",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(false)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
|
||||||
|
createReactions: {
|
||||||
|
Members: &model.ChannelModeratedRole{Value: false, Enabled: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Removing channel mentions from members role",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &channelMentions,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(false)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
|
||||||
|
channelMentions: {
|
||||||
|
Members: &model.ChannelModeratedRole{Value: false, Enabled: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &channelMentions,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Removing manage members from members role",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &manageMembers,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(false)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
|
||||||
|
manageMembers: {
|
||||||
|
Members: &model.ChannelModeratedRole{Value: false, Enabled: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &manageMembers,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Removing create posts from guests role",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createPosts,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(false)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
|
||||||
|
createPosts: {
|
||||||
|
Guests: &model.ChannelModeratedRole{Value: false, Enabled: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createPosts,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Removing create reactions from guests role",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(false)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
|
||||||
|
createReactions: {
|
||||||
|
Guests: &model.ChannelModeratedRole{Value: false, Enabled: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Removing channel mentions from guests role",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &channelMentions,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(false)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
|
||||||
|
channelMentions: {
|
||||||
|
Guests: &model.ChannelModeratedRole{Value: false, Enabled: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &channelMentions,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Removing manage members from guests role should error",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &manageMembers,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(false)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{},
|
||||||
|
ShouldError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Removing a permission that is not channel moderated should error",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &nonChannelModeratedPermission,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{
|
||||||
|
Members: model.NewBool(false),
|
||||||
|
Guests: model.NewBool(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{},
|
||||||
|
ShouldError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Error when adding a permission that is disabled in the parent member role",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createPosts,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{
|
||||||
|
Members: model.NewBool(true),
|
||||||
|
Guests: model.NewBool(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{},
|
||||||
|
HigherScopedMemberPermissions: []string{},
|
||||||
|
ShouldError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Error when adding a permission that is disabled in the parent guest role",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createPosts,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{
|
||||||
|
Members: model.NewBool(false),
|
||||||
|
Guests: model.NewBool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{},
|
||||||
|
HigherScopedGuestPermissions: []string{},
|
||||||
|
ShouldError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Removing a permission from the member role that is disabled in the parent guest role",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createPosts,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{
|
||||||
|
Members: model.NewBool(false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
|
||||||
|
createPosts: {
|
||||||
|
Members: &model.ChannelModeratedRole{Value: false, Enabled: true},
|
||||||
|
Guests: &model.ChannelModeratedRole{Value: false, Enabled: false},
|
||||||
|
},
|
||||||
|
createReactions: {
|
||||||
|
Guests: &model.ChannelModeratedRole{Value: false, Enabled: false},
|
||||||
|
},
|
||||||
|
channelMentions: {
|
||||||
|
Guests: &model.ChannelModeratedRole{Value: false, Enabled: false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HigherScopedGuestPermissions: []string{},
|
||||||
|
ShouldError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Channel should have no scheme when all moderated permissions are equivalent to higher scoped role",
|
||||||
|
ChannelModerationsPatch: []*model.ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createPosts,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{
|
||||||
|
Members: model.NewBool(true),
|
||||||
|
Guests: model.NewBool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{
|
||||||
|
Members: model.NewBool(true),
|
||||||
|
Guests: model.NewBool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: &channelMentions,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{
|
||||||
|
Members: model.NewBool(true),
|
||||||
|
Guests: model.NewBool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: &manageMembers,
|
||||||
|
Roles: &model.ChannelModeratedRolesPatch{
|
||||||
|
Members: model.NewBool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{},
|
||||||
|
ShouldHaveNoChannelScheme: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
higherScopedPermissionsOverriden := tc.HigherScopedMemberPermissions != nil || tc.HigherScopedGuestPermissions != nil
|
||||||
|
// If the test case restricts higher scoped permissions.
|
||||||
|
if higherScopedPermissionsOverriden {
|
||||||
|
higherScopedGuestRoleName, higherScopedMemberRoleName, _, _ := th.App.GetTeamSchemeChannelRoles(channel.TeamId)
|
||||||
|
if tc.HigherScopedMemberPermissions != nil {
|
||||||
|
higherScopedMemberRole, err := th.App.GetRoleByName(higherScopedMemberRoleName)
|
||||||
|
require.Nil(t, err)
|
||||||
|
originalPermissions := higherScopedMemberRole.Permissions
|
||||||
|
|
||||||
|
th.App.PatchRole(higherScopedMemberRole, &model.RolePatch{Permissions: &tc.HigherScopedMemberPermissions})
|
||||||
|
defer th.App.PatchRole(higherScopedMemberRole, &model.RolePatch{Permissions: &originalPermissions})
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.HigherScopedGuestPermissions != nil {
|
||||||
|
higherScopedGuestRole, err := th.App.GetRoleByName(higherScopedGuestRoleName)
|
||||||
|
require.Nil(t, err)
|
||||||
|
originalPermissions := higherScopedGuestRole.Permissions
|
||||||
|
|
||||||
|
th.App.PatchRole(higherScopedGuestRole, &model.RolePatch{Permissions: &tc.HigherScopedGuestPermissions})
|
||||||
|
defer th.App.PatchRole(higherScopedGuestRole, &model.RolePatch{Permissions: &originalPermissions})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
moderations, err := th.App.PatchChannelModerationsForChannel(channel, tc.ChannelModerationsPatch)
|
||||||
|
if tc.ShouldError {
|
||||||
|
require.Error(t, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
updatedChannel, _ := th.App.GetChannel(channel.Id)
|
||||||
|
if tc.ShouldHaveNoChannelScheme {
|
||||||
|
require.Nil(t, updatedChannel.SchemeId)
|
||||||
|
} else {
|
||||||
|
require.NotNil(t, updatedChannel.SchemeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, moderation := range moderations {
|
||||||
|
// If the permission is not found in the expected modified permissions table then require it to be true
|
||||||
|
if permission, found := tc.PermissionsModeratedByPatch[moderation.Name]; found && permission.Members != nil {
|
||||||
|
require.Equal(t, moderation.Roles.Members.Value, permission.Members.Value)
|
||||||
|
require.Equal(t, moderation.Roles.Members.Enabled, permission.Members.Enabled)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, moderation.Roles.Members.Value, true)
|
||||||
|
require.Equal(t, moderation.Roles.Members.Enabled, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if permission, found := tc.PermissionsModeratedByPatch[moderation.Name]; found && permission.Guests != nil {
|
||||||
|
require.Equal(t, moderation.Roles.Guests.Value, permission.Guests.Value)
|
||||||
|
require.Equal(t, moderation.Roles.Guests.Enabled, permission.Guests.Enabled)
|
||||||
|
} else if moderation.Name == manageMembers {
|
||||||
|
require.Empty(t, moderation.Roles.Guests)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, moderation.Roles.Guests.Value, true)
|
||||||
|
require.Equal(t, moderation.Roles.Guests.Enabled, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.RevertChannelModerationsPatch != nil {
|
||||||
|
th.App.PatchChannelModerationsForChannel(channel, tc.RevertChannelModerationsPatch)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -299,6 +299,10 @@
|
|||||||
"id": "api.channel.delete_channel.type.invalid",
|
"id": "api.channel.delete_channel.type.invalid",
|
||||||
"translation": "Unable to delete direct or group message channels"
|
"translation": "Unable to delete direct or group message channels"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "api.channel.get_channel_moderations.license.error",
|
||||||
|
"translation": "Your license does not support channel moderation"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "api.channel.guest_join_channel.post_and_forget",
|
"id": "api.channel.guest_join_channel.post_and_forget",
|
||||||
"translation": "%v joined the channel as guest."
|
"translation": "%v joined the channel as guest."
|
||||||
@@ -327,6 +331,10 @@
|
|||||||
"id": "api.channel.leave.left",
|
"id": "api.channel.leave.left",
|
||||||
"translation": "%v left the channel."
|
"translation": "%v left the channel."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "api.channel.patch_channel_moderations.license.error",
|
||||||
|
"translation": "Your license does not support channel moderation"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "api.channel.patch_update_channel.forbidden.app_error",
|
"id": "api.channel.patch_update_channel.forbidden.app_error",
|
||||||
"translation": "Failed to update the channel."
|
"translation": "Failed to update the channel."
|
||||||
|
|||||||
@@ -84,6 +84,31 @@ type DirectChannelForExport struct {
|
|||||||
Members *[]string
|
Members *[]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChannelModeration struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Roles *ChannelModeratedRoles `json:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChannelModeratedRoles struct {
|
||||||
|
Guests *ChannelModeratedRole `json:"guests"`
|
||||||
|
Members *ChannelModeratedRole `json:"members"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChannelModeratedRole struct {
|
||||||
|
Value bool `json:"value"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChannelModerationPatch struct {
|
||||||
|
Name *string `json:"name"`
|
||||||
|
Roles *ChannelModeratedRolesPatch `json:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChannelModeratedRolesPatch struct {
|
||||||
|
Guests *bool `json:"guests"`
|
||||||
|
Members *bool `json:"members"`
|
||||||
|
}
|
||||||
|
|
||||||
// ChannelSearchOpts contains options for searching channels.
|
// ChannelSearchOpts contains options for searching channels.
|
||||||
//
|
//
|
||||||
// NotAssociatedToGroup will exclude channels that have associated, active GroupChannels records.
|
// NotAssociatedToGroup will exclude channels that have associated, active GroupChannels records.
|
||||||
@@ -144,6 +169,18 @@ func ChannelPatchFromJson(data io.Reader) *ChannelPatch {
|
|||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ChannelModerationsFromJson(data io.Reader) []*ChannelModeration {
|
||||||
|
var o []*ChannelModeration
|
||||||
|
json.NewDecoder(data).Decode(&o)
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChannelModerationsPatchFromJson(data io.Reader) []*ChannelModerationPatch {
|
||||||
|
var o []*ChannelModerationPatch
|
||||||
|
json.NewDecoder(data).Decode(&o)
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Channel) Etag() string {
|
func (o *Channel) Etag() string {
|
||||||
return Etag(o.Id, o.UpdateAt)
|
return Etag(o.Id, o.UpdateAt)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4912,3 +4912,22 @@ func (c *Client4) PatchConfig(config *Config) (*Config, *Response) {
|
|||||||
defer closeBody(r)
|
defer closeBody(r)
|
||||||
return ConfigFromJson(r.Body), BuildResponse(r)
|
return ConfigFromJson(r.Body), BuildResponse(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client4) GetChannelModerations(channelID string, etag string) ([]*ChannelModeration, *Response) {
|
||||||
|
r, err := c.DoApiGet(c.GetChannelRoute(channelID)+"/moderations", etag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, BuildErrorResponse(r, err)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
return ChannelModerationsFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) PatchChannelModerations(channelID string, patch []*ChannelModerationPatch) ([]*ChannelModeration, *Response) {
|
||||||
|
payload, _ := json.Marshal(patch)
|
||||||
|
r, err := c.DoApiPut(c.GetChannelRoute(channelID)+"/moderations/patch", string(payload))
|
||||||
|
if err != nil {
|
||||||
|
return nil, BuildErrorResponse(r, err)
|
||||||
|
}
|
||||||
|
defer closeBody(r)
|
||||||
|
return ChannelModerationsFromJson(r.Body), BuildResponse(r)
|
||||||
|
}
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ var PERMISSION_MANAGE_SYSTEM *Permission
|
|||||||
|
|
||||||
var ALL_PERMISSIONS []*Permission
|
var ALL_PERMISSIONS []*Permission
|
||||||
|
|
||||||
|
var CHANNEL_MODERATED_PERMISSIONS []string
|
||||||
|
var CHANNEL_MODERATED_PERMISSIONS_MAP map[string]string
|
||||||
|
|
||||||
func initializePermissions() {
|
func initializePermissions() {
|
||||||
PERMISSION_INVITE_USER = &Permission{
|
PERMISSION_INVITE_USER = &Permission{
|
||||||
"invite_user",
|
"invite_user",
|
||||||
@@ -641,6 +644,22 @@ func initializePermissions() {
|
|||||||
PERMISSION_DEMOTE_TO_GUEST,
|
PERMISSION_DEMOTE_TO_GUEST,
|
||||||
PERMISSION_USE_CHANNEL_MENTIONS,
|
PERMISSION_USE_CHANNEL_MENTIONS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CHANNEL_MODERATED_PERMISSIONS = []string{
|
||||||
|
PERMISSION_CREATE_POST.Id,
|
||||||
|
"create_reactions",
|
||||||
|
"manage_members",
|
||||||
|
PERMISSION_USE_CHANNEL_MENTIONS.Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
CHANNEL_MODERATED_PERMISSIONS_MAP = map[string]string{
|
||||||
|
PERMISSION_CREATE_POST.Id: CHANNEL_MODERATED_PERMISSIONS[0],
|
||||||
|
PERMISSION_ADD_REACTION.Id: CHANNEL_MODERATED_PERMISSIONS[1],
|
||||||
|
PERMISSION_REMOVE_REACTION.Id: CHANNEL_MODERATED_PERMISSIONS[1],
|
||||||
|
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id: CHANNEL_MODERATED_PERMISSIONS[2],
|
||||||
|
PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id: CHANNEL_MODERATED_PERMISSIONS[2],
|
||||||
|
PERMISSION_USE_CHANNEL_MENTIONS.Id: CHANNEL_MODERATED_PERMISSIONS[3],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
109
model/role.go
109
model/role.go
@@ -123,6 +123,115 @@ func PermissionsChangedByPatch(role *Role, patch *RolePatch) []string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ChannelModeratedPermissionsChangedByPatch(role *Role, patch *RolePatch) []string {
|
||||||
|
var result []string
|
||||||
|
|
||||||
|
if patch.Permissions == nil {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
roleMap := make(map[string]bool)
|
||||||
|
patchMap := make(map[string]bool)
|
||||||
|
|
||||||
|
for _, permission := range role.Permissions {
|
||||||
|
if channelModeratedPermissionName, found := CHANNEL_MODERATED_PERMISSIONS_MAP[permission]; found {
|
||||||
|
roleMap[channelModeratedPermissionName] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, permission := range *patch.Permissions {
|
||||||
|
if channelModeratedPermissionName, found := CHANNEL_MODERATED_PERMISSIONS_MAP[permission]; found {
|
||||||
|
patchMap[channelModeratedPermissionName] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for permissionKey := range roleMap {
|
||||||
|
if !patchMap[permissionKey] {
|
||||||
|
result = append(result, permissionKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for permissionKey := range patchMap {
|
||||||
|
if !roleMap[permissionKey] {
|
||||||
|
result = append(result, permissionKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChannelModeratedPermissions returns a map of channel moderated permissions that the role has access to
|
||||||
|
func (r *Role) GetChannelModeratedPermissions() map[string]bool {
|
||||||
|
moderatedPermissions := make(map[string]bool)
|
||||||
|
for _, permission := range r.Permissions {
|
||||||
|
if _, found := CHANNEL_MODERATED_PERMISSIONS_MAP[permission]; !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for moderated, moderatedPermissionValue := range CHANNEL_MODERATED_PERMISSIONS_MAP {
|
||||||
|
if moderated == permission {
|
||||||
|
moderatedPermissions[moderatedPermissionValue] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return moderatedPermissions
|
||||||
|
}
|
||||||
|
|
||||||
|
// RolePatchFromChannelModerationsPatch Creates and returns a RolePatch based on a slice of ChannelModerationPatchs, roleName is expected to be either "members" or "guests".
|
||||||
|
func (r *Role) RolePatchFromChannelModerationsPatch(channelModerationsPatch []*ChannelModerationPatch, roleName string) *RolePatch {
|
||||||
|
permissionsToAddToPatch := make(map[string]bool)
|
||||||
|
|
||||||
|
// Iterate through the list of existing permissions on the role and append permissions that we want to keep.
|
||||||
|
for _, permission := range r.Permissions {
|
||||||
|
// Permission is not moderated so dont add it to the patch and skip the channelModerationsPatch
|
||||||
|
if _, isModerated := CHANNEL_MODERATED_PERMISSIONS_MAP[permission]; !isModerated {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
permissionEnabled := true
|
||||||
|
// Check if permission has a matching moderated permission name inside the channel moderation patch
|
||||||
|
for _, channelModerationPatch := range channelModerationsPatch {
|
||||||
|
if *channelModerationPatch.Name == CHANNEL_MODERATED_PERMISSIONS_MAP[permission] {
|
||||||
|
// Permission key exists in patch with a value of false so skip over it
|
||||||
|
if roleName == "members" {
|
||||||
|
if channelModerationPatch.Roles.Members != nil && !*channelModerationPatch.Roles.Members {
|
||||||
|
permissionEnabled = false
|
||||||
|
}
|
||||||
|
} else if roleName == "guests" {
|
||||||
|
if channelModerationPatch.Roles.Guests != nil && !*channelModerationPatch.Roles.Guests {
|
||||||
|
permissionEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if permissionEnabled {
|
||||||
|
permissionsToAddToPatch[permission] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through the patch and add any permissions that dont already exist on the role
|
||||||
|
for _, channelModerationPatch := range channelModerationsPatch {
|
||||||
|
for permission, moderatedPermissionName := range CHANNEL_MODERATED_PERMISSIONS_MAP {
|
||||||
|
if roleName == "members" && channelModerationPatch.Roles.Members != nil && *channelModerationPatch.Roles.Members && *channelModerationPatch.Name == moderatedPermissionName {
|
||||||
|
permissionsToAddToPatch[permission] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if roleName == "guests" && channelModerationPatch.Roles.Guests != nil && *channelModerationPatch.Roles.Guests && *channelModerationPatch.Name == moderatedPermissionName {
|
||||||
|
permissionsToAddToPatch[permission] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patchPermissions := make([]string, 0, len(permissionsToAddToPatch))
|
||||||
|
for permission := range permissionsToAddToPatch {
|
||||||
|
patchPermissions = append(patchPermissions, permission)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &RolePatch{Permissions: &patchPermissions}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Role) IsValid() bool {
|
func (r *Role) IsValid() bool {
|
||||||
if len(r.Id) != 26 {
|
if len(r.Id) != 26 {
|
||||||
return false
|
return false
|
||||||
|
|||||||
273
model/role_test.go
Обычный файл
273
model/role_test.go
Обычный файл
@@ -0,0 +1,273 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestChannelModeratedPermissionsChangedByPatch(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
Name string
|
||||||
|
Permissions []string
|
||||||
|
PatchPermissions []string
|
||||||
|
Expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Empty patch returns empty slice",
|
||||||
|
[]string{},
|
||||||
|
[]string{},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Adds permissions to empty initial permissions list",
|
||||||
|
[]string{},
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id, PERMISSION_ADD_REACTION.Id},
|
||||||
|
[]string{CHANNEL_MODERATED_PERMISSIONS[0], CHANNEL_MODERATED_PERMISSIONS[1]},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Ignores non moderated permissions in initial permissions list",
|
||||||
|
[]string{PERMISSION_ASSIGN_BOT.Id},
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id, PERMISSION_REMOVE_REACTION.Id},
|
||||||
|
[]string{CHANNEL_MODERATED_PERMISSIONS[0], CHANNEL_MODERATED_PERMISSIONS[1]},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Adds removed moderated permissions from initial permissions list",
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id},
|
||||||
|
[]string{},
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"No changes returns empty slice",
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id, PERMISSION_ASSIGN_BOT.Id},
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
baseRole := &Role{Permissions: tc.Permissions}
|
||||||
|
rolePatch := &RolePatch{Permissions: &tc.PatchPermissions}
|
||||||
|
result := ChannelModeratedPermissionsChangedByPatch(baseRole, rolePatch)
|
||||||
|
assert.ElementsMatch(t, tc.Expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRolePatchFromChannelModerationsPatch(t *testing.T) {
|
||||||
|
createPosts := CHANNEL_MODERATED_PERMISSIONS[0]
|
||||||
|
createReactions := CHANNEL_MODERATED_PERMISSIONS[1]
|
||||||
|
manageMembers := CHANNEL_MODERATED_PERMISSIONS[2]
|
||||||
|
channelMentions := CHANNEL_MODERATED_PERMISSIONS[3]
|
||||||
|
|
||||||
|
basePermissions := []string{
|
||||||
|
PERMISSION_ADD_REACTION.Id,
|
||||||
|
PERMISSION_REMOVE_REACTION.Id,
|
||||||
|
PERMISSION_CREATE_POST.Id,
|
||||||
|
PERMISSION_USE_CHANNEL_MENTIONS.Id,
|
||||||
|
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
|
||||||
|
PERMISSION_UPLOAD_FILE.Id,
|
||||||
|
PERMISSION_GET_PUBLIC_LINK.Id,
|
||||||
|
PERMISSION_USE_SLASH_COMMANDS.Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
baseModeratedPermissions := []string{
|
||||||
|
PERMISSION_ADD_REACTION.Id,
|
||||||
|
PERMISSION_REMOVE_REACTION.Id,
|
||||||
|
PERMISSION_CREATE_POST.Id,
|
||||||
|
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
|
||||||
|
PERMISSION_USE_CHANNEL_MENTIONS.Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
Name string
|
||||||
|
Permissions []string
|
||||||
|
ChannelModerationsPatch []*ChannelModerationPatch
|
||||||
|
RoleName string
|
||||||
|
ExpectedPatchPermissions []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Patch to member role adding a permission that already exists",
|
||||||
|
basePermissions,
|
||||||
|
[]*ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"members",
|
||||||
|
baseModeratedPermissions,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Patch to member role with moderation patch for guest role",
|
||||||
|
basePermissions,
|
||||||
|
[]*ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Guests: NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"members",
|
||||||
|
baseModeratedPermissions,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Patch to guest role with moderation patch for member role",
|
||||||
|
basePermissions,
|
||||||
|
[]*ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"guests",
|
||||||
|
baseModeratedPermissions,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Patch to member role removing multiple channel moderated permissions",
|
||||||
|
basePermissions,
|
||||||
|
[]*ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(false)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: &manageMembers,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(false)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: &channelMentions,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(false)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"members",
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Patch to guest role removing multiple channel moderated permissions",
|
||||||
|
basePermissions,
|
||||||
|
[]*ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Guests: NewBool(false)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: &manageMembers,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Guests: NewBool(false)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: &channelMentions,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Guests: NewBool(false)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"guests",
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Patch enabling and removing multiple channel moderated permissions ",
|
||||||
|
[]string{PERMISSION_ADD_REACTION.Id, PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id},
|
||||||
|
[]*ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(false)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: &manageMembers,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(false)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: &channelMentions,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(true)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: &createPosts,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"members",
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id, PERMISSION_USE_CHANNEL_MENTIONS.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Patch enabling a partially enabled permission",
|
||||||
|
[]string{PERMISSION_ADD_REACTION.Id},
|
||||||
|
[]*ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"members",
|
||||||
|
[]string{PERMISSION_ADD_REACTION.Id, PERMISSION_REMOVE_REACTION.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Patch disabling a partially disabled permission",
|
||||||
|
[]string{PERMISSION_ADD_REACTION.Id},
|
||||||
|
[]*ChannelModerationPatch{
|
||||||
|
{
|
||||||
|
Name: &createReactions,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(false)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: &createPosts,
|
||||||
|
Roles: &ChannelModeratedRolesPatch{Members: NewBool(true)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"members",
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
baseRole := &Role{Permissions: tc.Permissions}
|
||||||
|
rolePatch := baseRole.RolePatchFromChannelModerationsPatch(tc.ChannelModerationsPatch, tc.RoleName)
|
||||||
|
assert.ElementsMatch(t, tc.ExpectedPatchPermissions, *rolePatch.Permissions)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetChannelModeratedPermissions(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Name string
|
||||||
|
Permissions []string
|
||||||
|
Expected map[string]bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Filters non moderated permissions",
|
||||||
|
[]string{PERMISSION_CREATE_BOT.Id},
|
||||||
|
map[string]bool{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Returns a map of moderated permissions",
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id, PERMISSION_ADD_REACTION.Id, PERMISSION_REMOVE_REACTION.Id, PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, PERMISSION_USE_CHANNEL_MENTIONS.Id},
|
||||||
|
map[string]bool{
|
||||||
|
CHANNEL_MODERATED_PERMISSIONS[0]: true,
|
||||||
|
CHANNEL_MODERATED_PERMISSIONS[1]: true,
|
||||||
|
CHANNEL_MODERATED_PERMISSIONS[2]: true,
|
||||||
|
CHANNEL_MODERATED_PERMISSIONS[3]: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Returns a map of moderated permissions when non moderated present",
|
||||||
|
[]string{PERMISSION_CREATE_POST.Id, PERMISSION_CREATE_DIRECT_CHANNEL.Id},
|
||||||
|
map[string]bool{
|
||||||
|
CHANNEL_MODERATED_PERMISSIONS[0]: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Returns a nothing when no permissions present",
|
||||||
|
[]string{},
|
||||||
|
map[string]bool{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
role := &Role{Permissions: tc.Permissions}
|
||||||
|
moderatedPermissions := role.GetChannelModeratedPermissions()
|
||||||
|
for permission := range moderatedPermissions {
|
||||||
|
assert.Equal(t, moderatedPermissions[permission], tc.Expected[permission])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Ссылка в новой задаче
Block a user