diff --git a/cypress-tests/cypress/constants/texts/manageGroups.js b/cypress-tests/cypress/constants/texts/manageGroups.js index 2f41ac7648..d608c96cdf 100644 --- a/cypress-tests/cypress/constants/texts/manageGroups.js +++ b/cypress-tests/cypress/constants/texts/manageGroups.js @@ -4,7 +4,7 @@ export const groupsText = { tableHeader: "Name", allUsers: "All users", admin: "Admin", - cardTitle: "Add new group", + cardTitle: "Create new group", cancelButton: "Cancel", createGroupButton: "Create Group", groupNameExistToast: "Group name already exist", diff --git a/frontend/assets/translations/en.json b/frontend/assets/translations/en.json index 9e8a3bb8f0..016d9f5eed 100644 --- a/frontend/assets/translations/en.json +++ b/frontend/assets/translations/en.json @@ -268,7 +268,7 @@ "userGroups": "User Groups", "createNewGroup": "Create new group", "updateGroup": "Update group", - "addNewGroup": "Add new group", + "addNewGroup": "Create new group", "enterName": "Enter group name", "createGroup": "Create Group", "name": "Name" diff --git a/frontend/src/App/App.jsx b/frontend/src/App/App.jsx index 685cb6262e..90cd0b8e1b 100644 --- a/frontend/src/App/App.jsx +++ b/frontend/src/App/App.jsx @@ -307,14 +307,6 @@ class AppComponent extends React.Component { /> - - - } - /> - diff --git a/frontend/src/ManageGranularAccess/index.jsx b/frontend/src/ManageGranularAccess/index.jsx index ad10bf91ef..e26775fee6 100644 --- a/frontend/src/ManageGranularAccess/index.jsx +++ b/frontend/src/ManageGranularAccess/index.jsx @@ -11,6 +11,7 @@ import { toast } from 'react-hot-toast'; import GroupChipTD from '@/ManageGroupPermissionsV2/ResourceChip'; import '../ManageGroupPermissionsV2/groupPermissions.theme.scss'; import { Action } from 'rxjs/internal/scheduler/Action'; +import ChangeRoleModal from '@/ManageGroupPermissionResourcesV2/ChangeRoleModal'; class ManageGranularAccessComponent extends React.Component { constructor(props) { @@ -37,6 +38,13 @@ class ManageGranularAccessComponent extends React.Component { addableApps: [], modalType: 'add', modalTitle: 'Add app permissions', + showAutoRoleChangeModal: false, + autoRoleChangeModalMessage: '', + autoRoleChangeModalList: [], + autoRoleChangeMessageType: '', + updateParam: {}, + updatingPermission: {}, + updateType: '', }; } @@ -119,9 +127,15 @@ class ManageGranularAccessComponent extends React.Component { this.fetchGranularPermissions(this.props.groupPermissionId); this.closeAddPermissionModal(); }) - .catch((error) => { - toast.error(error.error); - console.log(error); + .catch(({ error }) => { + this.closeAddPermissionModal(); + this.props.updateParentState({ + showEditRoleErrorModal: true, + errorTitle: error?.title ? error?.title : 'Cannot remove last admin', + errorMessage: error.error, + errorIconName: 'usergear', + errorListItems: error.data, + }); }); // .then(()) }; @@ -160,10 +174,11 @@ class ManageGranularAccessComponent extends React.Component { console.log(granularPermission); }; - updateOnlyGranularPermissions = (permission, actions = {}) => { + updateOnlyGranularPermissions = (permission, actions = {}, allowRoleChange) => { console.log(actions); const body = { actions: actions, + allowRoleChange, }; groupPermissionV2Service .updateGranularPermission(permission.id, body) @@ -173,6 +188,20 @@ class ManageGranularAccessComponent extends React.Component { toast.success('Permission updated successfully'); }) .catch(({ error }) => { + console.log('error is'); + console.log(error); + if (error?.type) { + this.setState({ + showAutoRoleChangeModal: true, + autoRoleChangeModalMessage: error?.error, + autoRoleChangeModalList: error?.data, + autoRoleChangeMessageType: error?.type, + updateParam: actions, + updatingPermission: permission, + updateType: 'ONLY_PERMISSIONS', + }); + return; + } this.props.updateParentState({ showEditRoleErrorModal: true, errorTitle: error?.title ? error?.title : 'Cannot remove last admin', @@ -183,7 +212,7 @@ class ManageGranularAccessComponent extends React.Component { }); }; - updateGranularPermissions = () => { + updateGranularPermissions = (allowRoleChange) => { const { currentEditingPermissions, selectedApps, newPermissionName, isAll, initialPermissionState } = this.state; const currentResource = currentEditingPermissions?.appsGroupPermissions?.groupApps?.map((app) => { return app.app.id; @@ -207,17 +236,17 @@ class ManageGranularAccessComponent extends React.Component { id: id, }; }); - console.log('resource to add'); - console.log(resourcesToAdd); - console.log(resourcesToDelete); const body = { name: newPermissionName, isAll: isAll, actions: initialPermissionState, resourcesToAdd, resourcesToDelete, + allowRoleChange, }; + console.log(body); + groupPermissionV2Service .updateGranularPermission(currentEditingPermissions.id, body) .then(() => { @@ -225,8 +254,19 @@ class ManageGranularAccessComponent extends React.Component { this.closeAddPermissionModal(); toast.success('Permission updated successfully'); }) - .catch((err) => { - toast.error(err.error); + .catch(({ error }) => { + if (error?.type) { + this.setState({ + showAutoRoleChangeModal: true, + autoRoleChangeModalMessage: error?.error, + autoRoleChangeModalList: error?.data, + autoRoleChangeMessageType: error?.type, + updateType: '', + showAddPermissionModal: false, + }); + return; + } + toast.error(error.error); this.closeAddPermissionModal(); }); }; @@ -282,6 +322,30 @@ class ManageGranularAccessComponent extends React.Component { this.setState({ selectedApps: values }); }; + handleAutoRoleChangeModalClose = () => { + this.setState({ + showAutoRoleChangeModal: false, + autoRoleChangeModalMessage: '', + autoRoleChangeModalList: [], + autoRoleChangeMessageType: '', + updateParam: {}, + isLoading: false, + updatingPermission: {}, + updateType: '', + }); + }; + handleConfirmAutoRoleChangeGroupUpdate = () => { + console.log('this is running'); + this.updateGranularPermissions(true); + this.handleAutoRoleChangeModalClose(); + }; + + handleConfirmAutoRoleChangeOnlyGroupUpdate = () => { + const { updateParam, updatingPermission } = this.state; + this.updateOnlyGranularPermissions(updatingPermission, updateParam, true); + this.handleAutoRoleChangeModalClose(); + }; + render() { const { isEmpty, @@ -297,32 +361,45 @@ class ManageGranularAccessComponent extends React.Component { modalTitle, modalType, newPermissionName, + showAutoRoleChangeModal, + autoRoleChangeModalMessage, + autoRoleChangeModalList, + autoRoleChangeMessageType, + updateParam, + updatingPermission, + updateType, } = this.state; - const apps = [ - { name: 'App 1', value: 'App1', label: 'app 1' }, - { name: 'App Long name 1', value: 'App2', label: 'app long name 1' }, - { name: 'App very long name', value: 'App3', label: 'app very long name' }, - { name: 'App 4', value: 'App4', label: 'app 4' }, - { name: 'App5veryverylongname', value: 'App5veryverylongname', label: 'App5veryverylongname' }, - { name: 'App 6', value: 'App6', label: '6' }, - { name: 'App 7', value: 'App 7', label: 'app 7' }, - { name: 'App 8', value: 'App 8', label: 'app 8' }, - { name: 'App 9', value: 'App 9', label: 'app 9' }, - { name: 'App 10', value: 'App 10', label: 'app 10' }, - { name: 'App 11', value: 'App 11', label: 'app 11' }, - { name: 'App 12', value: 'App 12', label: 'app 12' }, - ]; + const currentGroupPermission = this.props?.groupPermission; const isRoleGroup = currentGroupPermission.name == 'admin'; const showPermissionInfo = currentGroupPermission.name == 'admin' || currentGroupPermission.name == 'end-user'; const disableEditUpdate = currentGroupPermission.name == 'end-user'; return (
+ { + this.updateGranularPermissions(); + } + } className="permission-manager-modal" title={
diff --git a/frontend/src/ManageGroupPermissionResourcesV2/ChangeRoleModal.jsx b/frontend/src/ManageGroupPermissionResourcesV2/ChangeRoleModal.jsx new file mode 100644 index 0000000000..c0e09c0910 --- /dev/null +++ b/frontend/src/ManageGroupPermissionResourcesV2/ChangeRoleModal.jsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import ModalBase from '@/_ui/Modal'; +import '../ManageGroupPermissionsV2/groupPermissions.theme.scss'; + +function ChangeRoleModal({ + showAutoRoleChangeModal, + autoRoleChangeModalList, + autoRoleChangeMessageType, + handleAutoRoleChangeModalClose, + handleConfirmation, + darkMode, + isLoading, +}) { + const { t } = useTranslation(); + + const renderUserChangeMessage = (type) => { + const changePermissionMessage = ( +

+ Granting this permission to the user group will result in a role change for the following user(s) from{' '} + end-users to builders. Are you sure you want to continue? +

+ ); + const addUserMessage = ( +

+ Adding the following user(s) to this group will change their default group from end-users to{' '} + builders. Are you sure you want to continue? +

+ ); + const message = type === 'USER_ROLE_CHANGE_ADD_USERS' ? addUserMessage : changePermissionMessage; + return message; + }; + + return ( + + Change in user role +
+ } + handleConfirm={handleConfirmation} + confirmBtnProps={{ title: 'Continue' }} + show={showAutoRoleChangeModal} + handleClose={handleAutoRoleChangeModalClose} + darkMode={darkMode} + isLoading={isLoading} + > + <> + {renderUserChangeMessage(autoRoleChangeMessageType)} +

+
+ {autoRoleChangeModalList.map((item, index) => ( +
+ {`${index + 1}. ${item}`} +
+ ))} +
+ +
+ ); +} + +export default ChangeRoleModal; diff --git a/frontend/src/ManageGroupPermissionResourcesV2/index.jsx b/frontend/src/ManageGroupPermissionResourcesV2/index.jsx index 817bd5f33a..c048d5a2ec 100644 --- a/frontend/src/ManageGroupPermissionResourcesV2/index.jsx +++ b/frontend/src/ManageGroupPermissionResourcesV2/index.jsx @@ -18,6 +18,7 @@ import './grpPermissionResc.theme.scss'; import { EDIT_ROLE_MESSAGE } from './constant'; import { SearchBox } from '@/_components/SearchBox'; import EditRoleErrorModal from '@/ManageGroupPermissionsV2/ErrorModal/ErrorModal'; +import ChangeRoleModal from '@/ManageGroupPermissionResourcesV2/ChangeRoleModal'; class ManageGroupPermissionResourcesComponent extends React.Component { constructor(props) { super(props); @@ -50,6 +51,11 @@ class ManageGroupPermissionResourcesComponent extends React.Component { errorTitle: '', showEditRoleErrorModal: false, errorIconName: '', + showAutoRoleChangeModal: false, + autoRoleChangeModalMessage: '', + autoRoleChangeModalList: [], + autoRoleChangeMessageType: '', + updateParam: {}, }; } @@ -64,13 +70,14 @@ class ManageGroupPermissionResourcesComponent extends React.Component { } fetchGroupPermission = (groupPermissionId) => { - groupPermissionV2Service.getGroup(groupPermissionId).then((data) => { + groupPermissionV2Service.getGroup(groupPermissionId).then(({ group, isBuilderLevel }) => { this.setState((prevState) => { return { - isRoleGroup: data.type === 'default', - groupPermission: data, + isRoleGroup: group.type === 'default', + groupPermission: group, currentTab: prevState.currentTab, isLoadingGroup: false, + isBuilderLevel: isBuilderLevel, }; }); }); @@ -93,6 +100,8 @@ class ManageGroupPermissionResourcesComponent extends React.Component { groupPermissionV2Service .getUsersNotInGroup(query, groupPermissionId) .then((users) => { + console.log('loggimgusers'); + console.log(users); resolve( users.map((user) => { return { @@ -101,6 +110,7 @@ class ManageGroupPermissionResourcesComponent extends React.Component { first_name: user.firstName, last_name: user.lastName, email: user.email, + role: user?.userGroups?.group?.name, }; }) ); @@ -148,15 +158,24 @@ class ManageGroupPermissionResourcesComponent extends React.Component { }); }; - updateGroupPermission = (groupPermissionId, params) => { + updateGroupPermission = (groupPermissionId, params, allowRoleChange) => { groupPermissionV2Service - .update(groupPermissionId, params) + .update(groupPermissionId, { ...params, allowRoleChange }) .then(() => { toast.success('Group permissions updated'); this.fetchGroupPermission(groupPermissionId); }) .catch(({ error }) => { console.log(error); + if (error?.type) { + this.setState({ + showAutoRoleChangeModal: true, + autoRoleChangeModalMessage: error?.error, + autoRoleChangeModalList: error?.data, + autoRoleChangeMessageType: error?.type, + }); + return; + } this.setState({ errorMessage: error?.error, showEditRoleErrorModal: true, @@ -274,11 +293,14 @@ class ManageGroupPermissionResourcesComponent extends React.Component { }); }; - addSelectedUsersToGroup = (groupPermissionId, selectedUsers) => { + addSelectedUsersToGroup = (groupPermissionId, selectedUsers, allowRoleChange) => { + console.log('selected users'); + console.log(selectedUsers); this.setState({ isAddingUsers: true }); const body = { userIds: selectedUsers.map((user) => user.value), groupId: groupPermissionId, + allowRoleChange, }; groupPermissionV2Service .addUsersInGroups(body) @@ -293,6 +315,16 @@ class ManageGroupPermissionResourcesComponent extends React.Component { this.fetchUsersInGroup(groupPermissionId); }) .catch(({ error }) => { + if (error?.type) { + this.setState({ + isLoadingUsers: false, + showAutoRoleChangeModal: true, + autoRoleChangeModalMessage: error?.error, + autoRoleChangeModalList: error?.data, + autoRoleChangeMessageType: error?.type, + }); + return; + } this.setState({ showEditRoleErrorModal: true, errorTitle: error?.title, @@ -366,6 +398,7 @@ class ManageGroupPermissionResourcesComponent extends React.Component { .then(() => { this.fetchUsersInGroup(groupPermission.id); toast.success('Role updated successfully'); + if (groupPermission?.name === 'admin') window.location.reload(); }) .catch(({ error }) => { this.setState({ @@ -422,8 +455,58 @@ class ManageGroupPermissionResourcesComponent extends React.Component { })); }; + toggleAutoRoleChangeModal = () => { + this.setState((prevState) => ({ + showAutoRoleChangeModal: !prevState.showAutoRoleChangeModal, + })); + }; + handleAutoRoleChangeModalClose = () => { + this.setState({ + showAutoRoleChangeModal: false, + autoRoleChangeModalMessage: '', + autoRoleChangeModalList: [], + autoRoleChangeMessageType: '', + updateParam: {}, + isLoadingGroup: false, + isLoadingUsers: false, + }); + }; + + renderUserChangeMessage = (type) => { + const changePermissionMessage = ( +

+ Granting this permission to the user group will result in a role change for the following user(s) from{' '} + end-users to builders. Are you sure you want to continue? +

+ ); + const addUserMessage = ( +

+ Adding the following user(s) to this group will change their default group from end-users to{' '} + builders. Are you sure you want to continue? +

+ ); + const message = type === 'USER_ROLE_CHANGE_ADD_USERS' ? addUserMessage : changePermissionMessage; + return message; + }; + toggleAddUsersToRoleModal = () => this.setState({ isAddUsersToRoleModalOpen: !this.state.isAddUsersToRoleModalOpen }); + handleConfirmAutoRoleChangeGroupUpdate = () => { + console.log('this is running'); + const { updateParam, groupPermission } = this.state; + this.updateGroupPermission(groupPermission.id, updateParam, true); + this.setState({ + updateParam: {}, + }); + this.handleAutoRoleChangeModalClose(); + }; + + handleConfirmAutoRoleChangeAddUser = () => { + const { groupPermission, selectedUsers } = this.state; + this.addSelectedUsersToGroup(groupPermission?.id, selectedUsers, true); + this.handleAutoRoleChangeModalClose(); + }; + render() { if (!this.props.groupPermissionId) return null; @@ -448,8 +531,10 @@ class ManageGroupPermissionResourcesComponent extends React.Component { errorTitle, showEditRoleErrorModal, errorIconName, - addableApps, - granularPermissions, + showAutoRoleChangeModal, + autoRoleChangeModalMessage, + autoRoleChangeModalList, + autoRoleChangeMessageType, } = this.state; const isBasicPlan = false; @@ -526,28 +611,19 @@ class ManageGroupPermissionResourcesComponent extends React.Component {
)} - - Edit user role -
- {updatingUserRole?.email} -
- + -

- Changing user default group from builder to end-user will affect the count of users covered by your plan. -

-
-

- This will also remove the user from any custom groups with builder-like permissions. Are you sure you want - to continue? -

-
+ isLoading={isLoadingGroup || isLoadingUsers} + />
{isLoadingGroup ? ( @@ -869,6 +945,9 @@ class ManageGroupPermissionResourcesComponent extends React.Component { this.updateGroupPermission(groupPermission.id, { appCreate: !groupPermission.appCreate, }); + this.setState({ + updateParam: { appCreate: !groupPermission.appCreate }, + }); }} checked={groupPermission.appCreate} disabled={disablePermissionUpdate} @@ -893,6 +972,9 @@ class ManageGroupPermissionResourcesComponent extends React.Component { this.updateGroupPermission(groupPermission.id, { appDelete: !groupPermission.appDelete, }); + this.setState({ + updateParam: { appDelete: !groupPermission.appDelete }, + }); }} checked={groupPermission.appDelete} disabled={disablePermissionUpdate} @@ -930,6 +1012,9 @@ class ManageGroupPermissionResourcesComponent extends React.Component { this.updateGroupPermission(groupPermission.id, { folderCRUD: !groupPermission.folderCRUD, }); + this.setState({ + updateParam: { folderCRUD: !groupPermission.folderCRUD }, + }); }} checked={groupPermission.folderCRUD} disabled={disablePermissionUpdate} @@ -966,6 +1051,9 @@ class ManageGroupPermissionResourcesComponent extends React.Component { this.updateGroupPermission(groupPermission.id, { orgConstantCRUD: !groupPermission.orgConstantCRUD, }); + this.setState({ + updateParam: { orgConstantCRUD: !groupPermission.orgConstantCRUD }, + }); }} checked={groupPermission.orgConstantCRUD} disabled={disablePermissionUpdate} @@ -1003,6 +1091,7 @@ class ManageGroupPermissionResourcesComponent extends React.Component { groupPermission={groupPermission} setErrorState={this.setErrorState} updateParentState={this.changeThisComponentState} + fetchGroup={this.fetchGroupPermission} />
diff --git a/frontend/src/ManageGroupPermissions/ManageGroupPermissions.jsx b/frontend/src/ManageGroupPermissions/ManageGroupPermissions.jsx index a7fb9ca550..3bed6a747c 100644 --- a/frontend/src/ManageGroupPermissions/ManageGroupPermissions.jsx +++ b/frontend/src/ManageGroupPermissions/ManageGroupPermissions.jsx @@ -455,7 +455,7 @@ class ManageGroupPermissionsComponent extends React.Component { title={ showGroupNameUpdateForm ? this.props.t('header.organization.menus.manageGroups.permissions.updateGroup', 'Update group') - : this.props.t('header.organization.menus.manageGroups.permissions.addNewGroup', 'Add new group') + : this.props.t('header.organization.menus.manageGroups.permissions.addNewGroup', 'Create new group') } >
{ + if (value.length > 50) return; this.setState({ newGroupName: value, isSaveBtnDisabled: false, @@ -477,7 +478,7 @@ class ManageGroupPermissionsComponent extends React.Component { className="btn btn-primary create-new-group-button" onClick={(e) => { e.preventDefault(); - this.setState({ newGroupName: null, showNewGroupForm: true, isSaveBtnDisabled: true }); + this.setState({ newGroupName: '', showNewGroupForm: true, isSaveBtnDisabled: true }); }} data-cy="create-new-group-button" leftIcon="plus" @@ -505,7 +506,7 @@ class ManageGroupPermissionsComponent extends React.Component { title={ showGroupNameUpdateForm ? this.props.t('header.organization.menus.manageGroups.permissions.updateGroup', 'Update group') - : this.props.t('header.organization.menus.manageGroups.permissions.addNewGroup', 'Add new group') + : this.props.t('header.organization.menus.manageGroups.permissions.addNewGroup', 'Create new group') } > + Group name must be unique and max 50 characters diff --git a/frontend/src/ManageOrgUsers/InviteUsersForm.jsx b/frontend/src/ManageOrgUsers/InviteUsersForm.jsx index f847ce357d..9f037675fd 100644 --- a/frontend/src/ManageOrgUsers/InviteUsersForm.jsx +++ b/frontend/src/ManageOrgUsers/InviteUsersForm.jsx @@ -106,7 +106,8 @@ function InviteUsersForm({ let role = null; if (currentEditingUser) role = selectedGroups.find( - (group) => group.groupType === 'default' && !currentEditingUser.role_group.includes(group.value) + (group) => + group.groupType === 'default' && !currentEditingUser.role_group.map((role) => role.name).includes(group.value) )?.value; const selectedGroupsIds = selectedGroups.filter((group) => group.groupType !== 'default').map((group) => group.id); const newGroupsToAdd = selectedGroupsIds.filter((selectedGroupId) => !existingGroups.includes(selectedGroupId)); @@ -115,11 +116,12 @@ function InviteUsersForm({ }; const isEdited = () => { - const { newGroupsToAdd, groupsToRemove } = getEditedGroups(); + const { newGroupsToAdd, groupsToRemove, role } = getEditedGroups(); const { first_name, last_name } = currentEditingUser || {}; return isEditing ? fields['fullName'] !== `${first_name}${last_name && ` ${last_name}`}` || groupsToRemove.length || + role !== undefined || newGroupsToAdd.length : true; }; @@ -301,7 +303,7 @@ function InviteUsersForm({ form={activeTab == 1 ? 'inviteByEmail' : 'inviteBulkUsers'} type="submit" variant="primary" - disabled={uploadingUsers || creatingUser || !isEdited() || !containRoleGroup} + disabled={uploadingUsers || creatingUser || !isEdited() || (!isEditing && !containRoleGroup)} data-cy={activeTab == 1 ? 'button-invite-users' : 'button-upload-users'} leftIcon={activeTab == 1 ? 'sent' : 'fileupload'} width="20" diff --git a/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx b/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx index ae7784bd29..6f69ce0233 100644 --- a/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx +++ b/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx @@ -11,6 +11,7 @@ import { ButtonSolid } from '@/_ui/AppButton/AppButton'; import ManageOrgUsersDrawer from './ManageOrgUsersDrawer'; import { USER_DRAWER_MODES } from '@/_helpers/utils'; import { getQueryParams } from '@/_helpers/routes'; +import EditRoleErrorModal from '@/ManageGroupPermissionsV2/ErrorModal/ErrorModal'; class ManageOrgUsersComponent extends React.Component { constructor(props) { @@ -36,6 +37,11 @@ class ManageOrgUsersComponent extends React.Component { userDrawerMode: USER_DRAWER_MODES.CREATE, newSelectedGroups: [], existingGroupsToRemove: [], + showErrorModal: false, + errorModalMessage: '', + errorItemList: [], + errorTitle: '', + errorIconName: 'usergear', }; } @@ -223,7 +229,13 @@ class ManageOrgUsersComponent extends React.Component { }); }) .catch(({ error }) => { - toast.error(error.error); + this.setState({ + showErrorModal: true, + errorModalMessage: error.error, + errorTitle: error?.title || 'Conflicting Permissions', + errorItemList: error?.data, + errorIconName: 'usergear', + }); this.setState({ creatingUser: false }); }); } else { @@ -254,6 +266,16 @@ class ManageOrgUsersComponent extends React.Component { toast.success('Invitation URL copied'); }; + clearErrorState = () => { + this.setState({ + showErrorModal: false, + errorModalMessage: '', + errorItemList: [], + errorTitle: '', + errorIconName: '', + }); + }; + pageChanged = (page) => { this.fetchUsers(page, this.state.options); }; @@ -293,10 +315,24 @@ class ManageOrgUsersComponent extends React.Component { meta, currentEditingUser, userDrawerMode, + showErrorModal, + errorModalMessage, + errorItemList, + errorTitle, + errorIconName, } = this.state; return (
+ {this.state.isInviteUsersDrawerOpen && ( { switch (groupName) { case 'users': return 'Users'; case 'groups': return 'Groups'; - case 'groups2': - return 'Groups V2'; case 'workspace-login': return 'Workspace login'; case 'workspace-variables': diff --git a/server/ee/services/oauth/oauth.service.ts b/server/ee/services/oauth/oauth.service.ts index 3fb6ed9a6c..a247d2c1b9 100644 --- a/server/ee/services/oauth/oauth.service.ts +++ b/server/ee/services/oauth/oauth.service.ts @@ -68,7 +68,6 @@ export class OauthService { organization.id, //Adding user as END-USER on workspace signup USER_ROLE.END_USER, - [], user, true, defaultOrganization?.id, @@ -240,7 +239,6 @@ export class OauthService { defaultOrganization.id, //Adding as admin since this is instance login USER_ROLE.ADMIN, - [], null, true, null, diff --git a/server/migrations/1714015513342-AddGroupPermissionsTable.ts b/server/migrations/1714015513342-AddGroupPermissionsTable.ts index 6515a005ff..1584209964 100644 --- a/server/migrations/1714015513342-AddGroupPermissionsTable.ts +++ b/server/migrations/1714015513342-AddGroupPermissionsTable.ts @@ -14,7 +14,7 @@ export class AddGroupPermissionsTable1714015513342 implements MigrationInterface CREATE TABLE IF NOT EXISTS permission_groups ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), organization_id UUID, - name VARCHAR NOT NULL, + name VARCHAR(50) NOT NULL, type group_permissions_type NOT NULL DEFAULT 'custom', app_create BOOLEAN DEFAULT false, app_delete BOOLEAN DEFAULT false, diff --git a/server/migrations/1714015541245-AddGroupUsersTable.ts b/server/migrations/1714015541245-AddGroupUsersTable.ts index 3004258086..e8f4893e99 100644 --- a/server/migrations/1714015541245-AddGroupUsersTable.ts +++ b/server/migrations/1714015541245-AddGroupUsersTable.ts @@ -7,8 +7,8 @@ export class AddGroupUsersTable1714015541245 implements MigrationInterface { ` CREATE TABLE IF NOT EXISTS group_users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - user_id UUID, - group_id UUID, + user_id UUID NOT NULL, + group_id UUID NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, diff --git a/server/src/controllers/group_permissions.controller.v2.ts b/server/src/controllers/group_permissions.controller.v2.ts index 5606652f67..5b541cd38c 100644 --- a/server/src/controllers/group_permissions.controller.v2.ts +++ b/server/src/controllers/group_permissions.controller.v2.ts @@ -101,7 +101,10 @@ export class GroupPermissionsControllerV2 { @CheckPolicies((ability: AppAbility) => ability.can(ORGANIZATION_RESOURCE_ACTIONS.ACCESS_PERMISSIONS, UserEntity)) @Get(':groupId/group-user') async getAllGroupUser(@User() user, @Param('groupId') groupId: string, @Query('input') searchInput: string) { - return await this.groupPermissionsService.getAllGroupUsers(groupId, searchInput); + return await this.groupPermissionsService.getAllGroupUsers( + { groupId, organizationId: user.organizationId }, + searchInput + ); } @UseGuards(JwtAuthGuard, PoliciesGuard) @@ -151,7 +154,7 @@ export class GroupPermissionsControllerV2 { //Check for license validation first here // What are license validation for this const { groupId, createAppsPermissionsObject } = createGranularPermissionsDto; - const group = await this.groupPermissionsService.getGroup(groupId); + const { group } = await this.groupPermissionsService.getGroup(groupId); validateGranularPermissionCreateOperation(group); return await this.granularPermissionsService.create( { diff --git a/server/src/dto/granular-permissions.dto.ts b/server/src/dto/granular-permissions.dto.ts index f9db3ab834..33e94ca1b2 100644 --- a/server/src/dto/granular-permissions.dto.ts +++ b/server/src/dto/granular-permissions.dto.ts @@ -28,6 +28,10 @@ export class CreateGranularPermissionDto { @IsOptional() createAppsPermissionsObject: CreateAppsPermissionsObject; + + // @IsBoolean() + // @IsOptional() + // allowRoleChange: boolean; } export class UpdateGranularPermissionDto { @@ -47,4 +51,8 @@ export class UpdateGranularPermissionDto { @IsOptional() resourcesToDelete: GranularPermissionDeleteResourceItems; + + @IsBoolean() + @IsOptional() + allowRoleChange: boolean; } diff --git a/server/src/dto/group_permissions.dto.ts b/server/src/dto/group_permissions.dto.ts index 6ea12543d1..58202b45cd 100644 --- a/server/src/dto/group_permissions.dto.ts +++ b/server/src/dto/group_permissions.dto.ts @@ -38,6 +38,10 @@ export class UpdateGroupPermissionDto { @IsBoolean() @IsOptional() dataSourceDelete: boolean; + + @IsBoolean() + @IsOptional() + allowRoleChange: boolean; } export class EditUserRoleDto { @@ -59,4 +63,8 @@ export class AddGroupUserDto { @IsString() @IsNotEmpty() groupId: string; + + @IsBoolean() + @IsOptional() + allowRoleChange: boolean; } diff --git a/server/src/entities/group_users.entity.ts b/server/src/entities/group_users.entity.ts index 92e58a9b3b..03ac55386b 100644 --- a/server/src/entities/group_users.entity.ts +++ b/server/src/entities/group_users.entity.ts @@ -16,10 +16,10 @@ export class GroupUsers extends BaseEntity { @PrimaryGeneratedColumn('uuid') id: string; - @Column({ name: 'user_id' }) + @Column({ name: 'user_id', nullable: false }) userId: string; - @Column({ name: 'group_id' }) + @Column({ name: 'group_id', nullable: false }) groupId: string; @CreateDateColumn({ default: () => 'now()', name: 'created_at' }) diff --git a/server/src/modules/user_resource_permissions/constants/granular-permissions.constant.ts b/server/src/modules/user_resource_permissions/constants/granular-permissions.constant.ts index 87c68b238d..f191193141 100644 --- a/server/src/modules/user_resource_permissions/constants/granular-permissions.constant.ts +++ b/server/src/modules/user_resource_permissions/constants/granular-permissions.constant.ts @@ -40,4 +40,6 @@ export const ERROR_HANDLER = { EDITOR_LEVEL_PERMISSIONS_NOT_ALLOWED: 'End-users can only be granted permission to view apps. If you wish to add this permission, kindly change the following users role from end-user to builder', EDITOR_LEVEL_PERMISSION_NOT_ALLOWED_END_USER: 'Cannot assign builder level permission to end users', + UPDATE_EDITABLE_PERMISSION_END_USER_GROUP: + 'End-users can only be granted permission to view apps. If you wish to add this permission, kindly change the following users role from end-user to builder- ', }; diff --git a/server/src/modules/user_resource_permissions/constants/group-permissions.constant.ts b/server/src/modules/user_resource_permissions/constants/group-permissions.constant.ts index 6519ca0aec..0ab1e39477 100644 --- a/server/src/modules/user_resource_permissions/constants/group-permissions.constant.ts +++ b/server/src/modules/user_resource_permissions/constants/group-permissions.constant.ts @@ -40,6 +40,7 @@ export const DEFAULT_GROUP_PERMISSIONS = { orgConstantCRUD: true, dataSourceCreate: true, dataSourceDelete: true, + isBuilderLevel: true, }, BUILDER: { name: USER_ROLE.BUILDER, @@ -52,6 +53,7 @@ export const DEFAULT_GROUP_PERMISSIONS = { orgConstantCRUD: true, dataSourceCreate: true, dataSourceDelete: true, + isBuilderLevel: true, }, END_USER: { name: USER_ROLE.END_USER, @@ -64,6 +66,7 @@ export const DEFAULT_GROUP_PERMISSIONS = { orgConstantCRUD: false, dataSourceCreate: false, dataSourceDelete: false, + isBuilderLevel: false, }, } as Record; diff --git a/server/src/modules/user_resource_permissions/interface/granular-permissions.interface.ts b/server/src/modules/user_resource_permissions/interface/granular-permissions.interface.ts index b16cc420e4..8d5db13422 100644 --- a/server/src/modules/user_resource_permissions/interface/granular-permissions.interface.ts +++ b/server/src/modules/user_resource_permissions/interface/granular-permissions.interface.ts @@ -30,11 +30,12 @@ export type GranularPermissionAddResourceItems = AppsPermissionAddResourceItem[] export type GranularPermissionDeleteResourceItems = AppsPermissionDeleteResourceItem[]; export interface UpdateResourceGroupPermissionsObject { - group?: GroupPermissions; + group: GroupPermissions; granularPermissions: GranularPermissions; actions: ResourceGroupActions; resourcesToAdd: GranularPermissionAddResourceItems; resourcesToDelete: GranularPermissionDeleteResourceItems; + allowRoleChange?: boolean; } export interface GranularPermissionQuerySearchParam { diff --git a/server/src/modules/user_resource_permissions/interface/group-permissions.interface.ts b/server/src/modules/user_resource_permissions/interface/group-permissions.interface.ts index 3542bbe82b..eaf769ddf8 100644 --- a/server/src/modules/user_resource_permissions/interface/group-permissions.interface.ts +++ b/server/src/modules/user_resource_permissions/interface/group-permissions.interface.ts @@ -15,6 +15,17 @@ export interface CreateDefaultGroupObject { dataSourceDelete: boolean; } +export interface ValidateEditUserGroupAdditionObject { + userId: string; + groupsToAddIds: string[]; + organizationId: string; +} + +export interface GetGroupUsersObject { + groupId: string; + organizationId: string; +} + export interface GroupQuerySearchParamObject { [key: string]: SearchParamItem | boolean | string | number; name?: SearchParamItem; diff --git a/server/src/modules/user_resource_permissions/services/group-permissions.utility.service.ts b/server/src/modules/user_resource_permissions/services/group-permissions.utility.service.ts index f0ee74a3ed..2ab24d66c8 100644 --- a/server/src/modules/user_resource_permissions/services/group-permissions.utility.service.ts +++ b/server/src/modules/user_resource_permissions/services/group-permissions.utility.service.ts @@ -1,7 +1,10 @@ -import { Injectable } from '@nestjs/common'; +import { BadRequestException, Injectable } from '@nestjs/common'; import { GroupPermissions } from 'src/entities/group_permissions.entity'; import { User } from 'src/entities/user.entity'; -import { USER_ROLE } from '@module/user_resource_permissions/constants/group-permissions.constant'; +import { + GROUP_PERMISSIONS_TYPE, + USER_ROLE, +} from '@module/user_resource_permissions/constants/group-permissions.constant'; import { addableUsersToGroupQuery, getRoleUsersListQuery, @@ -10,6 +13,9 @@ import { import { EntityManager } from 'typeorm'; import { dbTransactionWrap } from '@helpers/utils.helper'; import { App } from 'src/entities/app.entity'; +import { getAllGranularPermissionQuery } from '../utility/granular-permissios.utility'; +import { ResourceType } from '../constants/granular-permissions.constant'; +import { ValidateEditUserGroupAdditionObject } from '../interface/group-permissions.interface'; @Injectable() export class GroupPermissionsUtilityService { @@ -26,6 +32,13 @@ export class GroupPermissionsUtilityService { return await query.getMany(); }, manager); } + async getRoleGroup(role: USER_ROLE, organizationId: string, manager?: EntityManager): Promise { + return await dbTransactionWrap(async (manager) => { + return await manager.findOne(GroupPermissions, { + where: { name: role, organizationId, type: GROUP_PERMISSIONS_TYPE.DEFAULT }, + }); + }, manager); + } async getUserRole(userId: string, organizationId: string, manager?: EntityManager): Promise { return await dbTransactionWrap(async (manager: EntityManager) => { @@ -56,4 +69,59 @@ export class GroupPermissionsUtilityService { }); }, manager); } + + async checkIfBuilderLevelResourcesPermissions(groupId: string, manager?: EntityManager): Promise { + return await dbTransactionWrap(async (manager: EntityManager) => { + const allPermission = await getAllGranularPermissionQuery({ groupId }, manager).getMany(); + if (!allPermission) return false; + const isBuilderLevelAppsPermission = allPermission + .filter((permissions) => permissions.type === ResourceType.APP) + .some((permissions) => { + const appPermission = permissions.appsGroupPermissions; + return appPermission.canEdit === true; + }); + //Add for other permissions here + return isBuilderLevelAppsPermission; + }, manager); + } + + async isEditableGroup(group: GroupPermissions, manager?: EntityManager): Promise { + return await dbTransactionWrap(async (manager: EntityManager) => { + const editPermissionsPresent = + Object.values(group).some((value) => typeof value === 'boolean' && value === true) || + (await this.checkIfBuilderLevelResourcesPermissions(group.id, manager)); + return editPermissionsPresent; + }, manager); + } + + async validateEditUserGroupPermissionsAddition( + functionParam: ValidateEditUserGroupAdditionObject, + manager?: EntityManager + ) { + const { organizationId, userId, groupsToAddIds } = functionParam; + console.log('validating permissions'); + console.log(groupsToAddIds); + + return await dbTransactionWrap(async (manager: EntityManager) => { + const userRole = await this.getUserRole(userId, organizationId, manager); + console.log(userRole); + if (userRole.name === USER_ROLE.END_USER) { + return await Promise.all( + groupsToAddIds.map(async (id) => { + const group = await manager.findOne(GroupPermissions, id); + const isEditableGroup = await this.isEditableGroup(group, manager); + if (!isEditableGroup) { + throw new BadRequestException({ + message: { + error: + 'End-users can only be granted permission to view apps. Kindly change the user role or custom group to continue.', + title: 'Conflicting Permissions', + }, + }); + } + }) + ); + } + }, manager); + } } diff --git a/server/src/modules/user_resource_permissions/utility/group-permissions.utility.ts b/server/src/modules/user_resource_permissions/utility/group-permissions.utility.ts index 3f68527077..86ddc026f8 100644 --- a/server/src/modules/user_resource_permissions/utility/group-permissions.utility.ts +++ b/server/src/modules/user_resource_permissions/utility/group-permissions.utility.ts @@ -6,6 +6,7 @@ import { BadRequestException, MethodNotAllowedException } from '@nestjs/common'; import { CreateGroupPermissionDto, UpdateGroupPermissionDto } from '@dto/group_permissions.dto'; import { ERROR_HANDLER } from '@module/user_resource_permissions/constants/group-permissions.constant'; import { GroupUsers } from 'src/entities/group_users.entity'; +import { GetGroupUsersObject } from '../interface/group-permissions.interface'; export function getRoleUsersListQuery( role: USER_ROLE, @@ -38,6 +39,7 @@ export function getRoleUsersListQuery( 'user.lastName', 'user.email', 'userGroups.groupId', + 'userGroups.id', 'group.name', 'group.type', ]); @@ -145,6 +147,13 @@ export function addableUsersToGroupQuery( .innerJoin('users.organizationUsers', 'organization_users', 'organization_users.organizationId = :organizationId', { organizationId: organizationId, }) + .innerJoinAndSelect('users.userGroups', 'userGroups') + .innerJoinAndSelect('userGroups.group', 'group', 'group.organizationId = :organizationId', { + organizationId, + }) + .where('group.type = :type', { + type: GROUP_PERMISSIONS_TYPE.DEFAULT, + }) .where((qb) => { const subQuery = qb .subQuery() @@ -158,6 +167,7 @@ export function addableUsersToGroupQuery( return 'users.id NOT IN ' + subQuery; }) .andWhere(addableUserGetOrConditions(searchInput)) + .select(['users.id', 'users.firstName', 'users.lastName', 'users.email', 'userGroups.id', 'group.name']) .orderBy('users.createdAt', 'DESC'); return query; @@ -180,14 +190,35 @@ const addableUserGetOrConditions = (searchInput) => { }; export function getUserInGroupQuery( - groupId: string, + getGroupUsersObject: GetGroupUsersObject, manager: EntityManager, searchInput: string ): SelectQueryBuilder { + const { groupId, organizationId } = getGroupUsersObject; + const query = manager .createQueryBuilder(GroupUsers, 'groupUsers') - .innerJoinAndSelect('groupUsers.user', 'users', 'groupUsers.groupId = :groupId', { groupId }) - .select(['groupUsers.id', 'groupUsers.groupId', 'users.id', 'users.firstName', 'users.lastName', 'users.email']) + .innerJoinAndSelect('groupUsers.user', 'users', 'groupUsers.groupId = :groupId', { + groupId, + }) + .innerJoinAndSelect('users.userGroups', 'userRole') + .innerJoinAndSelect('userRole.group', 'role', 'role.organizationId = :organizationId', { + organizationId, + }) + .andWhere('role.type = :type', { + type: GROUP_PERMISSIONS_TYPE.DEFAULT, + }) + .select([ + 'groupUsers.id', + 'groupUsers.groupId', + 'users.id', + 'users.firstName', + 'users.lastName', + 'users.email', + 'userRole.id', + 'role.name', + ]) + .addSelect('role.name', 'userRole') .andWhere(addableUserGetOrConditions(searchInput)); return query; } diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index 86cd29d8bf..8a79e9494e 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -360,7 +360,6 @@ export class AuthService { }, personalWorkspace.id, USER_ROLE.ADMIN, - [], existingUser, true, null, @@ -719,7 +718,6 @@ export class AuthService { }, organization.id, USER_ROLE.ADMIN, - [], null, false, null, diff --git a/server/src/services/granular_permissions.service.ts b/server/src/services/granular_permissions.service.ts index 7fc0af2f99..38eaf5d00f 100644 --- a/server/src/services/granular_permissions.service.ts +++ b/server/src/services/granular_permissions.service.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Injectable } from '@nestjs/common'; +import { BadRequestException, Injectable, MethodNotAllowedException } from '@nestjs/common'; import { ResourceType } from '@module/user_resource_permissions/constants/granular-permissions.constant'; import { GranularResourcePermissions, @@ -28,7 +28,7 @@ import { } from '@module/user_resource_permissions/utility/granular-permissios.utility'; import { GroupPermissionsUtilityService } from '@module/user_resource_permissions/services/group-permissions.utility.service'; import { GroupApps } from 'src/entities/group_apps.entity'; -import { GroupPermissions } from 'src/entities/group_permissions.entity'; +import { GroupUsers } from 'src/entities/group_users.entity'; @Injectable() export class GranularPermissionsService { @@ -75,7 +75,7 @@ export class GranularPermissionsService { return await dbTransactionWrap(async (manager: EntityManager) => { const granularPermissions = await this.get(id, manager); const { organizationId, updateGranularPermissionDto, group } = updateGranularPermissionsObj; - const { isAll, name, resourcesToAdd, resourcesToDelete, actions } = updateGranularPermissionDto; + const { isAll, name, resourcesToAdd, resourcesToDelete, actions, allowRoleChange } = updateGranularPermissionDto; const updateGranularPermission = { isAll: isAll == true ? true : false, ...(name && { name }), @@ -86,6 +86,7 @@ export class GranularPermissionsService { actions, resourcesToDelete, resourcesToAdd, + allowRoleChange, }; await catchDbException(async () => { await manager.update(GranularPermissions, id, updateGranularPermission); @@ -186,41 +187,43 @@ export class GranularPermissionsService { manager?: EntityManager ) { return await dbTransactionWrap(async (manager: EntityManager) => { - const { - granularPermissions, - actions, - resourcesToDelete, - resourcesToAdd, - group: permissionGroup, - } = UpdateResourceGroupPermissionsObject; - let group: GroupPermissions; - if (permissionGroup) { - group = permissionGroup; - } else { - group = await manager.findOne(GroupPermissions, granularPermissions.groupId); - } + const { granularPermissions, actions, resourcesToDelete, resourcesToAdd, group, allowRoleChange } = + UpdateResourceGroupPermissionsObject; + validateAppResourcePermissionUpdateOperation(group, actions); const { canEdit } = actions; - const groupEditors = await this.groupPermissionsUtilityService.getRoleUsersList( + const groupEndUsers = await this.groupPermissionsUtilityService.getRoleUsersList( USER_ROLE.END_USER, organizationId, granularPermissions.groupId, manager ); - console.log('logging resource object'); + if (groupEndUsers.length && canEdit) { + if (!allowRoleChange) { + throw new MethodNotAllowedException({ + message: { + error: ERROR_HANDLER.UPDATE_EDITABLE_PERMISSION_END_USER_GROUP, + data: groupEndUsers?.map((user) => user.email), + title: 'Cannot add this permissions to the group', + type: 'USER_ROLE_CHANGE', + }, + }); + } + await Promise.all( + groupEndUsers.map(async (userItem) => { + const currentRoleUser = userItem.userGroups[0].id; + const roleGroup = await this.groupPermissionsUtilityService.getRoleGroup( + USER_ROLE.BUILDER, + group.organizationId, + manager + ); + await manager.delete(GroupUsers, currentRoleUser); + const newUserRole = manager.create(GroupUsers, { groupId: roleGroup.id, userId: userItem.id }); + await manager.save(newUserRole); + }) + ); + } - console.log(UpdateResourceGroupPermissionsObject); - - //Resource update level - - if (groupEditors.length && canEdit) - throw new BadRequestException({ - message: { - error: ERROR_HANDLER.EDITOR_LEVEL_PERMISSIONS_NOT_ALLOWED, - data: groupEditors.map((user) => user.email), - title: 'Cannot update permissions', - }, - }); const appsGroupPermissions = await manager.findOne(AppsGroupPermissions, { where: { granularPermissionId: granularPermissions.id, diff --git a/server/src/services/group_permissions.service.v2.ts b/server/src/services/group_permissions.service.v2.ts index c6a0e109bc..dd5b33db5e 100644 --- a/server/src/services/group_permissions.service.v2.ts +++ b/server/src/services/group_permissions.service.v2.ts @@ -12,6 +12,7 @@ import { dbTransactionWrap, catchDbException } from 'src/helpers/utils.helper'; import { EntityManager, getManager } from 'typeorm'; import { CreateDefaultGroupObject, + GetGroupUsersObject, GetUsersResponse, } from '@module/user_resource_permissions/interface/group-permissions.interface'; import { GroupUsers } from 'src/entities/group_users.entity'; @@ -24,7 +25,6 @@ import { validateUpdateGroupOperation, } from '@module/user_resource_permissions/utility/group-permissions.utility'; import { GroupPermissionsUtilityService } from '@module/user_resource_permissions/services/group-permissions.utility.service'; -import { ResourceType } from '@module/user_resource_permissions/constants/granular-permissions.constant'; @Injectable() export class GroupPermissionsServiceV2 { @@ -67,47 +67,70 @@ export class GroupPermissionsServiceV2 { return response; } - async getGroup(id: string, manager?: EntityManager): Promise { + async getGroup(id: string, manager?: EntityManager): Promise<{ group: GroupPermissions; isBuilderLevel: boolean }> { return await dbTransactionWrap(async (manager: EntityManager) => { - return await manager.findOne(GroupPermissions, { - where: { id }, - }); + const [group, isBuilderLevelResourcePermissions] = await Promise.all([ + manager.findOne(GroupPermissions, { + where: { id }, + }), + await this.groupPermissionsUtilityService.checkIfBuilderLevelResourcesPermissions(id, manager), + ]); + const isBuilderLevelMainPermissions = Object.values(group).some( + (value) => typeof value === 'boolean' && value === true + ); + const isBuilderLevel = isBuilderLevelResourcePermissions || isBuilderLevelMainPermissions; + return { group, isBuilderLevel }; }, manager); } async updateGroup(id: string, updateGroupPermissionDto: UpdateGroupPermissionDto, manager?: EntityManager) { //License level validation at controller level - const group = await this.getGroup(id); - if (!group) throw new BadRequestException(ERROR_HANDLER.GROUP_NOT_EXIST); - - validateUpdateGroupOperation(group, updateGroupPermissionDto); - - const keys = Object.keys(updateGroupPermissionDto); - const validatePermissionsUpdate = !(keys.length === 1 && keys.includes('name')); - const editPermissionsPresent = Object.values(updateGroupPermissionDto).some( - (value) => typeof value === 'boolean' && value === true - ); - console.log('running till here'); - - if (validatePermissionsUpdate) { - const getEndUsersList = await this.groupPermissionsUtilityService.getRoleUsersList( - USER_ROLE.END_USER, - group.organizationId, - group.id - ); - - if (getEndUsersList.length && editPermissionsPresent) { - throw new MethodNotAllowedException({ - message: { - error: ERROR_HANDLER.UPDATE_EDITABLE_PERMISSION_END_USER_GROUP, - data: getEndUsersList?.map((user) => user.email), - title: 'Cannot add this permissions to the group', - }, - }); - } - } return await dbTransactionWrap(async (manager: EntityManager) => { + const { group } = await this.getGroup(id); + if (!group) throw new BadRequestException(ERROR_HANDLER.GROUP_NOT_EXIST); + + validateUpdateGroupOperation(group, updateGroupPermissionDto); + const { allowRoleChange } = updateGroupPermissionDto; + delete updateGroupPermissionDto.allowRoleChange; + const keys = Object.keys(updateGroupPermissionDto); + const validatePermissionsUpdate = !(keys.length === 1 && keys.includes('name')); + const editPermissionsPresent = Object.keys(updateGroupPermissionDto).some( + (value) => typeof updateGroupPermissionDto?.[value] === 'boolean' && updateGroupPermissionDto?.[value] === true + ); + if (validatePermissionsUpdate) { + const getEndUsersList = await this.groupPermissionsUtilityService.getRoleUsersList( + USER_ROLE.END_USER, + group.organizationId, + group.id + ); + + if (getEndUsersList.length && editPermissionsPresent) { + if (!allowRoleChange) { + throw new MethodNotAllowedException({ + message: { + error: ERROR_HANDLER.UPDATE_EDITABLE_PERMISSION_END_USER_GROUP, + data: getEndUsersList?.map((user) => user.email), + title: 'Cannot add this permissions to the group', + type: 'USER_ROLE_CHANGE', + }, + }); + } + await Promise.all( + getEndUsersList.map(async (userItem) => { + const currentRoleUser = userItem.userGroups[0].id; + const roleGroup = await this.groupPermissionsUtilityService.getRoleGroup( + USER_ROLE.BUILDER, + group.organizationId, + manager + ); + await this.deleteGroupUser(currentRoleUser, manager); + const newUserRole = manager.create(GroupUsers, { groupId: roleGroup.id, userId: userItem.id }); + await manager.save(newUserRole); + }) + ); + } + } return await catchDbException(async () => { await manager.update(GroupPermissions, id, updateGroupPermissionDto); }, [DATA_BASE_CONSTRAINTS.GROUP_NAME_UNIQUE]); @@ -116,7 +139,7 @@ export class GroupPermissionsServiceV2 { async deleteGroup(id: string): Promise { return await dbTransactionWrap(async (manager: EntityManager) => { - const group = await this.getGroup(id, manager); + const { group } = await this.getGroup(id, manager); if (group.type == GROUP_PERMISSIONS_TYPE.DEFAULT) throw new BadRequestException(ERROR_HANDLER.DEFAULT_GROUP_UPDATE_NOT_ALLOWED); return await manager.delete(GroupPermissions, id); @@ -124,17 +147,31 @@ export class GroupPermissionsServiceV2 { } private async createGroupUser(user: User, group: GroupPermissions, manager?: EntityManager): Promise { + console.log(group); + console.log('logging group'); + const createObjec = { + userId: user.id, + groupId: group.id, + }; + console.log(createObjec); + return await dbTransactionWrap(async (manager: EntityManager) => { return await catchDbException(async () => { - const groupUser = manager.create(GroupUsers, { groupId: group.id, userId: user.id }); + const groupUser = manager.create(GroupUsers, createObjec); + console.log(groupUser); + return await manager.save(groupUser); }, [DATA_BASE_CONSTRAINTS.GROUP_USER_UNIQUE]); }, manager); } - async getAllGroupUsers(groupId: string, searchInput?: string, manager?: EntityManager): Promise { + async getAllGroupUsers( + getGroupUsersObject: GetGroupUsersObject, + searchInput?: string, + manager?: EntityManager + ): Promise { return await dbTransactionWrap(async (manager: EntityManager) => { - return await getUserInGroupQuery(groupId, manager, searchInput).getMany(); + return await getUserInGroupQuery(getGroupUsersObject, manager, searchInput).getMany(); }, manager); } @@ -159,33 +196,44 @@ export class GroupPermissionsServiceV2 { } async addGroupUsers(addGroupUserDto: AddGroupUserDto, organizationId: string, manager?: EntityManager) { - const { userIds, groupId } = addGroupUserDto; + const { userIds, groupId, allowRoleChange } = addGroupUserDto; return await dbTransactionWrap(async (manager: EntityManager) => { - const group = await this.getGroup(groupId, manager); - const granularPermission = await this.granularPermissionsService.getAll({ groupId: group.id }, manager); + const { group, isBuilderLevel } = await this.getGroup(groupId, manager); validateAddGroupUserOperation(group); - + const editorRoleUsers = await this.groupPermissionsUtilityService.getRoleUsersList( + USER_ROLE.END_USER, + organizationId + ); + const editorUsersToBeAdded = editorRoleUsers.filter((user) => userIds.includes(user.id)); + if (isBuilderLevel && editorUsersToBeAdded.length) { + if (!allowRoleChange) { + throw new MethodNotAllowedException({ + message: { + error: ERROR_HANDLER.UPDATE_EDITABLE_PERMISSION_END_USER_GROUP, + data: editorUsersToBeAdded?.map((user) => user.email), + title: 'Cannot add this permissions to the group', + type: 'USER_ROLE_CHANGE_ADD_USERS', + }, + }); + } + await Promise.all( + editorUsersToBeAdded.map(async (userItem) => { + const currentRoleUser = userItem.userGroups[0].id; + const roleGroup = await this.groupPermissionsUtilityService.getRoleGroup( + USER_ROLE.BUILDER, + organizationId, + manager + ); + await this.deleteGroupUser(currentRoleUser, manager); + const newUserRole = manager.create(GroupUsers, { groupId: roleGroup.id, userId: userItem.id }); + await manager.save(newUserRole); + }) + ); + } return await Promise.all( userIds.map(async (userId) => { const user = await getUserDetailQuery(userId, organizationId, manager).getOne(); if (!user) throw new BadRequestException(ERROR_HANDLER.ADD_GROUP_USER_NON_EXISTING_USER); - - const role = await this.groupPermissionsUtilityService.getUserRole(userId, organizationId, manager); - const editPermissionsPresent = - Object.values(group).some((value) => typeof value === 'boolean' && value === true) || - granularPermission.some((value) => { - return value.type === ResourceType.APP && value.appsGroupPermissions.canEdit; - }); - - if (editPermissionsPresent && role.name == USER_ROLE.END_USER) { - throw new MethodNotAllowedException({ - message: { - error: ERROR_HANDLER.GROUP_USERS_EDITABLE_GROUP_ADDITION(user.email), - title: 'Can not add end user to this group', - }, - }); - } - return await this.createGroupUser(user, group, manager); }) ); diff --git a/server/src/services/organization_users.service.ts b/server/src/services/organization_users.service.ts index f3035a7107..c782dee533 100644 --- a/server/src/services/organization_users.service.ts +++ b/server/src/services/organization_users.service.ts @@ -100,6 +100,9 @@ export class OrganizationUsersService { } async updateOrgUser(organizationUserId: string, updateUserDto) { + console.log(updateUserDto); + console.log('logging this'); + const organizationUser = await this.organizationUsersRepository.findOne({ where: { id: organizationUserId } }); return await this.usersService.update( organizationUser.userId, diff --git a/server/src/services/organizations.service.ts b/server/src/services/organizations.service.ts index f85762c15c..fe89e803d2 100644 --- a/server/src/services/organizations.service.ts +++ b/server/src/services/organizations.service.ts @@ -619,7 +619,6 @@ export class OrganizationsService { userParams, currentUser.organizationId, role, - groups, user, true, defaultOrganization?.id, @@ -649,6 +648,8 @@ export class OrganizationsService { manager ); + await this.usersService.attachUserGroup(groups, currentOrganization.id, user.id, manager); + const name = fullName(currentUser.firstName, currentUser.lastName); if (shouldSendWelcomeMail) { this.emailService diff --git a/server/src/services/user-role.service.ts b/server/src/services/user-role.service.ts index 0d383c18eb..5effd8fe30 100644 --- a/server/src/services/user-role.service.ts +++ b/server/src/services/user-role.service.ts @@ -6,7 +6,6 @@ import { USER_ROLE, ERROR_HANDLER, DEFAULT_GROUP_PERMISSIONS, - GROUP_PERMISSIONS_TYPE, } from '@module/user_resource_permissions/constants/group-permissions.constant'; import { dbTransactionWrap } from 'src/helpers/utils.helper'; import { EntityManager } from 'typeorm'; @@ -72,9 +71,7 @@ export class UserRoleService { async getRoleGroup(role: USER_ROLE, organizationId: string, manager?: EntityManager) { return await dbTransactionWrap(async (manager) => { - return await manager.findOne(GroupPermissions, { - where: { name: role, organizationId, type: GROUP_PERMISSIONS_TYPE.DEFAULT }, - }); + return await this.groupPermissionsUtilityService.getRoleGroup(role, organizationId, manager); }, manager); } @@ -94,7 +91,11 @@ export class UserRoleService { throw new BadRequestException(ERROR_HANDLER.DEFAULT_GROUP_ADD_USER_ROLE_EXIST(newRole)); if (userRole.name == USER_ROLE.ADMIN) { - const groupUsers = await this.groupPermissionsService.getAllGroupUsers(userRole.id, null, manager); + const groupUsers = await this.groupPermissionsService.getAllGroupUsers( + { groupId: userRole.id, organizationId }, + null, + manager + ); if (groupUsers.length < 2) throw new BadRequestException({ message: { @@ -143,9 +144,6 @@ export class UserRoleService { const { role, userId } = addUserRoleObject; return await dbTransactionWrap(async (manager: EntityManager) => { const roleGroup = await this.getRoleGroup(role, organizationId, manager); - console.log('role group is'); - console.log(roleGroup); - const newUserRole = manager.create(GroupUsers, { groupId: roleGroup.id, userId }); await manager.save(newUserRole); }, manager); diff --git a/server/src/services/users.service.ts b/server/src/services/users.service.ts index 2522725ed6..b28ad49b84 100644 --- a/server/src/services/users.service.ts +++ b/server/src/services/users.service.ts @@ -98,7 +98,6 @@ export class UsersService { userParams: Partial, organizationId: string, role: USER_ROLE, - groups?: string[], existingUser?: User, isInvite?: boolean, defaultOrganizationId?: string, @@ -136,8 +135,6 @@ export class UsersService { ); } await this.userRoleService.addUserRole({ role, userId: user.id }, organizationId, manager); - - await this.attachUserGroup(groups, organizationId, user.id, manager); }, manager); return user; @@ -150,10 +147,21 @@ export class UsersService { manager?: EntityManager ): Promise { if (!groups) return; + console.log('Adding groups'); + console.log(groups); await dbTransactionWrap(async (manager: EntityManager) => { + if (groups?.length) + await this.groupPermissionsUtilityService.validateEditUserGroupPermissionsAddition( + { userId, groupsToAddIds: groups, organizationId }, + manager + ); await Promise.all( groups.map(async (groupId) => { - await this.groupPermissionsService.addGroupUsers({ userIds: [userId], groupId }, organizationId, manager); + await this.groupPermissionsService.addGroupUsers( + { userIds: [userId], groupId, allowRoleChange: false }, + organizationId, + manager + ); }) ); }, manager); @@ -181,6 +189,8 @@ export class UsersService { await this.removeUserGroupPermissionsIfExists(manager, user, removeGroups, organizationId); if (role) await this.userRoleService.editDefaultGroupUserRole({ userId, newRole: role }, organizationId, manager); + console.log('working till here'); + await this.attachUserGroup(addGroups, organizationId, userId, manager); return user; }, manager); @@ -195,29 +205,6 @@ export class UsersService { }, manager); } - //TODO: Remove this function if not needed - // async addUserGroupPermissions(manager: EntityManager, user: User, addGroups: string[], organizationId?: string) { - // const orgId = organizationId || user.defaultOrganizationId; - // if (addGroups) { - // const orgGroupPermissions = await this.groupPermissionsForOrganization(orgId); - - // for (const group of addGroups) { - // const orgGroupPermission = orgGroupPermissions.find((permission) => permission.group == group); - - // if (!orgGroupPermission) { - // throw new BadRequestException(`${group} group does not exist for current organization`); - // } - // await dbTransactionWrap(async (manager: EntityManager) => { - // const userGroupPermission = manager.create(UserGroupPermission, { - // groupPermissionId: orgGroupPermission.id, - // userId: user.id, - // }); - // await manager.save(userGroupPermission); - // }, manager); - // } - // } - // } - async removeUserGroupPermissionsIfExists( manager: EntityManager, user: User,