From 8426e2248ea3b5b52472d933088b80878c9dbb09 Mon Sep 17 00:00:00 2001 From: kriks7iitk Date: Sun, 7 Jul 2024 13:59:29 +0530 Subject: [PATCH] Modify bulk upload, inspector and design fix --- frontend/src/Editor/Editor.jsx | 7 +- frontend/src/Editor/Viewer.jsx | 14 ++-- .../AppResourcePermission.jsx | 1 - frontend/src/ManageGranularAccess/index.jsx | 9 --- .../ManageGroupPermissionResources.jsx | 2 - .../index.jsx | 13 +--- .../ManageGroupPermissionsV2.jsx | 3 - .../src/ManageOrgUsers/InviteUsersForm.jsx | 8 ++- .../src/ManageOrgUsers/ManageOrgUsers.jsx | 1 - frontend/src/_components/LogoNavDropdown.jsx | 21 +++--- .../src/_services/authentication.service.js | 1 + .../_services/organization_user.service.js | 2 - frontend/src/_styles/theme.scss | 10 +++ frontend/src/_ui/Modal/AppsSelect.jsx | 5 -- .../casl/abilities/apps-ability.factory.ts | 28 ++++---- .../utility/group-permissions.utility.ts | 3 - server/src/services/auth.service.ts | 11 ++- server/src/services/organizations.service.ts | 72 +++++++++++-------- .../services/permissions-ability.service.ts | 2 - 19 files changed, 106 insertions(+), 107 deletions(-) diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx index 743cfc9043..314c259b69 100644 --- a/frontend/src/Editor/Editor.jsx +++ b/frontend/src/Editor/Editor.jsx @@ -223,12 +223,15 @@ const EditorComponent = (props) => { // Subscribe to changes in the current session using RxJS observable pattern const subscription = authenticationService.currentSession.subscribe((currentSession) => { - if (currentUser && currentSession?.group_permissions) { + if (currentUser && (currentSession?.group_permissions || currentSession?.role)) { const userVars = { email: currentUser.email, firstName: currentUser.first_name, lastName: currentUser.last_name, - groups: currentSession.group_permissions?.map((group) => group.name), + groups: currentSession?.group_permissions + ? ['all_users', ...currentSession.group_permissions.map((group) => group.name)] + : ['all_users'], + role: currentSession?.role?.name, }; const appUserDetails = { diff --git a/frontend/src/Editor/Viewer.jsx b/frontend/src/Editor/Viewer.jsx index a1b608669b..bb2a81b19f 100644 --- a/frontend/src/Editor/Viewer.jsx +++ b/frontend/src/Editor/Viewer.jsx @@ -280,13 +280,16 @@ class ViewerComponent extends React.Component { const currentUser = this.state.currentUser; let userVars = {}; - + const currentSessionValue = authenticationService.currentSessionValue; if (currentUser) { userVars = { email: currentUser.email, firstName: currentUser.first_name, lastName: currentUser.last_name, - groups: authenticationService.currentSessionValue?.group_permissions.map((group) => group.name), + groups: currentSessionValue?.group_permissions + ? ['All Users', ...currentSessionValue.group_permissions.map((group) => group.name)] + : ['All Users'], + role: currentSessionValue?.role?.name, }; } @@ -550,15 +553,18 @@ class ViewerComponent extends React.Component { const versionId = this.props.versionId; if (currentSession?.load_app && slug) { - if (currentSession?.group_permissions) { + if (currentSession?.group_permissions || currentSession?.role) { useAppDataStore.getState().actions.setAppId(appId); const currentUser = currentSession.current_user; + const currentSessionValue = authenticationService.currentSessionValue; const userVars = { email: currentUser.email, firstName: currentUser.first_name, lastName: currentUser.last_name, - groups: currentSession?.group_permissions?.map((group) => group.name), + groups: currentSessionValue?.group_permissions + ? ['All Users', ...currentSessionValue.group_permissions.map((group) => group.name)] + : ['All Users'], }; this.props.setCurrentState({ globals: { diff --git a/frontend/src/ManageGranularAccess/AppResourcePermission.jsx b/frontend/src/ManageGranularAccess/AppResourcePermission.jsx index 4e8028c7ad..82c193e861 100644 --- a/frontend/src/ManageGranularAccess/AppResourcePermission.jsx +++ b/frontend/src/ManageGranularAccess/AppResourcePermission.jsx @@ -30,7 +30,6 @@ function AppResourcePermissions({ setHover(false); }} onClick={() => { - if (notClickable) console.log('this is runing'); !isRoleGroup && !notClickable && openEditPermissionModal(permissions); }} > diff --git a/frontend/src/ManageGranularAccess/index.jsx b/frontend/src/ManageGranularAccess/index.jsx index 72f1f29df9..40f42dd527 100644 --- a/frontend/src/ManageGranularAccess/index.jsx +++ b/frontend/src/ManageGranularAccess/index.jsx @@ -49,7 +49,6 @@ class ManageGranularAccessComponent extends React.Component { } componentDidMount() { - console.log('addable apps are'); this.fetchAppsCanBeAdded(); this.fetchGranularPermissions(this.props.groupPermissionId); } @@ -58,8 +57,6 @@ class ManageGranularAccessComponent extends React.Component { groupPermissionV2Service .fetchAddableApps() .then((data) => { - console.log('fetching app'); - console.log(data); const addableApps = data.map((app) => { return { name: app.name, @@ -67,7 +64,6 @@ class ManageGranularAccessComponent extends React.Component { label: app.name, }; }); - console.log(addableApps); this.setState({ addableApps, }); @@ -232,8 +228,6 @@ class ManageGranularAccessComponent extends React.Component { const groupAppsToDelete = currentEditingPermissions?.appsGroupPermissions?.groupApps?.filter((groupApp) => appsToDelete?.includes(groupApp.appId) ); - console.log('logging groups apps to delete'); - console.log(groupAppsToDelete); const resourcesToDelete = groupAppsToDelete?.map(({ id }) => { return { id: id, @@ -320,8 +314,6 @@ class ManageGranularAccessComponent extends React.Component { }; setSelectedApps = (values) => { - console.log('Logging selected values'); - console.log(values); this.setState({ selectedApps: values }); }; @@ -338,7 +330,6 @@ class ManageGranularAccessComponent extends React.Component { }); }; handleConfirmAutoRoleChangeGroupUpdate = () => { - console.log('this is running'); this.updateGranularPermissions(true); this.handleAutoRoleChangeModalClose(); }; diff --git a/frontend/src/ManageGroupPermissionResources/ManageGroupPermissionResources.jsx b/frontend/src/ManageGroupPermissionResources/ManageGroupPermissionResources.jsx index f120db86a0..906517fe1f 100644 --- a/frontend/src/ManageGroupPermissionResources/ManageGroupPermissionResources.jsx +++ b/frontend/src/ManageGroupPermissionResources/ManageGroupPermissionResources.jsx @@ -73,8 +73,6 @@ class ManageGroupPermissionResourcesComponent extends React.Component { groupPermissionService .getUsersNotInGroup(query, groupPermissionId) .then(({ users }) => { - console.log('logging users'); - console.log(users); resolve( users.map((user) => { return { diff --git a/frontend/src/ManageGroupPermissionResourcesV2/index.jsx b/frontend/src/ManageGroupPermissionResourcesV2/index.jsx index 28a7986d7d..6547303f4a 100644 --- a/frontend/src/ManageGroupPermissionResourcesV2/index.jsx +++ b/frontend/src/ManageGroupPermissionResourcesV2/index.jsx @@ -102,8 +102,6 @@ class ManageGroupPermissionResourcesComponent extends React.Component { groupPermissionV2Service .getUsersNotInGroup(query, groupPermissionId) .then((users) => { - console.log('loggimgusers'); - console.log(users); resolve( users.map((user) => { return { @@ -152,7 +150,6 @@ class ManageGroupPermissionResourcesComponent extends React.Component { this.fetchGroupPermission(groupPermissionId); }) .catch(({ error }) => { - console.log(error); if (error?.type) { this.setState({ showAutoRoleChangeModal: true, @@ -173,8 +170,6 @@ class ManageGroupPermissionResourcesComponent extends React.Component { }; setSelectedUsers = (value) => { - console.log('user value'); - console.log(value); this.setState({ selectedUsers: value, }); @@ -187,8 +182,6 @@ class ManageGroupPermissionResourcesComponent extends React.Component { }; addSelectedUsersToGroup = (groupPermissionId, selectedUsers, allowRoleChange) => { - console.log('selected users'); - console.log(selectedUsers); this.setState({ isAddingUsers: true }); const body = { userIds: selectedUsers.map((user) => user.value), @@ -198,7 +191,6 @@ class ManageGroupPermissionResourcesComponent extends React.Component { groupPermissionV2Service .addUsersInGroups(body) .then(() => { - console.log('this is running'); this.setState({ selectedUsers: [], isLoadingUsers: true, @@ -320,7 +312,6 @@ class ManageGroupPermissionResourcesComponent extends React.Component { }); changeThisComponentState = (state = {}) => { - console.log('this is changing'); this.setState(state); }; @@ -390,7 +381,6 @@ class ManageGroupPermissionResourcesComponent extends React.Component { 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({ @@ -703,7 +693,7 @@ class ManageGroupPermissionResourcesComponent extends React.Component { )} -
+
{isLoadingGroup || isLoadingUsers ? ( @@ -722,7 +712,6 @@ class ManageGroupPermissionResourcesComponent extends React.Component { usersInGroup.map((item) => { const user = item.user; const groupUserId = item.id; - console.log(user); return (
{ const groupPermissions = data.groupPermissions; const defaultGroups = this.sortDefaultGroup(groupPermissions.filter((group) => group.type === 'default')); - console.log('logging group'); - const currentGroupId = type == 'admin' ? defaultGroups[0].id : type == 'current' ? this.findCurrentGroupDetails(groupPermissions) : groupPermissions.at(-1).id; - console.log(groupPermissions.find((group) => group.id === currentGroupId)); this.setState( { groups: groupPermissions.filter((group) => group.type === 'custom'), diff --git a/frontend/src/ManageOrgUsers/InviteUsersForm.jsx b/frontend/src/ManageOrgUsers/InviteUsersForm.jsx index 15c474931d..ccbb0cec99 100644 --- a/frontend/src/ManageOrgUsers/InviteUsersForm.jsx +++ b/frontend/src/ManageOrgUsers/InviteUsersForm.jsx @@ -120,8 +120,6 @@ function InviteUsersForm({ }; const handleEditUser = (e) => { - console.log(currentEditingUser); - console.log(newRole); e.preventDefault(); if (newRole) setIsChangeRoleModalOpen(true); else { @@ -354,7 +352,11 @@ function InviteUsersForm({ type="submit" variant="primary" disabled={ - uploadingUsers || creatingUser || !isEdited() || (!isEditing && !containRoleGroup) || !validUserDetail + uploadingUsers || + creatingUser || + !isEdited() || + (!isEditing && !containRoleGroup && uploadingUsers) || + (!isEditing && !validUserDetail && uploadingUsers) } data-cy={activeTab == 1 ? 'button-invite-users' : 'button-upload-users'} leftIcon={activeTab == 1 ? 'sent' : 'fileupload'} diff --git a/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx b/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx index 6f69ce0233..882f7a6624 100644 --- a/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx +++ b/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx @@ -79,7 +79,6 @@ class ManageOrgUsersComponent extends React.Component { if (!this.state.file) { errors['file'] = 'This field is required'; } - this.setState({ errors: errors }); return Object.keys(errors).length === 0; } diff --git a/frontend/src/_components/LogoNavDropdown.jsx b/frontend/src/_components/LogoNavDropdown.jsx index c5c2e6adcd..f512e41750 100644 --- a/frontend/src/_components/LogoNavDropdown.jsx +++ b/frontend/src/_components/LogoNavDropdown.jsx @@ -38,16 +38,17 @@ export default function LogoNavDropdown({ darkMode }) { Database )} - - - Data sources - - + {admin && ( + + + Data sources + + )} { return ( @@ -182,8 +179,6 @@ export function AppsSelect(props) { isSelectAllPresentInSelection && !isCurrentSelectAll ) { - console.log('value'); - console.log(props.value); if (props.value.find((app) => app?.isAllField)?.isAllField) props.onChange(selected.filter((app) => !app?.isAllField)); return props.onChange([...props.options, props.allOption]); diff --git a/server/src/modules/casl/abilities/apps-ability.factory.ts b/server/src/modules/casl/abilities/apps-ability.factory.ts index 45f68826a4..e58ff4f739 100644 --- a/server/src/modules/casl/abilities/apps-ability.factory.ts +++ b/server/src/modules/casl/abilities/apps-ability.factory.ts @@ -8,19 +8,19 @@ import { APP_RESOURCE_ACTIONS, TOOLJET_RESOURCE } from 'src/constants/global.con type Actions = | 'authorizeOauthForSource' //Deprecated - | APP_RESOURCE_ACTIONS.CLONE // - | APP_RESOURCE_ACTIONS.IMPORT // - | APP_RESOURCE_ACTIONS.CREATE // - | 'createDataSource' // - | 'createQuery' // - | 'createUsers' // - | APP_RESOURCE_ACTIONS.VERSIONS_CREATE // - | APP_RESOURCE_ACTIONS.VERSION_DELETE // - | 'deleteApp' // - | 'deleteDataSource' // - | 'deleteQuery' // - | 'fetchUsers' // - | APP_RESOURCE_ACTIONS.VERSION_READ // + | APP_RESOURCE_ACTIONS.CLONE + | APP_RESOURCE_ACTIONS.IMPORT + | APP_RESOURCE_ACTIONS.CREATE + | 'createDataSource' + | 'createQuery' + | 'createUsers' + | APP_RESOURCE_ACTIONS.VERSIONS_CREATE + | APP_RESOURCE_ACTIONS.VERSION_DELETE + | 'deleteApp' + | 'deleteDataSource' + | 'deleteQuery' + | 'fetchUsers' + | APP_RESOURCE_ACTIONS.VERSION_READ | 'getDataSources' | 'getQueries' | 'previewQuery' @@ -71,8 +71,8 @@ export class AppsAbilityFactory { if (userPermission.appCreate) { can(APP_RESOURCE_ACTIONS.CREATE, App); can(APP_RESOURCE_ACTIONS.IMPORT, App); + can(APP_RESOURCE_ACTIONS.EXPORT, App); if (appUpdateAllowed) { - can(APP_RESOURCE_ACTIONS.EXPORT, App); can(APP_RESOURCE_ACTIONS.CLONE, App); } } 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 ad89a6f590..4976fe9072 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 @@ -121,9 +121,6 @@ export function getAllUserGroupsQuery( }) .where('groupUsers.userId = :userId', { userId, - }) - .andWhere('groups.type = :type', { - type: GROUP_PERMISSIONS_TYPE.CUSTOM_GROUP, }); return query; } diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index deac059392..a50892830f 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -46,7 +46,10 @@ import { CookieOptions, Response } from 'express'; import { SessionService } from './session.service'; import { RequestContext } from 'src/models/request-context.model'; import * as requestIp from 'request-ip'; -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 { ActivateAccountWithTokenDto } from '@dto/activate-account-with-token.dto'; import { AppAuthenticationDto, AppSignupDto } from '@dto/app-authentication.dto'; import { SIGNUP_ERRORS } from 'src/helpers/errors.constants'; @@ -244,11 +247,13 @@ export class AuthService { return decamelizeKeys({ currentOrganizationId: user.organizationId, currentOrganizationSlug: organization.slug, - //Check this for permissions......... currentOrganizationName: organization.name, admin: await this.usersService.hasGroup(user, USER_ROLE.ADMIN, null, manager), userPermissions: userPermissions, - groupPermissions: permissions, + groupPermissions: permissions.filter( + (group) => group.type === GROUP_PERMISSIONS_TYPE.CUSTOM_GROUP || group.name === USER_ROLE.ADMIN + ), + role: permissions.find((group) => group.type === GROUP_PERMISSIONS_TYPE.DEFAULT), appGroupPermissions: appGroupPermissions, currentUser: { id: user.id, diff --git a/server/src/services/organizations.service.ts b/server/src/services/organizations.service.ts index 0816db8282..ca3b37f8a3 100644 --- a/server/src/services/organizations.service.ts +++ b/server/src/services/organizations.service.ts @@ -16,7 +16,6 @@ import { Brackets, createQueryBuilder, DeepPartial, EntityManager, getManager, R import { OrganizationUser } from '../entities/organization_user.entity'; import { EmailService } from './email.service'; import { EncryptionService } from './encryption.service'; -import { GroupPermissionsService } from './group_permissions.service'; import { OrganizationUsersService } from './organization_users.service'; import { DataSourcesService } from './data_sources.service'; import { UsersService } from './users.service'; @@ -29,7 +28,6 @@ import { USER_STATUS, WORKSPACE_USER_STATUS, } from 'src/helpers/user_lifecycle'; -import { decamelize } from 'humps'; import { Response } from 'express'; import { AppEnvironmentService } from './app_environments.service'; import { DataBaseConstraints } from 'src/helpers/db_constraints.constants'; @@ -44,6 +42,7 @@ import { DataSource } from 'src/entities/data_source.entity'; import { AppEnvironment } from 'src/entities/app_environments.entity'; import { DataSourceOptions } from 'src/entities/data_source_options.entity'; import { ERROR_HANDLER, ERROR_HANDLER_TITLE } from '@module/organizations/constant/constants'; +import { GroupPermissionsServiceV2 } from './group_permissions.service.v2'; const MAX_ROW_COUNT = 500; @@ -64,6 +63,7 @@ interface UserCsvRow { first_name: string; last_name: string; email: string; + role: string; groups?: any; } @@ -88,7 +88,7 @@ export class OrganizationsService { private usersService: UsersService, private dataSourceService: DataSourcesService, private organizationUserService: OrganizationUsersService, - private groupPermissionService: GroupPermissionsService, + private groupPermissionService: GroupPermissionsServiceV2, private appEnvironmentService: AppEnvironmentService, private encryptionService: EncryptionService, private emailService: EmailService, @@ -671,14 +671,8 @@ export class OrganizationsService { }, manager); } - decamelizeDefaultGroupNames(groups: string) { - return groups?.length - ? groups - .split('|') - .map((group: string) => - group === 'All Users' || group === 'Admin' ? decamelize(group.replace(' ', '')) : group - ) - : []; + createGroupsList(groups: string) { + return groups?.length ? groups.split('|') : []; } async inviteUserswrapper(users, currentUser: User): Promise { @@ -689,6 +683,19 @@ export class OrganizationsService { }); } + convertUserRolesCasing(role: string) { + switch (role) { + case 'End User': + return 'end-user'; + case 'Builder': + return 'builder'; + case 'Admin': + return 'admin'; + default: + break; + } + } + async bulkUploadUsers(currentUser: User, fileStream, res: Response) { const users = []; const existingUsers = []; @@ -696,14 +703,12 @@ export class OrganizationsService { const invalidRows = []; const invalidFields = new Set(); const invalidGroups = []; - let isUserInOtherGroupsAndAdmin = false; const emailPattern = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i; const manager = getManager(); - - //New group permissions change - const groupPermissions = await this.groupPermissionService.findAll(currentUser); - const existingGroups = groupPermissions.map((groupPermission) => groupPermission.group); - + const invalidRoles = []; + const groupPermissions = (await this.groupPermissionService.getAllGroup(currentUser.organizationId)) + .groupPermissions; + const existingGroups = groupPermissions.map((groupPermission) => groupPermission.name); csv .parseString(fileStream.toString(), { headers: ['first_name', 'last_name', 'email', 'groups', 'role'], @@ -713,13 +718,18 @@ export class OrganizationsService { .transform((row: UserCsvRow, next) => { return next(null, { ...row, - groups: this.decamelizeDefaultGroupNames(row?.groups), + groups: this.createGroupsList(row?.groups), + role: this.convertUserRolesCasing(row?.role), }); }) .validate(async (data: UserCsvRow, next) => { await dbTransactionWrap(async (manager: EntityManager) => { //Check for existing users + + let isInvalidRole = false; const user = await this.usersService.findByEmail(data?.email, undefined, undefined, manager); + console.log(data); + console.log('loggin row'); if (user?.status === USER_STATUS.ARCHIVED) { archivedUsers.push(data?.email); @@ -734,27 +744,29 @@ export class OrganizationsService { if (Array.isArray(receivedGroups)) { for (const group of receivedGroups) { - if (group === 'admin' && receivedGroups.includes('all_users') && receivedGroups.length > 2) { - isUserInOtherGroupsAndAdmin = true; - break; - } - if (existingGroups.indexOf(group) === -1) { invalidGroups.push(group); } } } + if (!Object.values(USER_ROLE).includes(data?.role as USER_ROLE)) { + invalidRoles.push(data?.role); + isInvalidRole = true; + } + data.first_name = data.first_name?.trim(); data.last_name = data.last_name?.trim(); const isValidName = data.first_name !== '' || data.last_name !== ''; - - return next(null, isValidName && emailPattern.test(data.email) && receivedGroups?.length > 0); + return next(null, isValidName && emailPattern.test(data.email) && !isInvalidRole); }, manager); }) .on('data', function () {}) .on('data-invalid', (row, rowNumber) => { + console.log('invalid row'); + console.log(row); + const invalidField = Object.keys(row).filter((key) => { if (Array.isArray(row[key])) { return row[key].length === 0; @@ -778,18 +790,16 @@ export class OrganizationsService { throw new BadRequestException(errorMsg); } - if (isUserInOtherGroupsAndAdmin) { - throw new BadRequestException( - 'Conflicting Group Memberships: User cannot be in both the Admin group and other groups simultaneously.' - ); - } - if (invalidGroups.length) { throw new BadRequestException( `${invalidGroups.length} group${isPlural(invalidGroups)} doesn't exist. No users were uploaded` ); } + if (invalidRoles.length > 0) { + throw new BadRequestException('Invalid role present for the users'); + } + if (archivedUsers.length) { throw new BadRequestException( `User${isPlural(archivedUsers)} with email ${archivedUsers.join( diff --git a/server/src/services/permissions-ability.service.ts b/server/src/services/permissions-ability.service.ts index 25ea2ecb59..6e92e7988d 100644 --- a/server/src/services/permissions-ability.service.ts +++ b/server/src/services/permissions-ability.service.ts @@ -37,11 +37,9 @@ export class AbilityService { resourcePermissionsObject: ResourcePermissionQueryObject ): Promise { const permissions = await this.getResourcePermission(user, resourcePermissionsObject); - console.log('loggin permissions'); const adminGroup = permissions.find((group) => group.name === USER_ROLE.ADMIN); const appsGranularPermissions = permissions.flatMap((item) => item.groupGranularPermissions); - console.log(appsGranularPermissions); const userPermissions: UserPermissions = permissions.reduce((acc, group) => { return { isAdmin: !!adminGroup,