mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
Merged woht merge branch
This commit is contained in:
commit
4b39935233
13 changed files with 647 additions and 133 deletions
|
|
@ -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
|
||||
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"
|
||||
|
|
|
@ -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 }));
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
|
|
@ -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 }));
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ function AppResourcePermissions({
|
|||
>
|
||||
<div className="resource-name d-flex">
|
||||
<SolidIcon name="app" width="20px" className="resource-icon" />
|
||||
<div className="resource-text">{permissions.name}</div>
|
||||
<div className="resource-text">{` ${permissions.name}`}</div>
|
||||
</div>
|
||||
<div className="text-muted">
|
||||
<div className="d-flex apps-permission-wrap flex-column">
|
||||
|
|
|
|||
|
|
@ -555,7 +555,10 @@ class ManageGroupPermissionResourcesComponent extends React.Component {
|
|||
|
||||
<nav className="nav nav-tabs groups-sub-header-wrap">
|
||||
<a
|
||||
onClick={() => 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 {
|
|||
</a>
|
||||
|
||||
<a
|
||||
onClick={() => 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 {
|
|||
)}
|
||||
</a>
|
||||
<a
|
||||
onClick={() => 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"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -507,7 +507,6 @@ class ManageGroupPermissionsComponent extends React.Component {
|
|||
</ButtonSolid>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
show={showNewGroupForm || showGroupNameUpdateForm}
|
||||
closeModal={() =>
|
||||
|
|
@ -589,16 +588,90 @@ class ManageGroupPermissionsComponent extends React.Component {
|
|||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
{!showNewGroupForm && !showGroupNameUpdateForm && (
|
||||
<div className="org-users-page-card-wrap">
|
||||
<div className="org-users-page-sidebar">
|
||||
<div className="default-group-list-container">
|
||||
<div className="org-users-page-card-wrap">
|
||||
<div className="org-users-page-sidebar">
|
||||
<div className="default-group-list-container">
|
||||
<div className="mb-2 d-flex align-items-center">
|
||||
<SolidIcon name="usergear" />
|
||||
<span className="ml-1 group-title">USER ROLE</span>
|
||||
</div>
|
||||
{defaultGroups.map((permissionGroup) => {
|
||||
return (
|
||||
<FolderList
|
||||
key={permissionGroup.id}
|
||||
listId={permissionGroup.id}
|
||||
overlayFunctionParam={{
|
||||
id: permissionGroup.id,
|
||||
groupName: permissionGroup.name,
|
||||
}}
|
||||
selectedItem={this.state.selectedGroup == this.humanizeifDefaultGroupName(permissionGroup.name)}
|
||||
onClick={() => {
|
||||
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, '-')}
|
||||
>
|
||||
<span>
|
||||
<OverflowTooltip>{this.humanizeifDefaultGroupName(permissionGroup.name)}</OverflowTooltip>
|
||||
</span>
|
||||
</FolderList>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
{!showGroupSearchBar ? (
|
||||
<div className="mb-2 d-flex align-items-center">
|
||||
<SolidIcon name="usergear" />
|
||||
<span className="ml-1 group-title">USER ROLE</span>
|
||||
<SolidIcon name="usergroup" width="18px" fill="#889096" />
|
||||
<span className="ml-1 group-title">CUSTOM GROUPS</span>
|
||||
<div className="create-group-cont">
|
||||
<ButtonSolid
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
this.setState({ showGroupSearchBar: true });
|
||||
}}
|
||||
size="xsm"
|
||||
rightIcon="search"
|
||||
iconWidth="15"
|
||||
fill="#889096"
|
||||
className="create-group-custom"
|
||||
/>
|
||||
<ButtonSolid
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
this.setState({ newGroupName: null, showNewGroupForm: true, isSaveBtnDisabled: true });
|
||||
}}
|
||||
size="sm"
|
||||
fill="#889096"
|
||||
rightIcon="plus"
|
||||
iconWidth="20"
|
||||
className="create-group-custom"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{defaultGroups.map((permissionGroup) => {
|
||||
) : (
|
||||
<div className="searchbox-custom">
|
||||
<SearchBox
|
||||
dataCy={`query-manager`}
|
||||
width="70px !important"
|
||||
callBack={this.handleGroupSearch}
|
||||
placeholder={'Search'}
|
||||
customClass="tj-common-search-input-group"
|
||||
onClearCallback={this.handleGroupSearchClose}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{groups.length ? (
|
||||
filteredGroup.map((permissionGroup) => {
|
||||
return (
|
||||
<FolderList
|
||||
key={permissionGroup.id}
|
||||
|
|
@ -629,116 +702,37 @@ class ManageGroupPermissionsComponent extends React.Component {
|
|||
</span>
|
||||
</FolderList>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
{!showGroupSearchBar ? (
|
||||
<div className="mb-2 d-flex align-items-center">
|
||||
<SolidIcon name="usergroup" width="18px" fill="#889096" />
|
||||
<span className="ml-1 group-title">CUSTOM GROUPS</span>
|
||||
<div className="create-group-cont">
|
||||
<ButtonSolid
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
this.setState({ showGroupSearchBar: true });
|
||||
}}
|
||||
size="xsm"
|
||||
rightIcon="search"
|
||||
iconWidth="15"
|
||||
fill="#889096"
|
||||
className="create-group-custom"
|
||||
/>
|
||||
<ButtonSolid
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
this.setState({ newGroupName: null, showNewGroupForm: true, isSaveBtnDisabled: true });
|
||||
}}
|
||||
size="sm"
|
||||
fill="#889096"
|
||||
rightIcon="plus"
|
||||
iconWidth="20"
|
||||
className="create-group-custom"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="searchbox-custom">
|
||||
<SearchBox
|
||||
dataCy={`query-manager`}
|
||||
width="70px !important"
|
||||
callBack={this.handleGroupSearch}
|
||||
placeholder={'Search'}
|
||||
customClass="tj-common-search-input-group"
|
||||
onClearCallback={this.handleGroupSearchClose}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{groups.length ? (
|
||||
filteredGroup.map((permissionGroup) => {
|
||||
return (
|
||||
<FolderList
|
||||
key={permissionGroup.id}
|
||||
listId={permissionGroup.id}
|
||||
overlayFunctionParam={{
|
||||
id: permissionGroup.id,
|
||||
groupName: permissionGroup.name,
|
||||
}}
|
||||
selectedItem={
|
||||
this.state.selectedGroup == this.humanizeifDefaultGroupName(permissionGroup.name)
|
||||
}
|
||||
onClick={() => {
|
||||
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, '-')}
|
||||
>
|
||||
<span>
|
||||
<OverflowTooltip>{this.humanizeifDefaultGroupName(permissionGroup.name)}</OverflowTooltip>
|
||||
</span>
|
||||
</FolderList>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div className="empty-custom-group-info">
|
||||
<SolidIcon className="info-icon" name="information" width="18px" />
|
||||
<span className="tj-text-xsm text-center info-label">No custom groups added</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="org-users-page-card-body">
|
||||
{isLoading ? (
|
||||
<Loader />
|
||||
})
|
||||
) : (
|
||||
<ManageGroupPermissionResourcesV2
|
||||
groupPermissionId={this.state.selectedGroupPermissionId}
|
||||
darkMode={this.props.darkMode}
|
||||
selectedGroup={this.state.selectedGroup}
|
||||
selectedGroupObject={this.state.selectedGroupObject}
|
||||
updateGroupName={this.updateGroupName}
|
||||
deleteGroup={this.deleteGroup}
|
||||
roleOptions={defaultGroups.map((group) => {
|
||||
return {
|
||||
name: this.humanizeifDefaultGroupName(group.name),
|
||||
value: group.name,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
<div className="empty-custom-group-info">
|
||||
<SolidIcon className="info-icon" name="information" width="18px" />
|
||||
<span className="tj-text-xsm text-center info-label">No custom groups added</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="org-users-page-card-body">
|
||||
{isLoading ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<ManageGroupPermissionResourcesV2
|
||||
groupPermissionId={this.state.selectedGroupPermissionId}
|
||||
darkMode={this.props.darkMode}
|
||||
selectedGroup={this.state.selectedGroup}
|
||||
selectedGroupObject={this.state.selectedGroupObject}
|
||||
updateGroupName={this.updateGroupName}
|
||||
deleteGroup={this.deleteGroup}
|
||||
roleOptions={defaultGroups.map((group) => {
|
||||
return {
|
||||
name: this.humanizeifDefaultGroupName(group.name),
|
||||
value: group.name,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
|
|
|
|||
|
|
@ -430,6 +430,7 @@
|
|||
}
|
||||
.resource-text {
|
||||
margin-left: 10px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
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<ResourceType, CreateResourcePermissionObject> =
|
||||
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<GranularPermissions> {
|
||||
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<AppsGroupPermissions> {
|
||||
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<void> {}
|
||||
}
|
||||
|
|
@ -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<void> {
|
||||
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<void> {}
|
||||
}
|
||||
|
|
@ -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<void> {
|
||||
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<GranularPermissions> {
|
||||
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<AppsGroupPermissions> {
|
||||
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<void> {}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class DropGroupPermissionsOlderRelatedTables1720513124281 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
queryRunner.query('DROP TABLE group_permissions, user_group_permissions, app_group_permissions CASCADE;');
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue