diff --git a/app/components/autocomplete/at_mention/index.js b/app/components/autocomplete/at_mention/index.js index a8d2b5969..efed90ceb 100644 --- a/app/components/autocomplete/at_mention/index.js +++ b/app/components/autocomplete/at_mention/index.js @@ -34,6 +34,7 @@ function mapStateToProps(state, ownProps) { { channel: currentChannelId, permission: Permissions.USE_CHANNEL_MENTIONS, + default: true, }, ); } diff --git a/app/components/post_textbox/index.js b/app/components/post_textbox/index.js index 9e544bd0e..7a9e8b24e 100644 --- a/app/components/post_textbox/index.js +++ b/app/components/post_textbox/index.js @@ -60,6 +60,7 @@ export function mapStateToProps(state, ownProps) { channel: currentChannel.id, team: currentChannel.team_id, permission: Permissions.CREATE_POST, + default: true, }, ); @@ -68,6 +69,7 @@ export function mapStateToProps(state, ownProps) { { channel: currentChannel.id, permission: Permissions.USE_CHANNEL_MENTIONS, + default: true, }, ); } diff --git a/app/components/post_textbox/index.test.js b/app/components/post_textbox/index.test.js index 74ba5174c..ae6b0f0d8 100644 --- a/app/components/post_textbox/index.test.js +++ b/app/components/post_textbox/index.test.js @@ -71,11 +71,13 @@ describe('mapStateToProps', () => { channel: undefined, team: undefined, permission: Permissions.CREATE_POST, + default: true, }); expect(roleSelectors.haveIChannelPermission).not.toHaveBeenCalledWith(state, { channel: undefined, permission: Permissions.USE_CHANNEL_MENTIONS, + default: true, }); }); @@ -90,11 +92,13 @@ describe('mapStateToProps', () => { channel: undefined, team: undefined, permission: Permissions.CREATE_POST, + default: true, }); expect(roleSelectors.haveIChannelPermission).toHaveBeenCalledWith(state, { channel: undefined, permission: Permissions.USE_CHANNEL_MENTIONS, + default: true, }); }); }); \ No newline at end of file diff --git a/app/components/reactions/index.js b/app/components/reactions/index.js index 7e2882f6e..a81d4e85b 100644 --- a/app/components/reactions/index.js +++ b/app/components/reactions/index.js @@ -43,6 +43,7 @@ function makeMapStateToProps() { team: teamId, channel: channelId, permission: Permissions.ADD_REACTION, + default: true, }); if (reactions) { @@ -54,6 +55,7 @@ function makeMapStateToProps() { team: teamId, channel: channelId, permission: Permissions.REMOVE_REACTION, + default: true, }); } diff --git a/app/mm-redux/selectors/entities/roles.test.js b/app/mm-redux/selectors/entities/roles.test.js index 88d00c75c..309e661ac 100644 --- a/app/mm-redux/selectors/entities/roles.test.js +++ b/app/mm-redux/selectors/entities/roles.test.js @@ -13,10 +13,12 @@ describe('Selectors.Roles', () => { const team1 = TestHelper.fakeTeamWithId(); const team2 = TestHelper.fakeTeamWithId(); const team3 = TestHelper.fakeTeamWithId(); + const team4 = TestHelper.fakeTeamWithId(); const myTeamMembers = {}; myTeamMembers[team1.id] = {roles: 'test_team1_role1 test_team1_role2'}; myTeamMembers[team2.id] = {roles: 'test_team2_role1 test_team2_role2'}; myTeamMembers[team3.id] = {}; + myTeamMembers[team4.id] = {roles: 'test_team4_role_not_found'}; const channel1 = TestHelper.fakeChannelWithId(team1.id); channel1.display_name = 'Channel Name'; @@ -50,6 +52,7 @@ describe('Selectors.Roles', () => { const channel11 = TestHelper.fakeChannelWithId(team1.id); channel11.type = General.PRIVATE_CHANNEL; const channel12 = TestHelper.fakeChannelWithId(team1.id); + const channel13 = TestHelper.fakeChannelWithId(team1.id); const channels = {}; channels[channel1.id] = channel1; @@ -64,6 +67,7 @@ describe('Selectors.Roles', () => { channels[channel10.id] = channel10; channels[channel11.id] = channel11; channels[channel12.id] = channel12; + channels[channel13.id] = channel13; const channelsInTeam = {}; channelsInTeam[team1.id] = [channel1.id, channel2.id, channel5.id, channel6.id, channel8.id, channel10.id, channel11.id]; @@ -87,6 +91,7 @@ describe('Selectors.Roles', () => { myChannelMembers[channel10.id] = {roles: 'test_channel_c_role1 test_channel_c_role2'}; myChannelMembers[channel11.id] = {roles: 'test_channel_c_role1 test_channel_c_role2'}; myChannelMembers[channel12.id] = {}; + myChannelMembers[channel13.id] = {roles: 'test_channel_not_found_role'}; const roles = { test_team1_role1: {permissions: ['team1_role1']}, test_team2_role1: {permissions: ['team2_role1']}, @@ -124,6 +129,7 @@ describe('Selectors.Roles', () => { const teamsRoles = {}; teamsRoles[team1.id] = new Set(['test_team1_role1', 'test_team1_role2']); teamsRoles[team2.id] = new Set(['test_team2_role1', 'test_team2_role2']); + teamsRoles[team4.id] = new Set(['test_team4_role_not_found']); const channelsRoles = {}; channelsRoles[channel1.id] = new Set(['test_channel_a_role1', 'test_channel_a_role2']); channelsRoles[channel2.id] = new Set(['test_channel_a_role1', 'test_channel_a_role2']); @@ -135,6 +141,7 @@ describe('Selectors.Roles', () => { channelsRoles[channel9.id] = new Set(['test_channel_b_role1', 'test_channel_b_role2']); channelsRoles[channel10.id] = new Set(['test_channel_c_role1', 'test_channel_c_role2']); channelsRoles[channel11.id] = new Set(['test_channel_c_role1', 'test_channel_c_role2']); + channelsRoles[channel13.id] = new Set(['test_channel_not_found_role']); const myRoles = { system: new Set(['test_user_role', 'test_user_role2']), team: teamsRoles, @@ -172,10 +179,18 @@ describe('Selectors.Roles', () => { assert.equal(Selectors.haveISystemPermission(testState, {permission: 'invalid_permission'}), false); }); - it('should return my team permission on getMyTeamPermissions', () => { - assert.deepEqual(Selectors.getMyTeamPermissions(testState, {team: team1.id}), new Set([ - 'user_role2', 'team1_role1', - ])); + it('should return my team permissions on getMyTeamPermissions', () => { + const {permissions, roleFound} = Selectors.getMyTeamPermissions(testState, {team: team1.id}); + const expectedPermissions = new Set(['user_role2', 'team1_role1']); + assert.deepEqual(permissions, expectedPermissions); + assert.equal(roleFound, true); + }); + + it('should return system permissions on getMyTeamPermissions when team not found', () => { + const {permissions, roleFound} = Selectors.getMyTeamPermissions(testState, {team: team4.id}); + const expectedPermissions = new Set(['user_role2']); + assert.deepEqual(permissions, expectedPermissions); + assert.equal(roleFound, false); }); it('should return if i have a team permission on haveITeamPermission', () => { @@ -185,10 +200,17 @@ describe('Selectors.Roles', () => { assert.equal(Selectors.haveITeamPermission(testState, {team: team1.id, permission: 'invalid_permission'}), false); }); + it('should return default if role not found in state on haveITeamPermission for team scoped permission', () => { + assert.equal(Selectors.haveITeamPermission(testState, {team: team4.id, permission: 'any_permission', default: true}), true); + assert.equal(Selectors.haveITeamPermission(testState, {team: team4.id, permission: 'any_permission', default: false}), false); + assert.equal(Selectors.haveITeamPermission(testState, {team: team1.id, permission: 'user_role2'}), true); + }); + it('should return my team permission on getMyCurrentTeamPermissions', () => { - assert.deepEqual(Selectors.getMyCurrentTeamPermissions(testState), new Set([ - 'user_role2', 'team1_role1', - ])); + const {permissions, roleFound} = Selectors.getMyCurrentTeamPermissions(testState); + const expectedPermissions = new Set(['user_role2', 'team1_role1']); + assert.deepEqual(permissions, expectedPermissions); + assert.equal(roleFound, true); }); it('should return if i have a team permission on haveICurrentTeamPermission', () => { @@ -199,9 +221,24 @@ describe('Selectors.Roles', () => { }); it('should return my channel permission on getMyChannelPermissions', () => { - assert.deepEqual(Selectors.getMyChannelPermissions(testState, {team: team1.id, channel: channel1.id}), new Set([ - 'user_role2', 'team1_role1', 'channel_a_role1', 'channel_a_role2', - ])); + const {permissions, roleFound} = Selectors.getMyChannelPermissions(testState, {team: team1.id, channel: channel1.id}); + const expectedPermissions = new Set(['user_role2', 'team1_role1', 'channel_a_role1', 'channel_a_role2']); + assert.deepEqual(permissions, expectedPermissions); + assert.equal(roleFound, true); + }); + + it('should only return my team permissions on getMyChannelPermissions when role not found', () => { + const {permissions, roleFound} = Selectors.getMyChannelPermissions(testState, {team: team1.id, channel: channel13.id}); + const expectedPermissions = new Set(['user_role2', 'team1_role1']); + assert.deepEqual(permissions, expectedPermissions); + assert.equal(roleFound, false); + }); + + it('should return my channel permission on getMyChannelPermissions when channel roles not found', () => { + const {permissions, roleFound} = Selectors.getMyChannelPermissions(testState, {team: team1.id, channel: channel1.id}); + const expectedPermissions = new Set(['user_role2', 'team1_role1', 'channel_a_role1', 'channel_a_role2']); + assert.deepEqual(permissions, expectedPermissions); + assert.equal(roleFound, true); }); it('should return if i have a channel permission on haveIChannelPermission', () => { @@ -212,10 +249,11 @@ describe('Selectors.Roles', () => { assert.equal(Selectors.haveIChannelPermission(testState, {team: team1.id, channel: channel1.id, permission: 'channel_b_role1'}), false); }); - it('should return my current channel permission on getMyCurrentChannelPermissions', () => { - assert.deepEqual(Selectors.getMyCurrentChannelPermissions(testState), new Set([ - 'user_role2', 'team1_role1', 'channel_a_role1', 'channel_a_role2', - ])); + it('should return default if role not found in state on haveIChannelPermission for channel scoped permission', () => { + assert.equal(Selectors.haveIChannelPermission(testState, {team: team1.id, channel: channel13.id, permission: 'any_permission', default: true}), true); + assert.equal(Selectors.haveIChannelPermission(testState, {team: team1.id, channel: channel13.id, permission: 'any_permission', default: false}), false); + assert.equal(Selectors.haveIChannelPermission(testState, {team: team1.id, channel: channel13.id, permission: 'user_role2'}), true); + assert.equal(Selectors.haveIChannelPermission(testState, {team: team1.id, channel: channel13.id, permission: 'team1_role1'}), true); }); it('should return if i have a channel permission on haveICurrentChannelPermission', () => { diff --git a/app/mm-redux/selectors/entities/roles.ts b/app/mm-redux/selectors/entities/roles.ts index 2fa1ea367..b0788dfb6 100644 --- a/app/mm-redux/selectors/entities/roles.ts +++ b/app/mm-redux/selectors/entities/roles.ts @@ -1,15 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import * as reselect from 'reselect'; -import {getCurrentUser, getCurrentChannelId} from '@mm-redux/selectors/entities/common'; +import {getCurrentChannelId} from '@mm-redux/selectors/entities/common'; import {getTeamMemberships, getCurrentTeamId} from './teams'; import * as types from '@mm-redux/types'; import {getMySystemPermissions, getMySystemRoles, getRoles, PermissionsOptions} from '@mm-redux/selectors/entities/roles_helpers'; import {GlobalState} from '@mm-redux/types/store'; import {Dictionary} from '@mm-redux/types/utilities'; import {Role} from '@mm-redux/types/roles'; -import {Channel} from '@mm-redux/types/channels'; -import {Team} from '@mm-redux/types/teams'; export {getMySystemPermissions, getMySystemRoles, getRoles}; @@ -73,21 +71,23 @@ export const getMyCurrentTeamPermissions = reselect.createSelector( getMySystemPermissions, getCurrentTeamId, (myTeamRoles, roles, systemPermissions, teamId) => { - const permissions = new Set(); + const permissions = new Set(); + let roleFound = false; if (myTeamRoles[teamId]) { for (const roleName of myTeamRoles[teamId]) { if (roles[roleName]) { for (const permission of roles[roleName].permissions) { permissions.add(permission); } + roleFound = true; } } } for (const permission of systemPermissions) { permissions.add(permission); } - return permissions; - } + return {permissions, roleFound}; + }, ); export const getMyCurrentChannelPermissions = reselect.createSelector( @@ -95,22 +95,24 @@ export const getMyCurrentChannelPermissions = reselect.createSelector( getRoles, getMyCurrentTeamPermissions, getCurrentChannelId, - (myChannelRoles, roles, teamPermissions, channelId) => { - const permissions = new Set(); + (myChannelRoles, roles, {permissions: teamPermissions, roleFound: teamRoleFound}, channelId) => { + const permissions = new Set(); + let roleFound = false; if (myChannelRoles[channelId]) { for (const roleName of myChannelRoles[channelId]) { if (roles[roleName]) { for (const permission of roles[roleName].permissions) { permissions.add(permission); } + roleFound = true && teamRoleFound; } } } for (const permission of teamPermissions) { permissions.add(permission); } - return permissions; - } + return {permissions, roleFound}; + }, ); export const getMyTeamPermissions = reselect.createSelector( @@ -119,21 +121,23 @@ export const getMyTeamPermissions = reselect.createSelector( getMySystemPermissions, (state: GlobalState, options: PermissionsOptions) => options.team, (myTeamRoles, roles, systemPermissions, teamId) => { - const permissions = new Set(); + const permissions = new Set(); + let roleFound = false; if (myTeamRoles[teamId!]) { for (const roleName of myTeamRoles[teamId!]) { if (roles[roleName]) { for (const permission of roles[roleName].permissions) { permissions.add(permission); } + roleFound = true; } } } for (const permission of systemPermissions) { permissions.add(permission); } - return permissions; - } + return {permissions, roleFound}; + }, ); export const getMyChannelPermissions = reselect.createSelector( @@ -141,22 +145,24 @@ export const getMyChannelPermissions = reselect.createSelector( getRoles, getMyTeamPermissions, (state, options: PermissionsOptions) => options.channel, - (myChannelRoles, roles, teamPermissions, channelId) => { - const permissions = new Set(); + (myChannelRoles, roles, {permissions: teamPermissions, roleFound: teamRoleFound}, channelId) => { + const permissions = new Set(); + let roleFound = false; if (myChannelRoles[channelId!]) { for (const roleName of myChannelRoles[channelId!]) { if (roles[roleName]) { for (const permission of roles[roleName].permissions) { permissions.add(permission); } + roleFound = true && teamRoleFound; } } } for (const permission of teamPermissions) { permissions.add(permission); } - return permissions; - } + return {permissions, roleFound}; + }, ); export const haveISystemPermission = reselect.createSelector( @@ -169,32 +175,48 @@ export const haveISystemPermission = reselect.createSelector( export const haveITeamPermission = reselect.createSelector( getMyTeamPermissions, - (state, options) => options.permission, - (permissions, permission) => { - return permissions.has(permission); - } + (state, options) => options, + ({permissions, roleFound}, options) => { + const hasPermission = permissions.has(options.permission); + if (roleFound) { + return hasPermission; + } + return options.default === true || hasPermission; + }, ); export const haveIChannelPermission = reselect.createSelector( getMyChannelPermissions, - (state, options) => options.permission, - (permissions, permission) => { - return permissions.has(permission); - } + (state, options) => options, + ({permissions, roleFound}, options) => { + const hasPermission = permissions.has(options.permission); + if (roleFound) { + return hasPermission; + } + return options.default === true || hasPermission; + }, ); export const haveICurrentTeamPermission = reselect.createSelector( getMyCurrentTeamPermissions, - (state: GlobalState, options: PermissionsOptions) => options.permission, - (permissions, permission) => { - return permissions.has(permission); - } + (state: GlobalState, options: PermissionsOptions) => options, + ({permissions, roleFound}, options) => { + const hasPermission = permissions.has(options.permission); + if (roleFound) { + return hasPermission; + } + return options.default === true || hasPermission; + }, ); export const haveICurrentChannelPermission = reselect.createSelector( getMyCurrentChannelPermissions, - (state: GlobalState, options: PermissionsOptions) => options.permission, - (permissions, permission) => { - return permissions.has(permission); - } + (state: GlobalState, options: PermissionsOptions) => options, + ({permissions, roleFound}, options) => { + const hasPermission = permissions.has(options.permission); + if (roleFound) { + return hasPermission; + } + return options.default === true || hasPermission; + }, ); diff --git a/app/mm-redux/selectors/entities/roles_helpers.ts b/app/mm-redux/selectors/entities/roles_helpers.ts index 81a9b85fd..4f4ffcfbc 100644 --- a/app/mm-redux/selectors/entities/roles_helpers.ts +++ b/app/mm-redux/selectors/entities/roles_helpers.ts @@ -9,6 +9,7 @@ export type PermissionsOptions = { channel?: string; team?: string; permission: string; + default?: boolean; }; export function getRoles(state: GlobalState) { diff --git a/app/screens/post_options/index.js b/app/screens/post_options/index.js index d09f8eabb..2a01a2fc0 100644 --- a/app/screens/post_options/index.js +++ b/app/screens/post_options/index.js @@ -65,6 +65,7 @@ export function makeMapStateToProps() { channel: post.channel_id, team: channel.team_id, permission: Permissions.CREATE_POST, + default: true, }, ); } @@ -74,6 +75,7 @@ export function makeMapStateToProps() { team: currentTeamId, channel: post.channel_id, permission: Permissions.ADD_REACTION, + default: true, }); } diff --git a/app/screens/post_options/index.test.js b/app/screens/post_options/index.test.js index 2f0798bca..123f04940 100644 --- a/app/screens/post_options/index.test.js +++ b/app/screens/post_options/index.test.js @@ -156,6 +156,7 @@ describe('makeMapStateToProps', () => { channel: undefined, team: undefined, permission: Permissions.CREATE_POST, + default: true, }); }); @@ -176,6 +177,7 @@ describe('makeMapStateToProps', () => { channel: undefined, team: undefined, permission: Permissions.CREATE_POST, + default: true, }); }); });