diff --git a/frontend/assets/csv/sample_upload.csv b/frontend/assets/csv/sample_upload.csv
index b92715cca1..cd152f5452 100644
--- a/frontend/assets/csv/sample_upload.csv
+++ b/frontend/assets/csv/sample_upload.csv
@@ -1,2 +1,2 @@
-First Name,Last Name,Email,Group
-test,user,test@gmail.com,For multiple groups separate using pipe (|) operator e.g. All Users|Admin
\ No newline at end of file
+First Name,Last Name,Email,Group,User Role
+test,user,test@gmail.com,"For multiple groups separate using pipe (|) operator e.g. Groups1|Group2 or leave blank if no group assign","Assign each user a role: Admin, Builder or End User. User role value should be exact same"
\ No newline at end of file
diff --git a/frontend/src/ManageGranularAccess/AddEditResourceModal/AddEditResourcePermissionsModal.jsx b/frontend/src/ManageGranularAccess/AddEditResourceModal/AddEditResourcePermissionsModal.jsx
index 3424779e03..3b7479032c 100644
--- a/frontend/src/ManageGranularAccess/AddEditResourceModal/AddEditResourcePermissionsModal.jsx
+++ b/frontend/src/ManageGranularAccess/AddEditResourceModal/AddEditResourcePermissionsModal.jsx
@@ -104,7 +104,7 @@ function AddEditResourcePermissionsModal({
type="radio"
checked={isAll}
onClick={() => {
- updateParentState((prevState) => ({ isAll: !prevState.isAll, isCustom: !!prevState.isAll }));
+ !isAll && updateParentState((prevState) => ({ isAll: !prevState.isAll, isCustom: !!prevState.isAll }));
}}
/>
@@ -121,7 +121,8 @@ function AddEditResourcePermissionsModal({
disabled={addableApps.length === 0 || disableBuilderLevelUpdate}
checked={isCustom}
onClick={() => {
- updateParentState((prevState) => ({ isCustom: !prevState.isCustom, isAll: prevState.isCustom }));
+ !isCustom &&
+ updateParentState((prevState) => ({ isCustom: !prevState.isCustom, isAll: prevState.isCustom }));
}}
/>
diff --git a/frontend/src/ManageGranularAccess/AppResourcePermission.jsx b/frontend/src/ManageGranularAccess/AppResourcePermission.jsx
index 0c93d0b70c..0b311e64a8 100644
--- a/frontend/src/ManageGranularAccess/AppResourcePermission.jsx
+++ b/frontend/src/ManageGranularAccess/AppResourcePermission.jsx
@@ -35,7 +35,7 @@ function AppResourcePermissions({
>
-
{permissions.name}
+
{` ${permissions.name}`}
diff --git a/frontend/src/ManageGroupPermissionResourcesV2/index.jsx b/frontend/src/ManageGroupPermissionResourcesV2/index.jsx
index f996216af8..3305097887 100644
--- a/frontend/src/ManageGroupPermissionResourcesV2/index.jsx
+++ b/frontend/src/ManageGroupPermissionResourcesV2/index.jsx
@@ -555,7 +555,10 @@ class ManageGroupPermissionResourcesComponent extends React.Component {
this.setState({ currentTab: 'users', showUserSearchBox: false })}
+ onClick={() => {
+ this.setState({ currentTab: 'granularAccess', showUserSearchBox: false });
+ this.setSelectedUsers([]);
+ }}
className={cx('nav-item nav-link', { active: currentTab === 'users' })}
data-cy="users-link"
>
@@ -570,7 +573,10 @@ class ManageGroupPermissionResourcesComponent extends React.Component {
this.setState({ currentTab: 'permissions', showUserSearchBox: false })}
+ onClick={() => {
+ this.setState({ currentTab: 'granularAccess', showUserSearchBox: false });
+ this.setSelectedUsers([]);
+ }}
className={cx('nav-item nav-link', { active: currentTab === 'permissions' })}
data-cy="permissions-link"
>
@@ -587,7 +593,10 @@ class ManageGroupPermissionResourcesComponent extends React.Component {
)}
this.setState({ currentTab: 'granularAccess', showUserSearchBox: false })}
+ onClick={() => {
+ this.setState({ currentTab: 'granularAccess', showUserSearchBox: false });
+ this.setSelectedUsers([]);
+ }}
className={cx('nav-item nav-link', { active: currentTab === 'granularAccess' })}
data-cy="granular-access-link"
>
diff --git a/frontend/src/ManageGroupPermissionsV2/ManageGroupPermissionsV2.jsx b/frontend/src/ManageGroupPermissionsV2/ManageGroupPermissionsV2.jsx
index 170e2f8602..94bf53bbe8 100644
--- a/frontend/src/ManageGroupPermissionsV2/ManageGroupPermissionsV2.jsx
+++ b/frontend/src/ManageGroupPermissionsV2/ManageGroupPermissionsV2.jsx
@@ -507,7 +507,6 @@ class ManageGroupPermissionsComponent extends React.Component {
)}
-
@@ -589,16 +588,90 @@ class ManageGroupPermissionsComponent extends React.Component {
-
- {!showNewGroupForm && !showGroupNameUpdateForm && (
-
-
-
+
+
+
+
+
+ USER ROLE
+
+ {defaultGroups.map((permissionGroup) => {
+ return (
+
{
+ this.setState({
+ selectedGroupPermissionId: permissionGroup.id,
+ selectedGroup: this.humanizeifDefaultGroupName(permissionGroup.name),
+ selectedGroupObject: permissionGroup,
+ });
+ }}
+ toolTipText={this.humanizeifDefaultGroupName(permissionGroup.name)}
+ overLayComponent={this.renderPopoverContent}
+ className="groups-folder-list"
+ dataCy={this.humanizeifDefaultGroupName(permissionGroup.name)
+ .toLowerCase()
+ .replace(/\s+/g, '-')}
+ >
+
+ {this.humanizeifDefaultGroupName(permissionGroup.name)}
+
+
+ );
+ })}
+
+
+ {!showGroupSearchBar ? (
-
-
USER ROLE
+
+
CUSTOM GROUPS
+
+ {
+ e.preventDefault();
+ this.setState({ showGroupSearchBar: true });
+ }}
+ size="xsm"
+ rightIcon="search"
+ iconWidth="15"
+ fill="#889096"
+ className="create-group-custom"
+ />
+ {
+ e.preventDefault();
+ this.setState({ newGroupName: null, showNewGroupForm: true, isSaveBtnDisabled: true });
+ }}
+ size="sm"
+ fill="#889096"
+ rightIcon="plus"
+ iconWidth="20"
+ className="create-group-custom"
+ />
+
- {defaultGroups.map((permissionGroup) => {
+ ) : (
+
+
+
+ )}
+
+ {groups.length ? (
+ filteredGroup.map((permissionGroup) => {
return (
);
- })}
-
-
- {!showGroupSearchBar ? (
-
-
-
CUSTOM GROUPS
-
- {
- e.preventDefault();
- this.setState({ showGroupSearchBar: true });
- }}
- size="xsm"
- rightIcon="search"
- iconWidth="15"
- fill="#889096"
- className="create-group-custom"
- />
- {
- e.preventDefault();
- this.setState({ newGroupName: null, showNewGroupForm: true, isSaveBtnDisabled: true });
- }}
- size="sm"
- fill="#889096"
- rightIcon="plus"
- iconWidth="20"
- className="create-group-custom"
- />
-
-
- ) : (
-
-
-
- )}
-
- {groups.length ? (
- filteredGroup.map((permissionGroup) => {
- return (
-
{
- this.setState({
- selectedGroupPermissionId: permissionGroup.id,
- selectedGroup: this.humanizeifDefaultGroupName(permissionGroup.name),
- selectedGroupObject: permissionGroup,
- });
- }}
- toolTipText={this.humanizeifDefaultGroupName(permissionGroup.name)}
- overLayComponent={this.renderPopoverContent}
- className="groups-folder-list"
- dataCy={this.humanizeifDefaultGroupName(permissionGroup.name)
- .toLowerCase()
- .replace(/\s+/g, '-')}
- >
-
- {this.humanizeifDefaultGroupName(permissionGroup.name)}
-
-
- );
- })
- ) : (
-
-
- No custom groups added
-
- )}
-
-
-
-
- {isLoading ? (
-
+ })
) : (
-
{
- return {
- name: this.humanizeifDefaultGroupName(group.name),
- value: group.name,
- };
- })}
- />
+
+
+ No custom groups added
+
)}
- )}
+
+
+ {isLoading ? (
+
+ ) : (
+ {
+ return {
+ name: this.humanizeifDefaultGroupName(group.name),
+ value: group.name,
+ };
+ })}
+ />
+ )}
+
+
diff --git a/frontend/src/ManageGroupPermissionsV2/groupPermissions.theme.scss b/frontend/src/ManageGroupPermissionsV2/groupPermissions.theme.scss
index 0ad36a1d01..1a6a4fd1cb 100644
--- a/frontend/src/ManageGroupPermissionsV2/groupPermissions.theme.scss
+++ b/frontend/src/ManageGroupPermissionsV2/groupPermissions.theme.scss
@@ -430,6 +430,7 @@
}
.resource-text {
margin-left: 10px;
+ padding-left: 20px;
}
}
}
diff --git a/server/data-migrations/1720352990850-CreateDefaultGroupInExistingWorkspace.ts b/server/data-migrations/1720352990850-CreateDefaultGroupInExistingWorkspace.ts
new file mode 100644
index 0000000000..01624e6946
--- /dev/null
+++ b/server/data-migrations/1720352990850-CreateDefaultGroupInExistingWorkspace.ts
@@ -0,0 +1,149 @@
+import { CreateGranularPermissionDto } from '@dto/granular-permissions.dto';
+import {
+ DEFAULT_GRANULAR_PERMISSIONS_NAME,
+ DEFAULT_RESOURCE_PERMISSIONS,
+ ResourceType,
+} from '@module/user_resource_permissions/constants/granular-permissions.constant';
+import {
+ USER_ROLE,
+ DEFAULT_GROUP_PERMISSIONS_MIGRATIONS,
+} from '@module/user_resource_permissions/constants/group-permissions.constant';
+import {
+ CreateResourcePermissionObject,
+ ResourcePermissionMetaData,
+} from '@module/user_resource_permissions/interface/granular-permissions.interface';
+import { AppsGroupPermissions } from 'src/entities/apps_group_permissions.entity';
+import { GranularPermissions } from 'src/entities/granular_permissions.entity';
+import { GroupPermissions } from 'src/entities/group_permissions.entity';
+import { Organization } from 'src/entities/organization.entity';
+import { UserGroupPermission } from 'src/entities/user_group_permission.entity';
+import { EntityManager, MigrationInterface, QueryRunner } from 'typeorm';
+
+export class CreateDefaultGroupInExistingWorkspace1720352990850 implements MigrationInterface {
+ public async up(queryRunner: QueryRunner): Promise
{
+ const manager = queryRunner.manager;
+ const organizationIds = (
+ await manager.find(Organization, {
+ select: ['id'],
+ })
+ ).map((organization) => organization.id);
+
+ for (const organizationId of organizationIds) {
+ for (const defaultGroup of Object.keys(USER_ROLE)) {
+ const groupPermissions = DEFAULT_GROUP_PERMISSIONS_MIGRATIONS[defaultGroup];
+ const query = `
+ INSERT INTO permission_groups (
+ organization_id,
+ name,
+ type,
+ app_create,
+ app_delete,
+ folder_crud,
+ org_constant_crud,
+ data_source_create,
+ data_source_delete
+ ) VALUES (
+ '${organizationId}',
+ '${groupPermissions.name}',
+ '${groupPermissions.type}',
+ ${groupPermissions.appCreate},
+ ${groupPermissions.appDelete},
+ ${groupPermissions.folderCRUD},
+ ${groupPermissions.orgConstantCRUD},
+ ${groupPermissions.dataSourceCreate},
+ ${groupPermissions.dataSourceDelete}
+ ) RETURNING *;
+ `;
+ const group: GroupPermissions = (await manager.query(query))[0];
+ const groupGranularPermissions: Record =
+ DEFAULT_RESOURCE_PERMISSIONS[group.name];
+
+ for (const resource of Object.keys(groupGranularPermissions)) {
+ const createResourcePermissionObj: CreateResourcePermissionObject = groupGranularPermissions[resource];
+ const dtoObject = {
+ name: DEFAULT_GRANULAR_PERMISSIONS_NAME[resource],
+ groupId: group.id,
+ type: resource as ResourceType,
+ isAll: true,
+ createAppsPermissionsObject: {},
+ };
+ if (group.name === USER_ROLE.ADMIN) {
+ const granularPermissions = await this.createGranularPermission(manager, dtoObject);
+ await this.createAppsResourcePermission(
+ manager,
+ { granularPermissions, organizationId },
+ createResourcePermissionObj
+ );
+ }
+ }
+ //Migrating Admins to new Admins
+ if (group.name === USER_ROLE.ADMIN) {
+ const adminsUsers = await manager
+ .createQueryBuilder(UserGroupPermission, 'usersGroup')
+ .innerJoin(
+ 'usersGroup.groupPermission',
+ 'groupPermission',
+ 'groupPermission.organizationId = :organizationId',
+ {
+ organizationId,
+ }
+ )
+ .where('groupPermission.group = :admin', {
+ admin: 'admin',
+ })
+ .getMany();
+ const userIds = adminsUsers.map((userGroup) => userGroup.userId);
+ await this.migrateUserGroup(manager, userIds, group.id);
+ }
+ }
+ }
+ }
+
+ async createGranularPermission(
+ manager: EntityManager,
+ createObject: CreateGranularPermissionDto
+ ): Promise {
+ const query = `
+ INSERT INTO granular_permissions (
+ group_id,
+ name,
+ type,
+ is_all
+ ) VALUES (
+ '${createObject.groupId}', '${createObject.name}', '${createObject.type}', ${createObject.isAll}
+ ) RETURNING *;
+ `;
+ return (await manager.query(query))[0];
+ }
+
+ async createAppsResourcePermission(
+ manager: EntityManager,
+ createMeta: ResourcePermissionMetaData,
+ createObject: CreateResourcePermissionObject
+ ): Promise {
+ const { granularPermissions } = createMeta;
+ const query = `
+ INSERT INTO apps_group_permissions (
+ granular_permission_id,
+ can_edit,
+ can_view,
+ hide_from_dashboard
+ ) VALUES (
+ '${granularPermissions.id}', ${createObject.canEdit}, ${createObject.canView}, ${createObject.hideFromDashboard}
+ ) RETURNING *;
+ `;
+ return (await manager.query(query))[0];
+ }
+
+ async migrateUserGroup(manager: EntityManager, userIds: string[], groupId: string) {
+ if (userIds.length == 0) return;
+ const valuesString = userIds.map((id) => `('${id}', '${groupId}')`).join(',');
+ const query = `
+ INSERT INTO group_users (user_id, group_id)
+ VALUES ${valuesString};
+ `;
+ return await manager.query(query);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {}
+}
diff --git a/server/data-migrations/1720365772516-AddingUsersToRespectiveRolesBuilderAndEndUsers.ts b/server/data-migrations/1720365772516-AddingUsersToRespectiveRolesBuilderAndEndUsers.ts
new file mode 100644
index 0000000000..25463d3ca7
--- /dev/null
+++ b/server/data-migrations/1720365772516-AddingUsersToRespectiveRolesBuilderAndEndUsers.ts
@@ -0,0 +1,124 @@
+import {
+ GROUP_PERMISSIONS_TYPE,
+ USER_ROLE,
+} from '@module/user_resource_permissions/constants/group-permissions.constant';
+import { GroupPermissions } from 'src/entities/group_permissions.entity';
+import { Organization } from 'src/entities/organization.entity';
+import { OrganizationUser } from 'src/entities/organization_user.entity';
+import { User } from 'src/entities/user.entity';
+import { UserGroupPermission } from 'src/entities/user_group_permission.entity';
+import { Brackets, EntityManager, MigrationInterface, QueryRunner } from 'typeorm';
+
+export class AddingUsersToRespectiveRolesBuilderAndEndUsers1720365772516 implements MigrationInterface {
+ public async up(queryRunner: QueryRunner): Promise {
+ const manager = queryRunner.manager;
+ const organizationIds = (
+ await manager.find(Organization, {
+ select: ['id'],
+ })
+ ).map((organization) => organization.id);
+ await this.getAndConvertEditorBuilderUsers(manager, organizationIds);
+ }
+
+ async getAndConvertEditorBuilderUsers(manager: EntityManager, organizationIds: string[]) {
+ for (const organizationId of organizationIds) {
+ const userIdsWithEditPermissions = (
+ await manager
+ .createQueryBuilder(User, 'users')
+ .innerJoin(
+ 'users.organizationUsers',
+ 'organization_users',
+ 'organization_users.organizationId = :organizationId ',
+ {
+ organizationId,
+ }
+ )
+ .innerJoin(
+ 'users.groupPermissions',
+ 'group_permissions',
+ 'organization_users.organizationId = group_permissions.organizationId'
+ )
+ .leftJoin('group_permissions.appGroupPermission', 'app_group_permissions')
+ .andWhere(
+ new Brackets((qb) => {
+ qb.where('app_group_permissions.read = true AND app_group_permissions.update = true').orWhere(
+ 'group_permissions.appCreate = true'
+ );
+ })
+ )
+ .select('users.id')
+ .distinct()
+ .getMany()
+ ).map((record) => record.id);
+
+ const userIdsOfAppOwners = (
+ await manager
+ .createQueryBuilder(User, 'users')
+ .innerJoin(
+ 'users.organizationUsers',
+ 'organization_users',
+ 'organization_users.organizationId = :organizationId',
+ {
+ organizationId,
+ }
+ )
+ .innerJoin('users.apps', 'apps')
+ .select('users.id')
+ .distinct()
+ .getMany()
+ ).map((record) => record.id);
+
+ const adminsUsers = (
+ await manager
+ .createQueryBuilder(UserGroupPermission, 'usersGroup')
+ .innerJoin(
+ 'usersGroup.groupPermission',
+ 'groupPermission',
+ 'groupPermission.organizationId = :organizationId',
+ {
+ organizationId,
+ }
+ )
+ .where('groupPermission.group = :admin', {
+ admin: 'admin',
+ })
+ .getMany()
+ ).map((record) => record.userId);
+ const builderUsersWithAdmin = [...new Set([...userIdsWithEditPermissions, ...userIdsOfAppOwners])];
+ const builderUsersWoAdmin = builderUsersWithAdmin.filter((id) => !adminsUsers.includes(id));
+ const builderGroup = await manager.findOne(GroupPermissions, {
+ where: { name: USER_ROLE.BUILDER, type: GROUP_PERMISSIONS_TYPE.DEFAULT, organizationId: organizationId },
+ });
+ const endUserGroup = await manager.findOne(GroupPermissions, {
+ where: { name: USER_ROLE.END_USER, type: GROUP_PERMISSIONS_TYPE.DEFAULT, organizationId: organizationId },
+ });
+
+ console.log('Builders users');
+ console.log(builderUsersWoAdmin);
+
+ await this.migrateUserGroup(manager, builderUsersWoAdmin, builderGroup.id);
+ const organizationUser = (
+ await manager.find(OrganizationUser, {
+ where: {
+ organizationId,
+ },
+ })
+ ).map((record) => record.userId);
+ const builderAdminUsers = [...new Set([...builderUsersWoAdmin, ...adminsUsers])];
+ const endUsers = organizationUser.filter((userId) => !builderAdminUsers.includes(userId));
+ await this.migrateUserGroup(manager, endUsers, endUserGroup.id);
+ }
+ }
+
+ async migrateUserGroup(manager: EntityManager, userIds: string[], groupId: string) {
+ if (userIds.length === 0) return;
+ const valuesString = userIds.map((id) => `('${id}', '${groupId}')`).join(',');
+ const query = `
+ INSERT INTO group_users (user_id, group_id)
+ VALUES ${valuesString};
+ `;
+ return await manager.query(query);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {}
+}
diff --git a/server/data-migrations/1720434737529-MigrateCustomGroupToNewUserGroup.ts b/server/data-migrations/1720434737529-MigrateCustomGroupToNewUserGroup.ts
new file mode 100644
index 0000000000..8cc12e70d9
--- /dev/null
+++ b/server/data-migrations/1720434737529-MigrateCustomGroupToNewUserGroup.ts
@@ -0,0 +1,213 @@
+import { CreateGranularPermissionDto } from '@dto/granular-permissions.dto';
+import {
+ DEFAULT_GRANULAR_PERMISSIONS_NAME,
+ ResourceType,
+} from '@module/user_resource_permissions/constants/granular-permissions.constant';
+import {
+ GROUP_PERMISSIONS_TYPE,
+ USER_ROLE,
+} from '@module/user_resource_permissions/constants/group-permissions.constant';
+import {
+ CreateResourcePermissionObject,
+ ResourcePermissionMetaData,
+} from '@module/user_resource_permissions/interface/granular-permissions.interface';
+import { AppGroupPermission } from 'src/entities/app_group_permission.entity';
+import { AppsGroupPermissions } from 'src/entities/apps_group_permissions.entity';
+import { GranularPermissions } from 'src/entities/granular_permissions.entity';
+import { GroupPermission } from 'src/entities/group_permission.entity';
+import { GroupPermissions } from 'src/entities/group_permissions.entity';
+import { Organization } from 'src/entities/organization.entity';
+import { EntityManager, MigrationInterface, QueryRunner } from 'typeorm';
+
+export class MigrateCustomGroupToNewUserGroup1720434737529 implements MigrationInterface {
+ public async up(queryRunner: QueryRunner): Promise {
+ const manager = queryRunner.manager;
+ const organizationIds = (
+ await manager.find(Organization, {
+ select: ['id'],
+ })
+ ).map((organization) => organization.id);
+ for (const organizationId of organizationIds) {
+ const groups = await manager
+ .createQueryBuilder(GroupPermission, 'groupPermission')
+ .where('groupPermission.organizationId = :organizationId', {
+ organizationId,
+ })
+ .leftJoinAndSelect('groupPermission.appGroupPermission', 'appGroupPermission')
+ .leftJoinAndSelect('groupPermission.userGroupPermission', 'userGroupPermission')
+ .andWhere('groupPermission.group != :admin', {
+ admin: 'admin',
+ })
+ .getMany();
+
+ for (const groupPermissions of groups) {
+ const query = `
+ INSERT INTO permission_groups (
+ organization_id,
+ name,
+ type,
+ app_create,
+ app_delete,
+ folder_crud,
+ org_constant_crud,
+ data_source_create,
+ data_source_delete
+ ) VALUES (
+ '${organizationId}',
+ '${this.getGroupName(groupPermissions.group)}',
+ '${GROUP_PERMISSIONS_TYPE.CUSTOM_GROUP}',
+ ${groupPermissions.appCreate},
+ ${groupPermissions.appDelete},
+ ${groupPermissions.folderCreate},
+ ${groupPermissions.orgEnvironmentConstantCreate},
+ false,
+ false
+ ) RETURNING *;
+ `;
+ const group: GroupPermissions = (await manager.query(query))[0];
+ const existingGroupUsers = groupPermissions.userGroupPermission;
+ await this.migrateUserGroup(manager, [...new Set(existingGroupUsers.map((record) => record.userId))], group.id);
+ const resources = [ResourceType.APP];
+ for (const resource of resources) {
+ if (resource === ResourceType.APP) {
+ const viewLevelAppsPermissions = groupPermissions.appGroupPermission.filter(
+ (appPermissions) => appPermissions.read
+ );
+ const updateLevelAppsPermissions = groupPermissions.appGroupPermission.filter(
+ (appPermissions) => appPermissions.update
+ );
+ const createResourcePermissionObjView: CreateResourcePermissionObject = {
+ canView: true,
+ canEdit: false,
+ hideFromDashboard: false,
+ };
+ await this.createAppLevelPermissions(
+ manager,
+ viewLevelAppsPermissions,
+ organizationId,
+ resource,
+ group,
+ createResourcePermissionObjView
+ );
+ const createResourcePermissionObjEdit: CreateResourcePermissionObject = {
+ canView: false,
+ canEdit: true,
+ hideFromDashboard: false,
+ };
+ await this.createAppLevelPermissions(
+ manager,
+ updateLevelAppsPermissions,
+ organizationId,
+ resource,
+ group,
+ createResourcePermissionObjEdit
+ );
+ }
+ }
+ }
+ }
+ }
+
+ getGroupName(name: string) {
+ switch (name) {
+ case USER_ROLE.BUILDER:
+ return `custom-${USER_ROLE.BUILDER}`;
+ case USER_ROLE.END_USER:
+ return `custom-${USER_ROLE.END_USER}`;
+ case 'all_users':
+ return `Custom All users`;
+ default:
+ return name;
+ }
+ }
+
+ async createGranularPermission(
+ manager: EntityManager,
+ createObject: CreateGranularPermissionDto
+ ): Promise {
+ const query = `
+ INSERT INTO granular_permissions (
+ group_id,
+ name,
+ type,
+ is_all,
+
+ ) VALUES (
+ ${createObject.groupId} , ${createObject.name} , ${createObject.type},${createObject.isAll}
+ ) RETURNING *;`;
+ return (await manager.query(query))[0];
+ }
+
+ async createAppsResourcePermission(
+ manager: EntityManager,
+ createMeta: ResourcePermissionMetaData,
+ createObject: CreateResourcePermissionObject
+ ): Promise {
+ const { granularPermissions } = createMeta;
+ const query = `
+ INSERT INTO apps_group_permissions (
+ granular_permission_id,
+ can_edit,
+ can_view,
+ hide_from_dashboard
+ ) VALUES (
+ ${granularPermissions.id},
+ ${createObject.canEdit},
+ ${createObject.canView},
+ ${createObject.hideFromDashboard}
+ ) RETURNING *;
+ `;
+ return (await manager.query(query))[0];
+ }
+
+ async migrateUserGroup(manager: EntityManager, userIds: string[], groupId: string) {
+ if (userIds.length == 0) return;
+ const valuesString = userIds.map((id) => `('${id}', '${groupId}')`).join(',');
+ const query = `
+ INSERT INTO group_users (user_id, group_id)
+ VALUES ${valuesString};
+ `;
+ return await manager.query(query);
+ }
+
+ async addAppsGroupToPermissions(manager: EntityManager, appIds: string[], appPermissionsId: string) {
+ const valuesString = appIds.map((id) => `('${id}', '${appPermissionsId}')`).join(',');
+ const query = `
+ INSERT INTO group_apps (app_id, apps_group_permissions_id)
+ VALUES ${valuesString};
+ `;
+ return await manager.query(query);
+ }
+
+ async createAppLevelPermissions(
+ manager: EntityManager,
+ appsPermissions: AppGroupPermission[],
+ organizationId: string,
+ resource: ResourceType,
+ group: GroupPermissions,
+ createResourcePermissionObj: CreateResourcePermissionObject
+ ) {
+ const nameInit = createResourcePermissionObj.canView ? 'Viewable' : 'Updatable';
+ if (appsPermissions.length === 0) return;
+ const dtoObject = {
+ name: `${nameInit} ${DEFAULT_GRANULAR_PERMISSIONS_NAME[resource]}`,
+ groupId: group.id,
+ type: resource as ResourceType,
+ isAll: false,
+ createAppsPermissionsObject: {},
+ };
+ const granularPermissions = await this.createGranularPermission(manager, dtoObject);
+ const appsGroupPermissions = await this.createAppsResourcePermission(
+ manager,
+ { granularPermissions, organizationId },
+ createResourcePermissionObj
+ );
+ await this.addAppsGroupToPermissions(
+ manager,
+ appsPermissions.map((record) => record.appId),
+ appsGroupPermissions.id
+ );
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {}
+}
diff --git a/server/data-migrations/1720513124281-DropGroupPermissionsOlderRelatedTables.ts b/server/data-migrations/1720513124281-DropGroupPermissionsOlderRelatedTables.ts
new file mode 100644
index 0000000000..8a47a54d64
--- /dev/null
+++ b/server/data-migrations/1720513124281-DropGroupPermissionsOlderRelatedTables.ts
@@ -0,0 +1,9 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+
+export class DropGroupPermissionsOlderRelatedTables1720513124281 implements MigrationInterface {
+ public async up(queryRunner: QueryRunner): Promise {
+ queryRunner.query('DROP TABLE group_permissions, user_group_permissions, app_group_permissions CASCADE;');
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {}
+}
diff --git a/server/src/helpers/queries.ts b/server/src/helpers/queries.ts
index fd971c5fa1..359cdd2a84 100644
--- a/server/src/helpers/queries.ts
+++ b/server/src/helpers/queries.ts
@@ -53,10 +53,17 @@ export function viewableAppsQueryUsingPermissions(
if (select) {
viewableAppsQb.select(select.map((col) => `viewable_apps.${col}`));
}
- if (userAppPermissions.hideAll || !(userAppPermissions.isAllEditable || userAppPermissions.isAllViewable)) {
+ const viewAll = userAppPermissions.isAllEditable || userAppPermissions.isAllViewable;
+ if (!viewAll || userAppPermissions.hideAll) {
viewableAppsQb.where('viewable_apps.id IN (:...viewableApps)', {
viewableApps,
});
}
+ const hiddenApps = userAppPermissions.hiddenAppsId;
+ if (!userAppPermissions.hideAll && viewAll && hiddenApps.length > 0) {
+ viewableAppsQb.where('viewable_apps.id NOT IN (:...hiddenApps)', {
+ hiddenApps,
+ });
+ }
return viewableAppsQb;
}
diff --git a/server/src/services/granular_permissions.service.ts b/server/src/services/granular_permissions.service.ts
index 386033fbb1..83e39ea582 100644
--- a/server/src/services/granular_permissions.service.ts
+++ b/server/src/services/granular_permissions.service.ts
@@ -40,7 +40,8 @@ export class GranularPermissionsService {
) {
return await dbTransactionWrap(async (manager: EntityManager) => {
const { createGranularPermissionDto, organizationId } = createGranularPermissionObject;
- const { name, type, groupId, isAll } = createGranularPermissionDto;
+ const { name, type, groupId, isAll: isAllDto } = createGranularPermissionDto;
+ const isAll = isAllDto ? true : false;
const granularPermissions: GranularPermissions = await catchDbException(async () => {
const granularPermissions = manager.create(GranularPermissions, { name, type, groupId, isAll });
return await manager.save(granularPermissions);
@@ -77,7 +78,7 @@ export class GranularPermissionsService {
const { organizationId, updateGranularPermissionDto, group } = updateGranularPermissionsObj;
const { isAll, name, resourcesToAdd, resourcesToDelete, actions, allowRoleChange } = updateGranularPermissionDto;
const updateGranularPermission = {
- isAll: isAll !== null || isAll !== undefined ? isAll : granularPermissions.isAll,
+ isAll: isAll ?? granularPermissions.isAll,
...(name && { name }),
};
const updateResource: UpdateResourceGroupPermissionsObject = {
diff --git a/server/src/services/organizations.service.ts b/server/src/services/organizations.service.ts
index 5892bf3131..4b94f7e9c9 100644
--- a/server/src/services/organizations.service.ts
+++ b/server/src/services/organizations.service.ts
@@ -63,7 +63,7 @@ interface UserCsvRow {
first_name: string;
last_name: string;
email: string;
- role: string;
+ user_role: string;
groups?: any;
}
@@ -711,7 +711,7 @@ export class OrganizationsService {
const existingGroups = groupPermissions.map((groupPermission) => groupPermission.name);
csv
.parseString(fileStream.toString(), {
- headers: ['first_name', 'last_name', 'email', 'groups', 'role'],
+ headers: ['first_name', 'last_name', 'email', 'groups', 'user_role'],
renameHeaders: true,
ignoreEmpty: true,
})
@@ -719,13 +719,12 @@ export class OrganizationsService {
return next(null, {
...row,
groups: this.createGroupsList(row?.groups),
- role: this.convertUserRolesCasing(row?.role),
+ user_role: this.convertUserRolesCasing(row?.user_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);
@@ -734,7 +733,14 @@ export class OrganizationsService {
} else if (user?.organizationUsers?.some((ou) => ou.organizationId === currentUser.organizationId)) {
existingUsers.push(data?.email);
} else {
- users.push(data);
+ const user = {
+ first_name: data.first_name,
+ last_name: data.last_name,
+ email: data.email,
+ role: data.user_role,
+ groups: data?.groups,
+ };
+ users.push(user);
}
//Check for invalid groups
@@ -748,8 +754,8 @@ export class OrganizationsService {
}
}
- if (!Object.values(USER_ROLE).includes(data?.role as USER_ROLE)) {
- invalidRoles.push(data?.role);
+ if (!Object.values(USER_ROLE).includes(data?.user_role as USER_ROLE)) {
+ invalidRoles.push(data?.user_role);
isInvalidRole = true;
}