Skip to content

Commit

Permalink
EPMRPP-92607 || User with different roles across different Organizati… (
Browse files Browse the repository at this point in the history
#3916)

* EPMRPP-92607 || User with different roles across different Organizations see pages in the same mode

* EPMRPP-92607 || Code Review fix - 1

* EPMRPP-92607 || Code Review fix - 2

* EPMRPP-92607 || Code Review fix - 3
  • Loading branch information
BlazarQSO authored Jul 15, 2024
1 parent ff082b7 commit edf5274
Show file tree
Hide file tree
Showing 48 changed files with 151 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import PlusIcon from 'common/img/plus-button-inline.svg';
import { canUpdateSettings } from 'common/utils/permissions';
import { COMMON_LOCALE_KEYS } from 'common/constants/localization';
import { userRolesType } from 'common/constants/projectRoles';
import { userRolesSelector } from 'controllers/user';
import { userRolesSelector } from 'controllers/pages';
import {
removePluginAction,
addIntegrationAction,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import {
urlOrganizationAndProjectSelector,
querySelector,
PROJECT_SETTINGS_TAB_PAGE,
userRolesSelector,
} from 'controllers/pages';
import { omit } from 'common/utils/omit';
import { userRolesSelector } from 'controllers/user';
import { projectKeySelector } from 'controllers/project';
import { canUpdateSettings } from 'common/utils/permissions';
import {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { userRolesSelector } from 'controllers/user';
import { userRolesSelector } from 'controllers/pages';
import { canDeleteUser } from 'common/utils/permissions';

export const validateDeleteUser = (user, users, state) => {
Expand Down
2 changes: 1 addition & 1 deletion app/src/controllers/launch/actionValidators.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { userRolesSelector } from 'controllers/user';
import { userRolesSelector } from 'controllers/pages';
import { IN_PROGRESS } from 'common/constants/launchStatuses';
import {
canMergeLaunches,
Expand Down
3 changes: 3 additions & 0 deletions app/src/controllers/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export {
urlProjectSlugSelector,
urlOrganizationSlugSelector,
urlOrganizationAndProjectSelector,
userRolesSelector,
activeProjectRoleSelector,
userAssignedSelector,
} from './selectors';
export { updatePagePropertiesAction, clearPageStateAction } from './actionCreators';

Expand Down
79 changes: 78 additions & 1 deletion app/src/controllers/pages/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,16 @@ import { extractNamespacedQuery } from 'common/utils/routingUtils';
import { DEFAULT_PAGINATION, SIZE_KEY, PAGE_KEY } from 'controllers/pagination/constants';
import { SORTING_KEY } from 'controllers/sorting/constants';
import { getStorageItem } from 'common/utils/storageUtils';
import { activeProjectSelector, userIdSelector } from 'controllers/user';
import {
activeProjectSelector,
assignedOrganizationsSelector,
assignedProjectsSelector,
userAccountRoleSelector,
userIdSelector,
} from 'controllers/user';
import { ALL } from 'common/constants/reservedFilterIds';
import { ADMINISTRATOR } from 'common/constants/accountRoles';
import { MANAGER } from 'common/constants/projectRoles';
import { pageNames, NO_PAGE } from './constants';
import { stringToArray } from './utils';

Expand Down Expand Up @@ -159,3 +167,72 @@ export const urlOrganizationAndProjectSelector = createSelector(
return activeProject;
},
);

// TODO: User role selectors are stored here due to circular dependency
export const activeProjectRoleSelector = createSelector(
urlProjectSlugSelector,
assignedProjectsSelector,
(projectSlug, assignedProjects) => {
const assignedProject = assignedProjects[projectSlug];

return assignedProject?.projectRole;
},
);

const activeOrganizationRoleSelector = createSelector(
urlOrganizationSlugSelector,
assignedOrganizationsSelector,
(organizationSlug, assignedOrganizations) => {
const assignedOrganization = assignedOrganizations[organizationSlug];

return assignedOrganization?.organizationRole;
},
);

export const userRolesSelector = createSelector(
userAccountRoleSelector,
activeOrganizationRoleSelector,
activeProjectRoleSelector,
(userRole, organizationRole, projectRole) => ({
userRole,
organizationRole,
projectRole,
}),
);

export const userAssignedSelector = (projectSlug, organizationSlug) => (state) => {
const assignedOrganizations = assignedOrganizationsSelector(state);
const assignedProjects = assignedProjectsSelector(state);
const { userRole, organizationRole } = userRolesSelector(state);

const isAdmin = userRole === ADMINISTRATOR;
const isManager = organizationRole === MANAGER;
let isAssignedToTargetOrganization = false;

if (organizationSlug) {
isAssignedToTargetOrganization = organizationSlug in assignedOrganizations;
} else {
const organizationId = assignedProjects[projectSlug]?.organizationId || '';
isAssignedToTargetOrganization = Object.keys(assignedOrganizations).some(
(key) => assignedOrganizations[key]?.organizationId === organizationId,
);
}

const isAssignedToTargetProject =
projectSlug && projectSlug in assignedProjects && isAssignedToTargetOrganization;

const assignmentNotRequired = isAdmin || (isManager && isAssignedToTargetOrganization);

const hasPermission = isAssignedToTargetProject || assignmentNotRequired;

const assignedProjectKey = assignedProjects?.[projectSlug]?.projectKey;

return {
isAdmin,
hasPermission,
assignedProjectKey,
assignmentNotRequired,
isAssignedToTargetProject,
isAssignedToTargetOrganization,
};
};
7 changes: 2 additions & 5 deletions app/src/controllers/plugins/uiExtensions/createImportProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,7 @@ import {
CANCELLED,
STOPPED,
} from 'common/constants/testStatuses';
import {
activeProjectRoleSelector,
isAdminSelector,
activeProjectSelector,
} from 'controllers/user';
import { isAdminSelector, activeProjectSelector } from 'controllers/user';
import {
projectMembersSelector,
projectInfoSelector,
Expand All @@ -78,6 +74,7 @@ import {
urlProjectSlugSelector,
querySelector,
payloadSelector,
activeProjectRoleSelector,
} from 'controllers/pages';
import { attributesArray, isNotEmptyArray } from 'common/utils/validation/validate';
import {
Expand Down
2 changes: 1 addition & 1 deletion app/src/controllers/testItem/actionValidators.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { userRolesSelector } from 'controllers/user';
import { userRolesSelector } from 'controllers/pages';
import { IN_PROGRESS } from 'common/constants/launchStatuses';
import { canDeleteTestItem } from 'common/utils/permissions';
import { launchSelector } from './selectors';
Expand Down
4 changes: 0 additions & 4 deletions app/src/controllers/user/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,12 @@ export {
startTimeFormatSelector,
isAdminSelector,
assignedProjectsSelector,
activeProjectRoleSelector,
userAccountRoleSelector,
activeOrganizationRoleSelector,
userRolesSelector,
photoTimeStampSelector,
apiKeysSelector,
photoIdSelector,
availableProjectsSelector,
activeProjectKeySelector,
assignedOrganizationsSelector,
createUserAssignedSelector,
} from './selectors';
export { userSagas } from './sagas';
6 changes: 3 additions & 3 deletions app/src/controllers/user/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { URLS } from 'common/urls';
import { showNotification, NOTIFICATION_TYPES } from 'controllers/notification';
import { PROJECT_MANAGER } from 'common/constants/projectRoles';
import { getStorageItem, setStorageItem } from 'common/utils/storageUtils';
import { urlOrganizationAndProjectSelector } from 'controllers/pages';
import { userAssignedSelector, urlOrganizationAndProjectSelector } from 'controllers/pages';
import { getLogTimeFormatFromStorage } from 'controllers/log/storageUtils';
import { setActiveOrganizationAction } from 'controllers/organizations/organization/actionCreators';
import {
Expand All @@ -46,7 +46,7 @@ import {
FETCH_USER,
DELETE_USER_ACCOUNT,
} from './constants';
import { createUserAssignedSelector, userIdSelector, userInfoSelector } from './selectors';
import { userIdSelector, userInfoSelector } from './selectors';

function* assignToProject({ payload: project }) {
const userId = yield select(userIdSelector);
Expand Down Expand Up @@ -139,7 +139,7 @@ function* fetchUserWorker() {
targetActiveProject || {};

const { assignmentNotRequired, isAssignedToTargetProject } = yield select(
createUserAssignedSelector(targetProjectSlug, targetOrganizationSlug),
userAssignedSelector(targetProjectSlug, targetOrganizationSlug),
);

const defaultProject = Object.keys(assignedProjects)[0];
Expand Down
69 changes: 0 additions & 69 deletions app/src/controllers/user/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import { ADMINISTRATOR } from 'common/constants/accountRoles';
import { createSelector } from 'reselect';
import { MANAGER } from 'common/constants/projectRoles';
import { START_TIME_FORMAT_ABSOLUTE, START_TIME_FORMAT_RELATIVE } from './constants';

const userSelector = (state) => state.user || {};
Expand All @@ -38,34 +37,6 @@ export const assignedProjectsSelector = (state) => userInfoSelector(state).assig
export const assignedOrganizationsSelector = (state) =>
userInfoSelector(state).assignedOrganizations || {};
export const userAccountRoleSelector = (state) => userInfoSelector(state).userRole || '';
export const activeProjectRoleSelector = createSelector(
activeProjectSelector,
assignedProjectsSelector,
(activeProject, assignedProjects) => {
const { projectSlug } = activeProject;
const assignedProject = assignedProjects[projectSlug];
return assignedProject?.projectRole;
},
);
export const activeOrganizationRoleSelector = createSelector(
activeProjectSelector,
assignedOrganizationsSelector,
(activeProject, assignedOrganizations) => {
const { organizationSlug } = activeProject;
const assignedOrganization = assignedOrganizations[organizationSlug];
return assignedOrganization?.organizationRole;
},
);
export const userRolesSelector = createSelector(
userAccountRoleSelector,
activeOrganizationRoleSelector,
activeProjectRoleSelector,
(userRole, organizationRole, projectRole) => ({
userRole,
organizationRole,
projectRole,
}),
);
export const isAdminSelector = (state) => userInfoSelector(state).userRole === ADMINISTRATOR;

export const availableProjectsSelector = createSelector(
Expand Down Expand Up @@ -106,43 +77,3 @@ export const availableProjectsSelector = createSelector(
export const apiKeysSelector = (state) => userSelector(state).apiKeys || [];

export const activeProjectKeySelector = (state) => userSelector(state).activeProjectKey;

export const createUserAssignedSelector = (projectSlug, organizationSlug) =>
createSelector(
userRolesSelector,
assignedOrganizationsSelector,
assignedProjectsSelector,
(userRoles, assignedOrganizations, assignedProjects) => {
const { userRole, organizationRole } = userRoles;
const isAdmin = userRole === ADMINISTRATOR;
const isManager = organizationRole === MANAGER;
let isAssignedToTargetOrganization = false;

if (organizationSlug) {
isAssignedToTargetOrganization = organizationSlug in assignedOrganizations;
} else {
const organizationId = assignedProjects[projectSlug]?.organizationId || '';
isAssignedToTargetOrganization = Object.keys(assignedOrganizations).some(
(key) => assignedOrganizations[key]?.organizationId === organizationId,
);
}

const isAssignedToTargetProject =
projectSlug && projectSlug in assignedProjects && isAssignedToTargetOrganization;

const assignmentNotRequired = isAdmin || (isManager && isAssignedToTargetOrganization);

const hasPermission = isAssignedToTargetProject || assignmentNotRequired;

const assignedProjectKey = assignedProjects?.[projectSlug]?.projectKey;

return {
isAdmin,
hasPermission,
assignedProjectKey,
assignmentNotRequired,
isAssignedToTargetProject,
isAssignedToTargetOrganization,
};
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { useTracking } from 'react-tracking';
import { userRolesSelector } from 'controllers/user';
import { userRolesSelector, urlOrganizationAndProjectSelector } from 'controllers/pages';
import { FormattedMessage } from 'react-intl';
import { canSeeMembers } from 'common/utils/permissions';
import {
Expand All @@ -29,7 +29,6 @@ import {
import { uiExtensionSidebarComponentsSelector } from 'controllers/plugins/uiExtensions';
import { AppSidebar } from 'layouts/common/appSidebar';
import { ExtensionLoader } from 'components/extensionLoader';
import { urlOrganizationAndProjectSelector } from 'controllers/pages';
import MembersIcon from 'common/img/sidebar/members-icon-inline.svg';
import SettingsIcon from 'common/img/sidebar/settings-icon-inline.svg';
import ProjectsIcon from 'common/img/sidebar/projects-icon-inline.svg';
Expand Down
3 changes: 1 addition & 2 deletions app/src/layouts/appLayout/projectSidebar/projectSidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { useTracking } from 'react-tracking';
import { userRolesSelector } from 'controllers/user';
import { userRolesSelector, urlOrganizationAndProjectSelector } from 'controllers/pages';
import { SIDEBAR_EVENTS } from 'components/main/analytics/events';
import { FormattedMessage } from 'react-intl';
import { canSeeMembers } from 'common/utils/permissions';
Expand All @@ -38,7 +38,6 @@ import {
} from 'controllers/plugins/uiExtensions';
import { AppSidebar } from 'layouts/common/appSidebar';
import { ExtensionLoader } from 'components/extensionLoader';
import { urlOrganizationAndProjectSelector } from 'controllers/pages';
import FiltersIcon from 'common/img/filters-icon-inline.svg';
import DashboardIcon from 'common/img/sidebar/dashboard-icon-inline.svg';
import LaunchesIcon from 'common/img/sidebar/launches-icon-inline.svg';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import { fetch } from 'common/utils';
import { URLS } from 'common/urls';
import { InputDropdown } from 'components/inputs/inputDropdown';
import { canChangeUserRole } from 'common/utils/permissions';
import { urlProjectSlugSelector } from 'controllers/pages';
import { userIdSelector, userRolesSelector } from 'controllers/user';
import { urlProjectSlugSelector, userRolesSelector } from 'controllers/pages';
import { userIdSelector } from 'controllers/user';
import { MEMBERS_PAGE_EVENTS } from 'components/main/analytics/events';
import { ROLES_MAP } from 'common/constants/projectRoles';
import { ADMINISTRATOR } from 'common/constants/accountRoles';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import { fetch } from 'common/utils';
import { URLS } from 'common/urls';
import { COMMON_LOCALE_KEYS } from 'common/constants/localization';
import { canAssignUnassignInternalUser } from 'common/utils/permissions';
import { urlProjectSlugSelector } from 'controllers/pages';
import { urlProjectSlugSelector, userRolesSelector } from 'controllers/pages';
import { userRolesType } from 'common/constants/projectRoles';
import { userIdSelector, assignedProjectsSelector, userRolesSelector } from 'controllers/user';
import { userIdSelector, assignedProjectsSelector } from 'controllers/user';
import { showNotification, NOTIFICATION_TYPES } from 'controllers/notification';
import { MEMBERS_PAGE_EVENTS } from 'components/main/analytics/events';
import UnassignIcon from 'common/img/unassign-inline.svg';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { showModalAction } from 'controllers/modal';
import { injectIntl, defineMessages } from 'react-intl';
import { reduxForm } from 'redux-form';
import { userRolesType } from 'common/constants/projectRoles';
import { userRolesSelector } from 'controllers/user';
import { userRolesSelector } from 'controllers/pages';
import { canInviteInternalUser } from 'common/utils/permissions';
import { GhostButton } from 'components/buttons/ghostButton';
import { FieldProvider } from 'components/fields/fieldProvider';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
activeDashboardIdSelector,
PROJECT_DASHBOARD_PAGE,
PROJECT_DASHBOARD_ITEM_PAGE,
userRolesSelector,
} from 'controllers/pages';
import {
dashboardItemsSelector,
Expand All @@ -33,7 +34,6 @@ import {
} from 'controllers/dashboard';
import { InputDropdown } from 'components/inputs/inputDropdown';
import { NavLink } from 'components/main/navLink';
import { userRolesSelector } from 'controllers/user';
import { userRolesType } from 'common/constants/projectRoles';
import { canWorkWithDashboard } from 'common/utils/permissions/permissions';
import { AddDashboardButton } from './addDashboardButton';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import AddFilterIcon from 'common/img/add-filter-inline.svg';
import { LAUNCHES_PAGE_EVENTS } from 'components/main/analytics/events';
import { canWorkWithFilters } from 'common/utils/permissions';
import { userRolesType } from 'common/constants/projectRoles';
import { userRolesSelector } from 'controllers/user';
import { userRolesSelector } from 'controllers/pages';
import { FilterList } from './filterList';
import { FiltersActionBar } from './filtersActionBar';
import { ExpandToggler } from './expandToggler';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
import { updateLaunchLocallyAction } from 'controllers/launch';
import { showModalAction } from 'controllers/modal';
import { userRolesType } from 'common/constants/projectRoles';
import { userRolesSelector } from 'controllers/user';
import { userRolesSelector } from 'controllers/pages';
import { enabledPattersSelector, projectKeySelector } from 'controllers/project';
import { analyzerExtensionsSelector } from 'controllers/appInfo';
import { COMMON_LOCALE_KEYS } from 'common/constants/localization';
Expand Down
Loading

0 comments on commit edf5274

Please sign in to comment.