MM-22212 & MM-22208: Read from the higher-scoped scheme if the permission is non-moderated. (#13813)

* MM-22212: Read non-moderated permissions from higher-scoped scheme.

* MM-2212: Corrects test count in comment.

* MM-22212: Adds godoc comment.

* MM-2212: Switches to the channel roles check in a few more places.

* MM-22212: Refactors and fixes.

* MM-22212: Reverts change, no longer required.

* MM-22212: Removes translation.

* MM-22212: Un-comments merged new permission.

* MM-22212: Un-comments merged new permission.

* MM-22212: Performance tweak.

* MM-22212: Fixes some fmting.

* MM-22212: Add unit test for newly-added store methods.

* MM-22212: Renames app method.

* MM-22212: Re-uses existing function to find string in slice.

* MM-22212: Keeps 'higher-scoped' terminology for consistency.

* MM-22212: Refactors based on PR feedback.

* MM-22212: Fix for some bad merging.

* MM-22212: Renamed some things.

* MM-22212: Use an 'else' instead of a 'continue' for readability.

* MM-22212: Caches (*SqlRoleStore).ChannelRolesUnderTeamRole.

* MM-22212: Adds mock to new cache store.

* MM-22212: Adds missing open tracing app layer methods.

* MM-22212: Adds migration to add moderated permissions to channel_admin if present on channel_user.

* MM-22212: Migrates team schemes. Removes unused AppError.

* MM-22212: Fix for for if.

* MM-22212: Fixes iterator.

* MM-22212: Updates open tracing generated methods.

* MM-22212: Fix mocks.

* MM-22212: Change migration key name.

* MM-22212: Switched to data structure from other branch.

* MM-22212: Fixes tests after adding 'use_channel_mentions' to the channel_admin role.

* MM-22212: Adds tracking of channel moderation.

* Revert "MM-22212: Adds tracking of channel moderation."

This reverts commit 23689efa22c112e4ba37f6a212535dd7ebfb63db.

* MM-22212: Switch some functions to methods and vice versa.

* MM-22212: Fix for refactor bug not notifiying websocket about changed role.

* MM-22212: Adds test for public/private 'manage_members' handling.

* MM-22122 Fix manage channel members edge case for public and private channels (#14049)

* MM-22212: Adds moderated permission to team_admin.

* MM-22212: Updates migration.

* MM-22212: Revert unnecessary update to default roles.

* Add channel scheme updated event when channel scheme is deleted or created (#14057)

* MM-22212: Adds newline.

* MM-22212: Migration fix.

* MM-22212: Fix for migration.

* MM-22212: Test fix.

Co-authored-by: Farhan Munshi <3207297+fm2munsh@users.noreply.github.com>
Этот коммит содержится в:
Martin Kraft
2020-03-23 13:44:20 -04:00
коммит произвёл GitHub
родитель ace46443b3
Коммит 4d99aa22ba
39 изменённых файлов: 1252 добавлений и 123 удалений

Просмотреть файл

@@ -102,7 +102,7 @@ type AppIface interface {
// Notifies cluster peers through config change.
DisablePlugin(id string) *model.AppError
// DoPermissionsMigrations execute all the permissions migrations need by the current version.
DoPermissionsMigrations() *model.AppError
DoPermissionsMigrations() error
// EnablePlugin will set the config for an installed plugin to enabled, triggering asynchronous
// activation if inactive anywhere in the cluster.
// Notifies cluster peers through config change.
@@ -782,7 +782,7 @@ type AppIface interface {
SaveLicense(licenseBytes []byte) (*model.License, *model.AppError)
SaveReactionForPost(reaction *model.Reaction) (*model.Reaction, *model.AppError)
SaveUserTermsOfService(userId, termsOfServiceId string, accepted bool) *model.AppError
SchemesIterator(batchSize int) func() []*model.Scheme
SchemesIterator(scope string, batchSize int) func() []*model.Scheme
SearchArchivedChannels(teamId string, term string, userId string) (*model.ChannelList, *model.AppError)
SearchChannels(teamId string, term string) (*model.ChannelList, *model.AppError)
SearchChannelsForUser(userId, teamId, term string) (*model.ChannelList, *model.AppError)

Просмотреть файл

@@ -596,6 +596,12 @@ func TestDoEmojisPermissionsMigration(t *testing.T) {
model.PERMISSION_DELETE_OTHERS_POSTS.Id,
model.PERMISSION_CREATE_EMOJIS.Id,
model.PERMISSION_DELETE_EMOJIS.Id,
model.PERMISSION_ADD_REACTION.Id,
model.PERMISSION_CREATE_POST.Id,
model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
model.PERMISSION_REMOVE_REACTION.Id,
model.PERMISSION_USE_CHANNEL_MENTIONS.Id,
}
sort.Strings(expected2)
sort.Strings(role2.Permissions)

Просмотреть файл

@@ -6,7 +6,7 @@ package app
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v5/model"
)
@@ -31,7 +31,13 @@ func TestCheckIfRolesGrantPermission(t *testing.T) {
}
for _, testcase := range cases {
assert.Equal(t, th.App.RolesGrantPermission(testcase.roles, testcase.permissionId), testcase.shouldGrant)
require.Equal(t, th.App.RolesGrantPermission(testcase.roles, testcase.permissionId), testcase.shouldGrant)
}
}
func TestChannelRolesGrantPermission(t *testing.T) {
testPermissionInheritance(t, func(t *testing.T, th *TestHelper, testData permissionInheritanceTestData) {
require.Equal(t, testData.shouldHavePermission, th.App.RolesGrantPermission([]string{testData.channelRole.Name}, testData.permission.Id), "row: %+v\n", testData.truthTableRow)
})
}

Просмотреть файл

@@ -720,7 +720,7 @@ func (a *App) GetChannelModerationsForChannel(channel *model.Channel) ([]*model.
return nil, err
}
return buildChannelModerations(memberRole, guestRole, higherScopedMemberRole, higherScopedGuestRole), nil
return buildChannelModerations(channel.Type, 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.
@@ -736,8 +736,8 @@ func (a *App) PatchChannelModerationsForChannel(channel *model.Channel, channelM
return nil, err
}
higherScopedMemberPermissions := higherScopedMemberRole.GetChannelModeratedPermissions()
higherScopedGuestPermissions := higherScopedGuestRole.GetChannelModeratedPermissions()
higherScopedMemberPermissions := higherScopedMemberRole.GetChannelModeratedPermissions(channel.Type)
higherScopedGuestPermissions := higherScopedGuestRole.GetChannelModeratedPermissions(channel.Type)
for _, moderationPatch := range channelModerationsPatch {
if moderationPatch.Roles.Members != nil && *moderationPatch.Roles.Members && !higherScopedMemberPermissions[*moderationPatch.Name] {
@@ -753,6 +753,9 @@ func (a *App) PatchChannelModerationsForChannel(channel *model.Channel, channelM
if _, err = a.CreateChannelScheme(channel); err != nil {
return nil, err
}
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_SCHEME_UPDATED, "", channel.Id, "", nil)
a.Publish(message)
mlog.Info("Permission scheme created.", mlog.String("channel_id", channel.Id), mlog.String("channel_name", channel.Name))
}
@@ -796,6 +799,10 @@ func (a *App) PatchChannelModerationsForChannel(channel *model.Channel, channelM
if _, err = a.DeleteChannelScheme(channel); err != nil {
return nil, err
}
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_SCHEME_UPDATED, "", channel.Id, "", nil)
a.Publish(message)
memberRole = higherScopedMemberRole
guestRole = higherScopedGuestRole
mlog.Info("Permission scheme deleted.", mlog.String("channel_id", channel.Id), mlog.String("channel_name", channel.Name))
@@ -810,14 +817,14 @@ func (a *App) PatchChannelModerationsForChannel(channel *model.Channel, channelM
}
}
return buildChannelModerations(memberRole, guestRole, higherScopedMemberRole, higherScopedGuestRole), nil
return buildChannelModerations(channel.Type, 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()
func buildChannelModerations(channelType string, memberRole *model.Role, guestRole *model.Role, higherScopedMemberRole *model.Role, higherScopedGuestRole *model.Role) []*model.ChannelModeration {
memberPermissions := memberRole.GetChannelModeratedPermissions(channelType)
guestPermissions := guestRole.GetChannelModeratedPermissions(channelType)
higherScopedMemberPermissions := higherScopedMemberRole.GetChannelModeratedPermissions(channelType)
higherScopedGuestPermissions := higherScopedGuestRole.GetChannelModeratedPermissions(channelType)
var channelModerations []*model.ChannelModeration
for _, permissionKey := range model.CHANNEL_MODERATED_PERMISSIONS {

Просмотреть файл

@@ -905,7 +905,9 @@ func TestAllowChannelMentions(t *testing.T) {
t.Run("should return false for a post where the post user does not have USE_CHANNEL_MENTIONS permission", func(t *testing.T) {
defer th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
defer th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
allowChannelMentions := th.App.allowChannelMentions(post, 5)
assert.False(t, allowChannelMentions)
})

Просмотреть файл

@@ -3046,7 +3046,7 @@ func (a *OpenTracingAppLayer) DoLogin(w http.ResponseWriter, r *http.Request, us
return resultVar0
}
func (a *OpenTracingAppLayer) DoPermissionsMigrations() *model.AppError {
func (a *OpenTracingAppLayer) DoPermissionsMigrations() error {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.DoPermissionsMigrations")
@@ -3060,11 +3060,6 @@ func (a *OpenTracingAppLayer) DoPermissionsMigrations() *model.AppError {
defer span.Finish()
resultVar0 := a.app.DoPermissionsMigrations()
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
@@ -11547,7 +11542,7 @@ func (a *OpenTracingAppLayer) SaveUserTermsOfService(userId string, termsOfServi
return resultVar0
}
func (a *OpenTracingAppLayer) SchemesIterator(batchSize int) func() []*model.Scheme {
func (a *OpenTracingAppLayer) SchemesIterator(scope string, batchSize int) func() []*model.Scheme {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SchemesIterator")
@@ -11559,7 +11554,7 @@ func (a *OpenTracingAppLayer) SchemesIterator(batchSize int) func() []*model.Sch
}()
defer span.Finish()
resultVar0 := a.app.SchemesIterator(batchSize)
resultVar0 := a.app.SchemesIterator(scope, batchSize)
return resultVar0
}

Просмотреть файл

@@ -65,7 +65,7 @@ func (a *App) ResetPermissionsSystem() *model.AppError {
func (a *App) ExportPermissions(w io.Writer) error {
next := a.SchemesIterator(permissionsExportBatchSize)
next := a.SchemesIterator("", permissionsExportBatchSize)
var schemeBatch []*model.Scheme
for schemeBatch = next(); len(schemeBatch) > 0; schemeBatch = next() {

Просмотреть файл

@@ -49,6 +49,10 @@ const (
PERMISSION_USE_CHANNEL_MENTIONS = "use_channel_mentions"
PERMISSION_CREATE_POST = "create_post"
PERMISSION_CREATE_POST_PUBLIC = "create_post_public"
PERMISSION_ADD_REACTION = "add_reaction"
PERMISSION_REMOVE_REACTION = "remove_reaction"
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS = "manage_public_channel_members"
PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS = "manage_private_channel_members"
)
func isRole(role string) func(string, map[string]map[string]bool) bool {
@@ -152,7 +156,7 @@ func (a *App) doPermissionsMigration(key string, migrationMap permissionsMap) *m
return nil
}
func getEmojisPermissionsSplitMigration() permissionsMap {
func (a *App) getEmojisPermissionsSplitMigration() (permissionsMap, error) {
return permissionsMap{
permissionTransformation{
On: permissionExists(PERMISSION_MANAGE_EMOJIS),
@@ -164,10 +168,10 @@ func getEmojisPermissionsSplitMigration() permissionsMap {
Add: []string{PERMISSION_DELETE_OTHERS_EMOJIS},
Remove: []string{PERMISSION_MANAGE_OTHERS_EMOJIS},
},
}
}, nil
}
func getWebhooksPermissionsSplitMigration() permissionsMap {
func (a *App) getWebhooksPermissionsSplitMigration() (permissionsMap, error) {
return permissionsMap{
permissionTransformation{
On: permissionExists(PERMISSION_MANAGE_WEBHOOKS),
@@ -179,10 +183,10 @@ func getWebhooksPermissionsSplitMigration() permissionsMap {
Add: []string{PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS, PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS},
Remove: []string{PERMISSION_MANAGE_OTHERS_WEBHOOKS},
},
}
}, nil
}
func getListJoinPublicPrivateTeamsPermissionsMigration() permissionsMap {
func (a *App) getListJoinPublicPrivateTeamsPermissionsMigration() (permissionsMap, error) {
return permissionsMap{
permissionTransformation{
On: isRole(model.SYSTEM_ADMIN_ROLE_ID),
@@ -194,29 +198,29 @@ func getListJoinPublicPrivateTeamsPermissionsMigration() permissionsMap {
Add: []string{PERMISSION_LIST_PUBLIC_TEAMS, PERMISSION_JOIN_PUBLIC_TEAMS},
Remove: []string{},
},
}
}, nil
}
func removePermanentDeleteUserMigration() permissionsMap {
func (a *App) removePermanentDeleteUserMigration() (permissionsMap, error) {
return permissionsMap{
permissionTransformation{
On: permissionExists(PERMISSION_PERMANENT_DELETE_USER),
Remove: []string{PERMISSION_PERMANENT_DELETE_USER},
},
}
}, nil
}
func getAddBotPermissionsMigration() permissionsMap {
func (a *App) getAddBotPermissionsMigration() (permissionsMap, error) {
return permissionsMap{
permissionTransformation{
On: isRole(model.SYSTEM_ADMIN_ROLE_ID),
Add: []string{PERMISSION_CREATE_BOT, PERMISSION_READ_BOTS, PERMISSION_READ_OTHERS_BOTS, PERMISSION_MANAGE_BOTS, PERMISSION_MANAGE_OTHERS_BOTS},
Remove: []string{},
},
}
}, nil
}
func applyChannelManageDeleteToChannelUser() permissionsMap {
func (a *App) applyChannelManageDeleteToChannelUser() (permissionsMap, error) {
return permissionsMap{
permissionTransformation{
On: permissionAnd(isRole(model.CHANNEL_USER_ROLE_ID), onOtherRole(model.TEAM_USER_ROLE_ID, permissionExists(PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES))),
@@ -234,10 +238,10 @@ func applyChannelManageDeleteToChannelUser() permissionsMap {
On: permissionAnd(isRole(model.CHANNEL_USER_ROLE_ID), onOtherRole(model.TEAM_USER_ROLE_ID, permissionExists(PERMISSION_DELETE_PUBLIC_CHANNEL))),
Add: []string{PERMISSION_DELETE_PUBLIC_CHANNEL},
},
}
}, nil
}
func removeChannelManageDeleteFromTeamUser() permissionsMap {
func (a *App) removeChannelManageDeleteFromTeamUser() (permissionsMap, error) {
return permissionsMap{
permissionTransformation{
On: permissionAnd(isRole(model.TEAM_USER_ROLE_ID), permissionExists(PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES)),
@@ -255,10 +259,10 @@ func removeChannelManageDeleteFromTeamUser() permissionsMap {
On: permissionAnd(isRole(model.TEAM_USER_ROLE_ID), permissionExists(PERMISSION_DELETE_PUBLIC_CHANNEL)),
Remove: []string{PERMISSION_DELETE_PUBLIC_CHANNEL},
},
}
}, nil
}
func getViewMembersPermissionMigration() permissionsMap {
func (a *App) getViewMembersPermissionMigration() (permissionsMap, error) {
return permissionsMap{
permissionTransformation{
On: isRole(model.SYSTEM_USER_ROLE_ID),
@@ -268,47 +272,154 @@ func getViewMembersPermissionMigration() permissionsMap {
On: isRole(model.SYSTEM_ADMIN_ROLE_ID),
Add: []string{PERMISSION_VIEW_MEMBERS},
},
}
}, nil
}
func getAddManageGuestsPermissionsMigration() permissionsMap {
func (a *App) getAddManageGuestsPermissionsMigration() (permissionsMap, error) {
return permissionsMap{
permissionTransformation{
On: isRole(model.SYSTEM_ADMIN_ROLE_ID),
Add: []string{PERMISSION_PROMOTE_GUEST, PERMISSION_DEMOTE_TO_GUEST, PERMISSION_INVITE_GUEST},
},
}
}, nil
}
func getAddUseMentionChannelsPermissionMigration() permissionsMap {
return permissionsMap{
permissionTransformation{
On: permissionOr(permissionExists(PERMISSION_CREATE_POST), permissionExists(PERMISSION_CREATE_POST_PUBLIC)),
Add: []string{PERMISSION_USE_CHANNEL_MENTIONS},
},
func (a *App) channelModerationPermissionsMigration() (permissionsMap, error) {
transformations := permissionsMap{}
var allTeamSchemes []*model.Scheme
next := a.SchemesIterator(model.SCHEME_SCOPE_TEAM, 100)
var schemeBatch []*model.Scheme
for schemeBatch = next(); len(schemeBatch) > 0; schemeBatch = next() {
allTeamSchemes = append(allTeamSchemes, schemeBatch...)
}
moderatedPermissionsMinusCreatePost := []string{
PERMISSION_ADD_REACTION,
PERMISSION_REMOVE_REACTION,
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS,
PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS,
PERMISSION_USE_CHANNEL_MENTIONS,
}
teamAndChannelAdminConditionalTransformations := func(teamAdminID, channelAdminID, channelUserID, channelGuestID string) []permissionTransformation {
transformations := []permissionTransformation{}
for _, perm := range moderatedPermissionsMinusCreatePost {
// add each moderated permission to the channel admin if channel user or guest has the permission
trans := permissionTransformation{
On: permissionAnd(
isRole(channelAdminID),
permissionOr(
onOtherRole(channelUserID, permissionExists(perm)),
onOtherRole(channelGuestID, permissionExists(perm)),
),
),
Add: []string{perm},
}
transformations = append(transformations, trans)
// add each moderated permission to the team admin if channel admin, user, or guest has the permission
trans = permissionTransformation{
On: permissionAnd(
isRole(teamAdminID),
permissionOr(
onOtherRole(channelAdminID, permissionExists(perm)),
onOtherRole(channelUserID, permissionExists(perm)),
onOtherRole(channelGuestID, permissionExists(perm)),
),
),
Add: []string{perm},
}
transformations = append(transformations, trans)
}
return transformations
}
for _, ts := range allTeamSchemes {
// ensure all team scheme channel admins have create_post because it's not exposed via the UI
trans := permissionTransformation{
On: isRole(ts.DefaultChannelAdminRole),
Add: []string{PERMISSION_CREATE_POST},
}
transformations = append(transformations, trans)
// ensure all team scheme team admins have create_post because it's not exposed via the UI
trans = permissionTransformation{
On: isRole(ts.DefaultTeamAdminRole),
Add: []string{PERMISSION_CREATE_POST},
}
transformations = append(transformations, trans)
// conditionally add all other moderated permissions to team and channel admins
transformations = append(transformations, teamAndChannelAdminConditionalTransformations(
ts.DefaultTeamAdminRole,
ts.DefaultChannelAdminRole,
ts.DefaultChannelUserRole,
ts.DefaultChannelGuestRole,
)...)
}
// ensure team admins have create_post
transformations = append(transformations, permissionTransformation{
On: isRole(model.TEAM_ADMIN_ROLE_ID),
Add: []string{PERMISSION_CREATE_POST},
})
// ensure channel admins have create_post
transformations = append(transformations, permissionTransformation{
On: isRole(model.CHANNEL_ADMIN_ROLE_ID),
Add: []string{PERMISSION_CREATE_POST},
})
// conditionally add all other moderated permissions to team and channel admins
transformations = append(transformations, teamAndChannelAdminConditionalTransformations(
model.TEAM_ADMIN_ROLE_ID,
model.CHANNEL_ADMIN_ROLE_ID,
model.CHANNEL_USER_ROLE_ID,
model.CHANNEL_GUEST_ROLE_ID,
)...)
// ensure system admin has all of the moderated permissions
transformations = append(transformations, permissionTransformation{
On: isRole(model.SYSTEM_ADMIN_ROLE_ID),
Add: append(moderatedPermissionsMinusCreatePost, PERMISSION_CREATE_POST),
})
// add the new use_channel_mentions permission to everyone who has create_post
transformations = append(transformations, permissionTransformation{
On: permissionOr(permissionExists(PERMISSION_CREATE_POST), permissionExists(PERMISSION_CREATE_POST_PUBLIC)),
Add: []string{PERMISSION_USE_CHANNEL_MENTIONS},
})
return transformations, nil
}
// DoPermissionsMigrations execute all the permissions migrations need by the current version.
func (a *App) DoPermissionsMigrations() *model.AppError {
func (a *App) DoPermissionsMigrations() error {
PermissionsMigrations := []struct {
Key string
Migration func() permissionsMap
Migration func() (permissionsMap, error)
}{
{Key: model.MIGRATION_KEY_EMOJI_PERMISSIONS_SPLIT, Migration: getEmojisPermissionsSplitMigration},
{Key: model.MIGRATION_KEY_WEBHOOK_PERMISSIONS_SPLIT, Migration: getWebhooksPermissionsSplitMigration},
{Key: model.MIGRATION_KEY_LIST_JOIN_PUBLIC_PRIVATE_TEAMS, Migration: getListJoinPublicPrivateTeamsPermissionsMigration},
{Key: model.MIGRATION_KEY_REMOVE_PERMANENT_DELETE_USER, Migration: removePermanentDeleteUserMigration},
{Key: model.MIGRATION_KEY_ADD_BOT_PERMISSIONS, Migration: getAddBotPermissionsMigration},
{Key: model.MIGRATION_KEY_APPLY_CHANNEL_MANAGE_DELETE_TO_CHANNEL_USER, Migration: applyChannelManageDeleteToChannelUser},
{Key: model.MIGRATION_KEY_REMOVE_CHANNEL_MANAGE_DELETE_FROM_TEAM_USER, Migration: removeChannelManageDeleteFromTeamUser},
{Key: model.MIGRATION_KEY_VIEW_MEMBERS_NEW_PERMISSION, Migration: getViewMembersPermissionMigration},
{Key: model.MIGRATION_KEY_ADD_MANAGE_GUESTS_PERMISSIONS, Migration: getAddManageGuestsPermissionsMigration},
{Key: model.MIGRATION_KEY_ADD_USE_CHANNEL_MENTIONS_PERMISSION, Migration: getAddUseMentionChannelsPermissionMigration},
{Key: model.MIGRATION_KEY_EMOJI_PERMISSIONS_SPLIT, Migration: a.getEmojisPermissionsSplitMigration},
{Key: model.MIGRATION_KEY_WEBHOOK_PERMISSIONS_SPLIT, Migration: a.getWebhooksPermissionsSplitMigration},
{Key: model.MIGRATION_KEY_LIST_JOIN_PUBLIC_PRIVATE_TEAMS, Migration: a.getListJoinPublicPrivateTeamsPermissionsMigration},
{Key: model.MIGRATION_KEY_REMOVE_PERMANENT_DELETE_USER, Migration: a.removePermanentDeleteUserMigration},
{Key: model.MIGRATION_KEY_ADD_BOT_PERMISSIONS, Migration: a.getAddBotPermissionsMigration},
{Key: model.MIGRATION_KEY_APPLY_CHANNEL_MANAGE_DELETE_TO_CHANNEL_USER, Migration: a.applyChannelManageDeleteToChannelUser},
{Key: model.MIGRATION_KEY_REMOVE_CHANNEL_MANAGE_DELETE_FROM_TEAM_USER, Migration: a.removeChannelManageDeleteFromTeamUser},
{Key: model.MIGRATION_KEY_VIEW_MEMBERS_NEW_PERMISSION, Migration: a.getViewMembersPermissionMigration},
{Key: model.MIGRATION_KEY_ADD_MANAGE_GUESTS_PERMISSIONS, Migration: a.getAddManageGuestsPermissionsMigration},
{Key: model.MIGRATION_KEY_CHANNEL_MODERATIONS_PERMISSIONS, Migration: a.channelModerationPermissionsMigration},
}
for _, migration := range PermissionsMigrations {
if err := a.doPermissionsMigration(migration.Key, migration.Migration()); err != nil {
migMap, err := migration.Migration()
if err != nil {
return err
}
if err := a.doPermissionsMigration(migration.Key, migMap); err != nil {
return err
}
}

Просмотреть файл

@@ -708,6 +708,7 @@ func TestCreatePost(t *testing.T) {
t.Run("Sets prop when post has mentions and user does not have USE_CHANNEL_MENTIONS", func(t *testing.T) {
th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
postWithNoMention := &model.Post{
ChannelId: th.BasicChannel.Id,
@@ -728,6 +729,7 @@ func TestCreatePost(t *testing.T) {
assert.Equal(t, rpost.GetProp(model.POST_PROPS_MENTION_HIGHLIGHT_DISABLED), true)
th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
})
})
}
@@ -798,6 +800,7 @@ func TestPatchPost(t *testing.T) {
t.Run("Sets prop when user does not have USE_CHANNEL_MENTIONS", func(t *testing.T) {
th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
th.RemovePermissionFromRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
patchWithNoMention := &model.PostPatch{Message: model.NewString("This patch still does not have a mention")}
rpost, err = th.App.PatchPost(rpost.Id, patchWithNoMention)
@@ -811,6 +814,7 @@ func TestPatchPost(t *testing.T) {
assert.Equal(t, rpost.GetProp(model.POST_PROPS_MENTION_HIGHLIGHT_DISABLED), true)
th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
th.AddPermissionToRole(model.PERMISSION_USE_CHANNEL_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
})
})
}

Просмотреть файл

@@ -9,6 +9,7 @@ import (
"strings"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/utils"
)
func (a *App) GetRole(id string) (*model.Role, *model.AppError) {
@@ -20,11 +21,62 @@ func (a *App) GetAllRoles() ([]*model.Role, *model.AppError) {
}
func (a *App) GetRoleByName(name string) (*model.Role, *model.AppError) {
return a.Srv().Store.Role().GetByName(name)
role, err := a.Srv().Store.Role().GetByName(name)
if err != nil {
return nil, err
}
err = a.mergeChannelHigherScopedPermissions([]*model.Role{role})
if err != nil {
return nil, err
}
return role, nil
}
func (a *App) GetRolesByNames(names []string) ([]*model.Role, *model.AppError) {
return a.Srv().Store.Role().GetByNames(names)
roles, err := a.Srv().Store.Role().GetByNames(names)
if err != nil {
return nil, err
}
err = a.mergeChannelHigherScopedPermissions(roles)
if err != nil {
return nil, err
}
return roles, nil
}
// mergeChannelHigherScopedPermissions updates the permissions based on the role type, whether the permission is
// moderated, and the value of the permission on the higher-scoped scheme.
func (a *App) mergeChannelHigherScopedPermissions(roles []*model.Role) *model.AppError {
var higherScopeNamesToQuery []string
for _, role := range roles {
if role.SchemeManaged {
higherScopeNamesToQuery = append(higherScopeNamesToQuery, role.Name)
}
}
if len(higherScopeNamesToQuery) == 0 {
return nil
}
higherScopedPermissionsMap, err := a.Srv().Store.Role().ChannelHigherScopedPermissions(higherScopeNamesToQuery)
if err != nil {
return err
}
for _, role := range roles {
if role.SchemeManaged {
if higherScopedPermissions, ok := higherScopedPermissionsMap[role.Name]; ok {
role.MergeChannelHigherScopedPermissions(higherScopedPermissions)
}
}
}
return nil
}
func (a *App) PatchRole(role *model.Role, patch *model.RolePatch) (*model.Role, *model.AppError) {
@@ -59,10 +111,47 @@ func (a *App) UpdateRole(role *model.Role) (*model.Role, *model.AppError) {
if err != nil {
return nil, err
}
a.sendUpdatedRoleEvent(savedRole)
builtInChannelRoles := []string{
model.CHANNEL_GUEST_ROLE_ID,
model.CHANNEL_USER_ROLE_ID,
model.CHANNEL_ADMIN_ROLE_ID,
}
builtInRolesMinusChannelRoles := utils.RemoveStringsFromSlice(model.BuiltInSchemeManagedRoleIDs, builtInChannelRoles...)
if utils.StringInSlice(savedRole.Name, builtInRolesMinusChannelRoles) {
return savedRole, nil
}
var roleRetrievalFunc func() ([]*model.Role, *model.AppError)
if utils.StringInSlice(savedRole.Name, builtInChannelRoles) {
roleRetrievalFunc = func() ([]*model.Role, *model.AppError) {
return a.Srv().Store.Role().AllChannelSchemeRoles()
}
} else {
roleRetrievalFunc = func() ([]*model.Role, *model.AppError) {
return a.Srv().Store.Role().ChannelRolesUnderTeamRole(savedRole.Name)
}
}
impactedRoles, err := roleRetrievalFunc()
if err != nil {
return nil, err
}
impactedRoles = append(impactedRoles, role)
err = a.mergeChannelHigherScopedPermissions(impactedRoles)
if err != nil {
return nil, err
}
for _, ir := range impactedRoles {
a.sendUpdatedRoleEvent(ir)
}
return savedRole, nil
}
func (a *App) CheckRolesExist(roleNames []string) *model.AppError {

217
app/role_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,217 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"encoding/csv"
"io/ioutil"
"os"
"strconv"
"strings"
"testing"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/utils"
"github.com/stretchr/testify/require"
)
type permissionInheritanceTestData struct {
channelRole *model.Role
permission *model.Permission
shouldHavePermission bool
channel *model.Channel
higherScopedRole *model.Role
truthTableRow []string
}
func TestGetRolesByNames(t *testing.T) {
testPermissionInheritance(t, func(t *testing.T, th *TestHelper, testData permissionInheritanceTestData) {
actualRoles, err := th.App.GetRolesByNames([]string{testData.channelRole.Name})
require.Nil(t, err)
require.Len(t, actualRoles, 1)
actualRole := actualRoles[0]
require.NotNil(t, actualRole)
require.Equal(t, testData.channelRole.Name, actualRole.Name)
require.Equal(t, testData.shouldHavePermission, utils.StringInSlice(testData.permission.Id, actualRole.Permissions))
})
}
func TestGetRoleByName(t *testing.T) {
testPermissionInheritance(t, func(t *testing.T, th *TestHelper, testData permissionInheritanceTestData) {
actualRole, err := th.App.GetRoleByName(testData.channelRole.Name)
require.Nil(t, err)
require.NotNil(t, actualRole)
require.Equal(t, testData.channelRole.Name, actualRole.Name)
require.Equal(t, testData.shouldHavePermission, utils.StringInSlice(testData.permission.Id, actualRole.Permissions), "row: %+v", testData.truthTableRow)
})
}
// testPermissionInheritance tests 48 combinations of scheme, permission, role data.
func testPermissionInheritance(t *testing.T, testCallback func(t *testing.T, th *TestHelper, testData permissionInheritanceTestData)) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.App.SetLicense(model.NewTestLicense(""))
th.App.SetPhase2PermissionsMigrationStatus(true)
permissionsDefault := []string{
model.PERMISSION_MANAGE_CHANNEL_ROLES.Id,
model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
}
// Defer resetting the system scheme permissions
systemSchemeRoles, err := th.App.GetRolesByNames([]string{
model.CHANNEL_GUEST_ROLE_ID,
model.CHANNEL_USER_ROLE_ID,
model.CHANNEL_ADMIN_ROLE_ID,
})
require.Nil(t, err)
require.Len(t, systemSchemeRoles, 3)
// defer resetting the system role permissions
for _, systemRole := range systemSchemeRoles {
defer th.App.PatchRole(systemRole, &model.RolePatch{
Permissions: &systemRole.Permissions,
})
}
// Make a channel scheme, clear its permissions
channelScheme, err := th.App.CreateScheme(&model.Scheme{
Name: model.NewId(),
DisplayName: model.NewId(),
Scope: model.SCHEME_SCOPE_CHANNEL,
})
require.Nil(t, err)
defer th.App.DeleteScheme(channelScheme.Id)
team := th.CreateTeam()
defer th.App.PermanentDeleteTeamId(team.Id)
// Make a channel
channel := th.CreateChannel(team)
defer th.App.PermanentDeleteChannel(channel)
// Set the channel scheme
channel.SchemeId = &channelScheme.Id
channel, err = th.App.UpdateChannelScheme(channel)
require.Nil(t, err)
// Get the truth table from CSV
file, e := os.Open("tests/channel-role-has-permission.csv")
require.Nil(t, e)
defer file.Close()
b, e := ioutil.ReadAll(file)
require.Nil(t, e)
r := csv.NewReader(strings.NewReader(string(b)))
records, e := r.ReadAll()
require.Nil(t, e)
test := func(higherScopedGuest, higherScopedUser, higherScopedAdmin string) {
for _, roleNameUnderTest := range []string{higherScopedGuest, higherScopedUser, higherScopedAdmin} {
for i, row := range records {
// skip csv header
if i == 0 {
continue
}
higherSchemeHasPermission, e := strconv.ParseBool(row[0])
require.Nil(t, e)
permissionIsModerated, e := strconv.ParseBool(row[1])
require.Nil(t, e)
channelSchemeHasPermission, e := strconv.ParseBool(row[2])
require.Nil(t, e)
channelRoleIsChannelAdmin, e := strconv.ParseBool(row[3])
require.Nil(t, e)
shouldHavePermission, e := strconv.ParseBool(row[4])
require.Nil(t, e)
// skip some invalid combinations because of the outer loop iterating all 3 channel roles
if (channelRoleIsChannelAdmin && roleNameUnderTest != higherScopedAdmin) || (!channelRoleIsChannelAdmin && roleNameUnderTest == higherScopedAdmin) {
continue
}
// select the permission to test (moderated or non-moderated)
var permission *model.Permission
if permissionIsModerated {
permission = model.PERMISSION_CREATE_POST // moderated
} else {
permission = model.PERMISSION_READ_CHANNEL // non-moderated
}
// add or remove the permission from the higher-scoped scheme
higherScopedRole, testErr := th.App.GetRoleByName(roleNameUnderTest)
require.Nil(t, testErr)
var higherScopedPermissions []string
if higherSchemeHasPermission {
higherScopedPermissions = []string{permission.Id}
} else {
higherScopedPermissions = permissionsDefault
}
higherScopedRole, testErr = th.App.PatchRole(higherScopedRole, &model.RolePatch{Permissions: &higherScopedPermissions})
require.Nil(t, testErr)
// get channel role
var channelRoleName string
switch roleNameUnderTest {
case higherScopedGuest:
channelRoleName = channelScheme.DefaultChannelGuestRole
case higherScopedUser:
channelRoleName = channelScheme.DefaultChannelUserRole
case higherScopedAdmin:
channelRoleName = channelScheme.DefaultChannelAdminRole
}
channelRole, testErr := th.App.GetRoleByName(channelRoleName)
require.Nil(t, testErr)
// add or remove the permission from the channel scheme
var channelSchemePermissions []string
if channelSchemeHasPermission {
channelSchemePermissions = []string{permission.Id}
} else {
channelSchemePermissions = permissionsDefault
}
channelRole, testErr = th.App.PatchRole(channelRole, &model.RolePatch{Permissions: &channelSchemePermissions})
require.Nil(t, testErr)
testCallback(t, th, permissionInheritanceTestData{
channelRole: channelRole,
permission: permission,
shouldHavePermission: shouldHavePermission,
channel: channel,
higherScopedRole: higherScopedRole,
truthTableRow: row,
})
}
}
}
// test 24 combinations where the higher-scoped scheme is the SYSTEM scheme
test(model.CHANNEL_GUEST_ROLE_ID, model.CHANNEL_USER_ROLE_ID, model.CHANNEL_ADMIN_ROLE_ID)
// create a team scheme
teamScheme, err := th.App.CreateScheme(&model.Scheme{
Name: model.NewId(),
DisplayName: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
})
require.Nil(t, err)
defer th.App.DeleteScheme(teamScheme.Id)
// assign the scheme to the team
team.SchemeId = &teamScheme.Id
team, err = th.App.UpdateTeamScheme(team)
require.Nil(t, err)
// test 24 combinations where the higher-scoped scheme is a TEAM scheme
test(teamScheme.DefaultChannelGuestRole, teamScheme.DefaultChannelUserRole, teamScheme.DefaultChannelAdminRole)
}

Просмотреть файл

@@ -139,10 +139,10 @@ func (a *App) IsPhase2MigrationCompleted() *model.AppError {
return nil
}
func (a *App) SchemesIterator(batchSize int) func() []*model.Scheme {
func (a *App) SchemesIterator(scope string, batchSize int) func() []*model.Scheme {
offset := 0
return func() []*model.Scheme {
schemes, err := a.Srv().Store.Scheme().GetAllPage("", offset, batchSize)
schemes, err := a.Srv().Store.Scheme().GetAllPage(scope, offset, batchSize)
if err != nil {
return []*model.Scheme{}
}