mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-22 16:38:21 +00:00
Permissions refactor (#12624)
* permissions refactor * update * updates
This commit is contained in:
parent
6f12ec47bb
commit
4fdc36a99e
8 changed files with 200 additions and 70 deletions
|
|
@ -19,7 +19,9 @@ export class FeatureAbilityGuard extends AbilityGuard {
|
|||
{
|
||||
resourceType: MODULES.APP,
|
||||
},
|
||||
{ resourceType: MODULES.GLOBAL_DATA_SOURCE },
|
||||
{
|
||||
resourceType: MODULES.GLOBAL_DATA_SOURCE,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { Ability, AbilityBuilder, InferSubjects, SubjectType } from '@casl/ability';
|
||||
import { Ability, AbilityBuilder, InferSubjects } from '@casl/ability';
|
||||
import { AbilityFactory } from '@modules/app/ability-factory';
|
||||
import { UserAllPermissions } from '@modules/app/types';
|
||||
import { FEATURE_KEY } from '../../constants';
|
||||
import { DataSource } from '@entities/data_source.entity';
|
||||
import { MODULES } from '@modules/app/constants/modules';
|
||||
import { App } from '@entities/app.entity';
|
||||
|
||||
|
|
@ -16,8 +15,13 @@ export class FeatureAbilityFactory extends AbilityFactory<FEATURE_KEY, Subjects>
|
|||
return App;
|
||||
}
|
||||
|
||||
protected defineAbilityFor(can: AbilityBuilder<FeatureAbility>['can'], UserAllPermissions: UserAllPermissions): void {
|
||||
const { superAdmin, isAdmin, userPermission, isBuilder } = UserAllPermissions;
|
||||
protected defineAbilityFor(
|
||||
can: AbilityBuilder<FeatureAbility>['can'],
|
||||
UserAllPermissions: UserAllPermissions,
|
||||
extractedMetadata: { moduleName: string; features: string[] },
|
||||
request?: any
|
||||
): void {
|
||||
const { superAdmin, isAdmin, userPermission } = UserAllPermissions;
|
||||
|
||||
const resourcePermissions = userPermission?.[MODULES.APP];
|
||||
const isAllEditable = !!resourcePermissions?.isAllEditable;
|
||||
|
|
@ -25,23 +29,69 @@ export class FeatureAbilityFactory extends AbilityFactory<FEATURE_KEY, Subjects>
|
|||
const isCanDelete = userPermission.appDelete;
|
||||
const isAllViewable = !!resourcePermissions?.isAllViewable;
|
||||
|
||||
//if (isAdmin || superAdmin) {
|
||||
const appId = request?.tj_resource_id;
|
||||
|
||||
// Admin or super admin and do all operations
|
||||
can(
|
||||
[
|
||||
FEATURE_KEY.CREATE,
|
||||
FEATURE_KEY.GET,
|
||||
FEATURE_KEY.UPDATE,
|
||||
FEATURE_KEY.DELETE,
|
||||
FEATURE_KEY.UPDATE_DATA_SOURCE,
|
||||
FEATURE_KEY.UPDATE_ONE,
|
||||
FEATURE_KEY.RUN_EDITOR,
|
||||
FEATURE_KEY.RUN_VIEWER,
|
||||
FEATURE_KEY.PREVIEW,
|
||||
],
|
||||
App
|
||||
);
|
||||
return;
|
||||
//}
|
||||
if (isAdmin || superAdmin) {
|
||||
can(
|
||||
[
|
||||
FEATURE_KEY.CREATE,
|
||||
FEATURE_KEY.GET,
|
||||
FEATURE_KEY.UPDATE,
|
||||
FEATURE_KEY.DELETE,
|
||||
FEATURE_KEY.UPDATE_DATA_SOURCE,
|
||||
FEATURE_KEY.UPDATE_ONE,
|
||||
FEATURE_KEY.RUN_EDITOR,
|
||||
FEATURE_KEY.RUN_VIEWER,
|
||||
FEATURE_KEY.PREVIEW,
|
||||
],
|
||||
App
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAllEditable || isCanCreate || isCanDelete) {
|
||||
// Can create and can delete has master permissions
|
||||
can(
|
||||
[
|
||||
FEATURE_KEY.GET,
|
||||
FEATURE_KEY.UPDATE,
|
||||
FEATURE_KEY.UPDATE_ONE,
|
||||
FEATURE_KEY.RUN_EDITOR,
|
||||
FEATURE_KEY.RUN_VIEWER,
|
||||
FEATURE_KEY.PREVIEW,
|
||||
FEATURE_KEY.DELETE,
|
||||
FEATURE_KEY.CREATE,
|
||||
],
|
||||
App
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (resourcePermissions?.editableAppsId?.length && appId && resourcePermissions?.editableAppsId?.includes(appId)) {
|
||||
can(
|
||||
[
|
||||
FEATURE_KEY.GET,
|
||||
FEATURE_KEY.UPDATE,
|
||||
FEATURE_KEY.UPDATE_ONE,
|
||||
FEATURE_KEY.RUN_EDITOR,
|
||||
FEATURE_KEY.RUN_VIEWER,
|
||||
FEATURE_KEY.PREVIEW,
|
||||
FEATURE_KEY.DELETE,
|
||||
FEATURE_KEY.CREATE,
|
||||
],
|
||||
App
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAllViewable) {
|
||||
can([FEATURE_KEY.GET, FEATURE_KEY.PREVIEW, FEATURE_KEY.RUN_VIEWER], App);
|
||||
return;
|
||||
}
|
||||
if (resourcePermissions?.viewableAppsId?.length && appId && resourcePermissions?.viewableAppsId?.includes(appId)) {
|
||||
can([FEATURE_KEY.GET, FEATURE_KEY.PREVIEW, FEATURE_KEY.RUN_VIEWER], App);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { FeatureAbilityFactory } from '.';
|
||||
import { AbilityGuard } from '@modules/app/guards/ability.guard';
|
||||
import { MODULES } from '@modules/app/constants/modules';
|
||||
import { ResourceDetails } from '@modules/app/types';
|
||||
import { DataSource } from '@entities/data_source.entity';
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -13,6 +15,14 @@ export class FeatureAbilityGuard extends AbilityGuard {
|
|||
return DataSource;
|
||||
}
|
||||
|
||||
protected getResource(): ResourceDetails | ResourceDetails[] {
|
||||
return [
|
||||
{
|
||||
resourceType: MODULES.GLOBAL_DATA_SOURCE,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
protected forwardAbility(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,56 +3,108 @@ import { Ability, AbilityBuilder, InferSubjects } from '@casl/ability';
|
|||
import { AbilityFactory } from '@modules/app/ability-factory';
|
||||
import { UserAllPermissions } from '@modules/app/types';
|
||||
import { FEATURE_KEY } from '../../constants';
|
||||
import { MODULES } from '@modules/app/constants/modules';
|
||||
import { DataSource } from '@entities/data_source.entity';
|
||||
// import { MODULES } from '@modules/app/constants/modules';
|
||||
import { DataSourcesRepository } from '@modules/data-sources/repository';
|
||||
import { AbilityService } from '@modules/ability/interfaces/IService';
|
||||
|
||||
type Subjects = InferSubjects<typeof DataSource> | 'all';
|
||||
export type FeatureAbility = Ability<[FEATURE_KEY, Subjects]>;
|
||||
|
||||
@Injectable()
|
||||
export class FeatureAbilityFactory extends AbilityFactory<FEATURE_KEY, Subjects> {
|
||||
constructor(private readonly dataSourceRepository: DataSourcesRepository, protected abilityService: AbilityService) {
|
||||
super(abilityService);
|
||||
}
|
||||
protected getSubjectType() {
|
||||
return DataSource;
|
||||
}
|
||||
|
||||
protected async defineAbilityFor(
|
||||
protected defineAbilityFor(
|
||||
can: AbilityBuilder<FeatureAbility>['can'],
|
||||
UserAllPermissions: UserAllPermissions,
|
||||
extractedMetadata: { moduleName: string; features: string[] },
|
||||
request?: any
|
||||
): Promise<void> {
|
||||
// Data source permissions
|
||||
// EE - data source create/delete -> full access
|
||||
// CE - Admin - full access. builder -> use access
|
||||
): void {
|
||||
const { superAdmin, isAdmin, userPermission } = UserAllPermissions;
|
||||
|
||||
// const { userPermission } = UserAllPermissions;
|
||||
// const staticDataSources = await this.dataSourceRepository.getAllStaticDataSources(request.params.versionId);
|
||||
const resourcePermissions = userPermission?.[MODULES.GLOBAL_DATA_SOURCE];
|
||||
const isAllEditable = !!resourcePermissions?.isAllConfigurable;
|
||||
const isCanCreate = userPermission.dataSourceCreate;
|
||||
const isCanDelete = userPermission.dataSourceDelete;
|
||||
const isAllViewable = !!resourcePermissions?.isAllUsable;
|
||||
|
||||
// const resourcePermissions = userPermission?.[MODULES.GLOBAL_DATA_SOURCE];
|
||||
// const isAllEditable = !!resourcePermissions?.isAllConfigurable;
|
||||
// const isCanCreate = userPermission.dataSourceCreate;
|
||||
// const isCanDelete = userPermission.dataSourceDelete;
|
||||
// const isAllViewable = !!resourcePermissions?.isAllUsable;
|
||||
const dataSourceId = request?.tj_resource_id;
|
||||
|
||||
can(
|
||||
[
|
||||
FEATURE_KEY.CREATE,
|
||||
FEATURE_KEY.GET,
|
||||
FEATURE_KEY.UPDATE,
|
||||
FEATURE_KEY.DELETE,
|
||||
FEATURE_KEY.UPDATE_DATA_SOURCE,
|
||||
FEATURE_KEY.UPDATE_ONE,
|
||||
FEATURE_KEY.RUN_EDITOR,
|
||||
FEATURE_KEY.RUN_VIEWER,
|
||||
FEATURE_KEY.PREVIEW,
|
||||
],
|
||||
DataSource
|
||||
);
|
||||
return;
|
||||
// Define permissions for data queries
|
||||
|
||||
if (isAdmin || superAdmin || isAllEditable || isCanCreate || isCanDelete) {
|
||||
can(
|
||||
[
|
||||
FEATURE_KEY.CREATE,
|
||||
FEATURE_KEY.GET,
|
||||
FEATURE_KEY.UPDATE,
|
||||
FEATURE_KEY.DELETE,
|
||||
FEATURE_KEY.RUN_VIEWER,
|
||||
FEATURE_KEY.PREVIEW,
|
||||
FEATURE_KEY.UPDATE_DATA_SOURCE,
|
||||
FEATURE_KEY.UPDATE_ONE,
|
||||
FEATURE_KEY.RUN_EDITOR,
|
||||
],
|
||||
DataSource
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
resourcePermissions?.configurableDataSourceId?.length &&
|
||||
dataSourceId &&
|
||||
resourcePermissions?.configurableDataSourceId?.includes(dataSourceId)
|
||||
) {
|
||||
can(
|
||||
[
|
||||
FEATURE_KEY.CREATE,
|
||||
FEATURE_KEY.GET,
|
||||
FEATURE_KEY.UPDATE,
|
||||
FEATURE_KEY.DELETE,
|
||||
FEATURE_KEY.RUN_VIEWER,
|
||||
FEATURE_KEY.PREVIEW,
|
||||
FEATURE_KEY.UPDATE_DATA_SOURCE,
|
||||
FEATURE_KEY.UPDATE_ONE,
|
||||
FEATURE_KEY.RUN_EDITOR,
|
||||
],
|
||||
DataSource
|
||||
);
|
||||
}
|
||||
if (isAllViewable) {
|
||||
can(
|
||||
[
|
||||
FEATURE_KEY.GET,
|
||||
FEATURE_KEY.UPDATE,
|
||||
FEATURE_KEY.UPDATE_ONE,
|
||||
FEATURE_KEY.RUN_EDITOR,
|
||||
FEATURE_KEY.RUN_VIEWER,
|
||||
FEATURE_KEY.PREVIEW,
|
||||
FEATURE_KEY.DELETE,
|
||||
FEATURE_KEY.CREATE,
|
||||
],
|
||||
DataSource
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
resourcePermissions.usableDataSourcesId?.length &&
|
||||
dataSourceId &&
|
||||
resourcePermissions?.usableDataSourcesId?.includes(dataSourceId)
|
||||
) {
|
||||
can(
|
||||
[
|
||||
FEATURE_KEY.GET,
|
||||
FEATURE_KEY.CREATE,
|
||||
FEATURE_KEY.UPDATE,
|
||||
FEATURE_KEY.DELETE,
|
||||
FEATURE_KEY.RUN_VIEWER,
|
||||
FEATURE_KEY.PREVIEW,
|
||||
FEATURE_KEY.UPDATE_ONE,
|
||||
FEATURE_KEY.RUN_EDITOR,
|
||||
],
|
||||
DataSource
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import { FeatureAbilityGuard as AppFeatureAbilityGuard } from './ability/app/gua
|
|||
import { FeatureAbilityGuard as DataSourceFeatureAbilityGuard } from './ability/data-source/guard';
|
||||
import { ValidateQuerySourceGuard } from './guards/validate-query-source.guard';
|
||||
import { ValidateAppVersionGuard } from '@modules/versions/guards/validate-app-version.guard';
|
||||
import { QueryAuthGuard } from './guards/query-auth.guard';
|
||||
import { AbilityDecorator as Ability } from '@modules/app/decorators/ability.decorator';
|
||||
import { AppAbility } from '@modules/casl/casl-ability.factory';
|
||||
import { AppDecorator } from '@modules/app/decorators/app.decorator';
|
||||
|
|
@ -26,19 +25,18 @@ import { IDataQueriesController } from './interfaces/IController';
|
|||
export class DataQueriesController implements IDataQueriesController {
|
||||
constructor(protected dataQueriesService: DataQueriesService) {}
|
||||
|
||||
// Add ability check - App editable
|
||||
@InitFeature(FEATURE_KEY.GET)
|
||||
@UseGuards(JwtAuthGuard, ValidateAppVersionGuard, AppFeatureAbilityGuard)
|
||||
@UseGuards(JwtAuthGuard, ValidateAppVersionGuard, ValidateQueryAppGuard, AppFeatureAbilityGuard)
|
||||
@Get(':versionId')
|
||||
index(@Param('versionId') versionId: string) {
|
||||
return this.dataQueriesService.getAll(versionId);
|
||||
}
|
||||
|
||||
@InitFeature(FEATURE_KEY.CREATE)
|
||||
// Add ability check - App editable and data source configurable
|
||||
@UseGuards(
|
||||
JwtAuthGuard,
|
||||
ValidateAppVersionGuard,
|
||||
ValidateQueryAppGuard,
|
||||
AppFeatureAbilityGuard,
|
||||
ValidateQuerySourceGuard,
|
||||
DataSourceFeatureAbilityGuard
|
||||
|
|
@ -55,7 +53,6 @@ export class DataQueriesController implements IDataQueriesController {
|
|||
}
|
||||
|
||||
@InitFeature(FEATURE_KEY.UPDATE_ONE)
|
||||
// Add ability check - App editable and data source editable
|
||||
@UseGuards(
|
||||
JwtAuthGuard,
|
||||
ValidateQueryAppGuard,
|
||||
|
|
@ -64,8 +61,9 @@ export class DataQueriesController implements IDataQueriesController {
|
|||
DataSourceFeatureAbilityGuard
|
||||
)
|
||||
@Patch(':id/versions/:versionId')
|
||||
async updateDataSource(
|
||||
async updateDataQuery(
|
||||
@User() user: UserEntity,
|
||||
@AppDecorator() app: App,
|
||||
@Param('id') dataQueryId,
|
||||
@Param('versionId') versionId,
|
||||
@Body() updateDataQueryDto: UpdateDataQueryDto
|
||||
|
|
@ -76,7 +74,7 @@ export class DataQueriesController implements IDataQueriesController {
|
|||
|
||||
@InitFeature(FEATURE_KEY.UPDATE)
|
||||
//* On Updating references, need update the options of multiple queries
|
||||
@UseGuards(JwtAuthGuard, ValidateAppVersionGuard, AppFeatureAbilityGuard)
|
||||
@UseGuards(JwtAuthGuard, ValidateAppVersionGuard, ValidateQueryAppGuard, AppFeatureAbilityGuard)
|
||||
@Patch('versions/:versionId')
|
||||
async bulkUpdate(@User() user: UserEntity, @Body() updatingReferencesOptions: UpdatingReferencesOptionsDto) {
|
||||
return await this.dataQueriesService.bulkUpdateQueryOptions(user, updatingReferencesOptions.data_queries_options);
|
||||
|
|
@ -97,7 +95,6 @@ export class DataQueriesController implements IDataQueriesController {
|
|||
}
|
||||
|
||||
@InitFeature(FEATURE_KEY.RUN_EDITOR)
|
||||
// TODO: Validate against app edit access
|
||||
@UseGuards(
|
||||
JwtAuthGuard,
|
||||
ValidateQueryAppGuard,
|
||||
|
|
@ -108,6 +105,7 @@ export class DataQueriesController implements IDataQueriesController {
|
|||
@Post(':id/versions/:versionId/run/:environmentId')
|
||||
runQueryOnBuilder(
|
||||
@User() user: UserEntity,
|
||||
@AppDecorator() app: App,
|
||||
@Param('id') dataQueryId,
|
||||
@Param('environmentId') environmentId,
|
||||
@Body() updateDataQueryDto: UpdateDataQueryDto,
|
||||
|
|
@ -127,11 +125,17 @@ export class DataQueriesController implements IDataQueriesController {
|
|||
}
|
||||
|
||||
@InitFeature(FEATURE_KEY.RUN_VIEWER)
|
||||
// TODO: Validate against app view access
|
||||
@UseGuards(QueryAuthGuard, AppFeatureAbilityGuard)
|
||||
@UseGuards(
|
||||
JwtAuthGuard,
|
||||
ValidateQueryAppGuard,
|
||||
AppFeatureAbilityGuard,
|
||||
ValidateQuerySourceGuard,
|
||||
DataSourceFeatureAbilityGuard
|
||||
)
|
||||
@Post(':id/run')
|
||||
async runQuery(
|
||||
@User() user: UserEntity,
|
||||
@AppDecorator() app: App,
|
||||
@Param('id') dataQueryId,
|
||||
@Body() updateDataQueryDto: UpdateDataQueryDto,
|
||||
@Res({ passthrough: true }) response: Response
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ export class ValidateQueryAppGuard implements CanActivate {
|
|||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const { id, versionId } = request.params;
|
||||
const appId = request.body?.app_id;
|
||||
const user: User = request.user;
|
||||
|
||||
// Check if either id is provided, otherwise throw BadRequestException
|
||||
if (!id) {
|
||||
if (!versionId) {
|
||||
throw new BadRequestException();
|
||||
}
|
||||
|
||||
|
|
@ -28,8 +28,16 @@ export class ValidateQueryAppGuard implements CanActivate {
|
|||
if (!user) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
const app = await this.appsRepository.findByDataQuery(id, user.organizationId, versionId);
|
||||
let app;
|
||||
if (id) {
|
||||
app = await this.appsRepository.findByDataQuery(id, user.organizationId, versionId);
|
||||
}
|
||||
if (appId) {
|
||||
app = await this.appsRepository.findById(appId, user.organizationId, versionId);
|
||||
}
|
||||
if (versionId) {
|
||||
app = await this.versionRepository.findAppFromVersion(versionId, user.organizationId);
|
||||
}
|
||||
|
||||
// If app is not found, throw NotFoundException
|
||||
if (!app) {
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ export interface IDataQueriesController {
|
|||
dataQueryDto: CreateDataQueryDto
|
||||
): Promise<object>;
|
||||
|
||||
updateDataSource(
|
||||
updateDataQuery(
|
||||
user: UserEntity,
|
||||
app: App,
|
||||
dataQueryId: string,
|
||||
versionId: string,
|
||||
updateDataQueryDto: UpdateDataQueryDto
|
||||
|
|
@ -29,6 +30,7 @@ export interface IDataQueriesController {
|
|||
|
||||
runQueryOnBuilder(
|
||||
user: UserEntity,
|
||||
app: App,
|
||||
dataQueryId: string,
|
||||
environmentId: string,
|
||||
updateDataQueryDto: UpdateDataQueryDto,
|
||||
|
|
@ -39,6 +41,7 @@ export interface IDataQueriesController {
|
|||
|
||||
runQuery(
|
||||
user: UserEntity,
|
||||
app: App,
|
||||
dataQueryId: string,
|
||||
updateDataQueryDto: UpdateDataQueryDto,
|
||||
response: Response
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ export class FeatureAbilityFactory extends AbilityFactory<FEATURE_KEY, Subjects>
|
|||
resourcePermissions?.usableDataSourcesId?.includes(dataSourceId)
|
||||
) {
|
||||
can([FEATURE_KEY.GET, FEATURE_KEY.GET_BY_ENVIRONMENT], DataSource);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue