feat: refactoring casl permission checks for recursive nested operations (#778)

* feat: nested casl abilities

* fix: remove unused packages

* Fixes

* Fix createMany broken

* Fix lint

* Fix lint

* Fix lint

* Fix lint

* Fixes

* Fix CommentThread

* Fix bugs

* Fix lint

* Fix bugs

* Fixed auto routing

* Fixed app path

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Jérémy M
2023-07-26 01:37:22 +02:00
committed by GitHub
parent 92b9e987a5
commit 51cfc0d82c
69 changed files with 1192 additions and 883 deletions

View File

@ -42,7 +42,7 @@ export class CreateAttachmentAbilityHandler implements IAbilityHandler {
const args = gqlContext.getArgs<AttachmentArgs>();
assert(args.activityId, '', ForbiddenException);
const activity = await this.prismaService.commentThread.findUnique({
const activity = await this.prismaService.client.commentThread.findUnique({
where: { id: args.activityId },
include: { workspace: true },
});

View File

@ -13,10 +13,12 @@ import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { CommentThreadTargetWhereInput } from 'src/core/@generated/comment-thread-target/comment-thread-target-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
class CommentThreadTargetArgs {
where?: CommentThreadTargetWhereInput;
[key: string]: any;
}
@Injectable()
@ -39,7 +41,23 @@ export class ReadCommentThreadTargetAbilityHandler implements IAbilityHandler {
export class CreateCommentThreadTargetAbilityHandler
implements IAbilityHandler
{
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'CommentThreadTarget',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'CommentThreadTarget');
}
}
@ -54,11 +72,22 @@ export class UpdateCommentThreadTargetAbilityHandler
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<CommentThreadTargetArgs>();
const commentThreadTarget =
await this.prismaService.commentThreadTarget.findFirst({
await this.prismaService.client.commentThreadTarget.findFirst({
where: args.where,
});
assert(commentThreadTarget, '', NotFoundException);
const allowed = await relationAbilityChecker(
'CommentThreadTarget',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(
AbilityAction.Update,
subject('CommentThreadTarget', commentThreadTarget),
@ -76,7 +105,7 @@ export class DeleteCommentThreadTargetAbilityHandler
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<CommentThreadTargetArgs>();
const commentThreadTarget =
await this.prismaService.commentThreadTarget.findFirst({
await this.prismaService.client.commentThreadTarget.findFirst({
where: args.where,
});
assert(commentThreadTarget, '', NotFoundException);

View File

@ -13,10 +13,12 @@ import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { CommentThreadWhereInput } from 'src/core/@generated/comment-thread/comment-thread-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
class CommentThreadArgs {
where?: CommentThreadWhereInput;
[key: string]: any;
}
@Injectable()
@ -35,7 +37,23 @@ export class ReadCommentThreadAbilityHandler implements IAbilityHandler {
@Injectable()
export class CreateCommentThreadAbilityHandler implements IAbilityHandler {
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'CommentThread',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'CommentThread');
}
}
@ -47,11 +65,23 @@ export class UpdateCommentThreadAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<CommentThreadArgs>();
const commentThread = await this.prismaService.commentThread.findFirst({
where: args.where,
});
const commentThread =
await this.prismaService.client.commentThread.findFirst({
where: args.where,
});
assert(commentThread, '', NotFoundException);
const allowed = await relationAbilityChecker(
'CommentThread',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(
AbilityAction.Update,
subject('CommentThread', commentThread),
@ -66,9 +96,10 @@ export class DeleteCommentThreadAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<CommentThreadArgs>();
const commentThread = await this.prismaService.commentThread.findFirst({
where: args.where,
});
const commentThread =
await this.prismaService.client.commentThread.findFirst({
where: args.where,
});
assert(commentThread, '', NotFoundException);
return ability.can(

View File

@ -13,10 +13,12 @@ import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { CommentWhereInput } from 'src/core/@generated/comment/comment-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
class CommentArgs {
where?: CommentWhereInput;
[key: string]: any;
}
@Injectable()
@ -35,7 +37,23 @@ export class ReadCommentAbilityHandler implements IAbilityHandler {
@Injectable()
export class CreateCommentAbilityHandler implements IAbilityHandler {
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'Comment',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'Comment');
}
}
@ -47,11 +65,22 @@ export class UpdateCommentAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<CommentArgs>();
const comment = await this.prismaService.comment.findFirst({
const comment = await this.prismaService.client.comment.findFirst({
where: args.where,
});
assert(comment, '', NotFoundException);
const allowed = await relationAbilityChecker(
'Comment',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Update, subject('Comment', comment));
}
}
@ -63,7 +92,7 @@ export class DeleteCommentAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<CommentArgs>();
const comment = await this.prismaService.comment.findFirst({
const comment = await this.prismaService.client.comment.findFirst({
where: args.where,
});
assert(comment, '', NotFoundException);

View File

@ -13,10 +13,12 @@ import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { CompanyWhereInput } from 'src/core/@generated/company/company-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
class CompanyArgs {
where?: CompanyWhereInput;
[key: string]: any;
}
@Injectable()
@ -35,7 +37,23 @@ export class ReadCompanyAbilityHandler implements IAbilityHandler {
@Injectable()
export class CreateCompanyAbilityHandler implements IAbilityHandler {
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'Company',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'Company');
}
}
@ -47,12 +65,22 @@ export class UpdateCompanyAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<CompanyArgs>();
const company = await this.prismaService.company.findFirst({
const company = await this.prismaService.client.company.findFirst({
where: args.where,
});
assert(company, '', NotFoundException);
const allowed = await relationAbilityChecker(
'Company',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Update, subject('Company', company));
}
}
@ -64,7 +92,7 @@ export class DeleteCompanyAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<CompanyArgs>();
const company = await this.prismaService.company.findFirst({
const company = await this.prismaService.client.company.findFirst({
where: args.where,
});
assert(company, '', NotFoundException);

View File

@ -13,10 +13,12 @@ import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { PersonWhereInput } from 'src/core/@generated/person/person-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
class PersonArgs {
where?: PersonWhereInput;
[key: string]: any;
}
@Injectable()
@ -35,7 +37,23 @@ export class ReadPersonAbilityHandler implements IAbilityHandler {
@Injectable()
export class CreatePersonAbilityHandler implements IAbilityHandler {
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'Person',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'Person');
}
}
@ -47,11 +65,22 @@ export class UpdatePersonAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<PersonArgs>();
const person = await this.prismaService.person.findFirst({
const person = await this.prismaService.client.person.findFirst({
where: args.where,
});
assert(person, '', NotFoundException);
const allowed = await relationAbilityChecker(
'Person',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Update, subject('Person', person));
}
}
@ -63,7 +92,7 @@ export class DeletePersonAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<PersonArgs>();
const person = await this.prismaService.person.findFirst({
const person = await this.prismaService.client.person.findFirst({
where: args.where,
});
assert(person, '', NotFoundException);

View File

@ -12,11 +12,13 @@ import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interfac
import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { assert } from 'src/utils/assert';
import { PipelineProgressWhereInput } from 'src/core/@generated/pipeline-progress/pipeline-progress-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
class PipelineProgressArgs {
where?: PipelineProgressWhereInput;
[key: string]: any;
}
@Injectable()
@ -35,7 +37,23 @@ export class ReadPipelineProgressAbilityHandler implements IAbilityHandler {
@Injectable()
export class CreatePipelineProgressAbilityHandler implements IAbilityHandler {
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'PipelineProgress',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'PipelineProgress');
}
}
@ -48,11 +66,22 @@ export class UpdatePipelineProgressAbilityHandler implements IAbilityHandler {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<PipelineProgressArgs>();
const pipelineProgress =
await this.prismaService.pipelineProgress.findFirst({
await this.prismaService.client.pipelineProgress.findFirst({
where: args.where,
});
assert(pipelineProgress, '', NotFoundException);
const allowed = await relationAbilityChecker(
'PipelineProgress',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(
AbilityAction.Update,
subject('PipelineProgress', pipelineProgress),
@ -68,7 +97,7 @@ export class DeletePipelineProgressAbilityHandler implements IAbilityHandler {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<PipelineProgressArgs>();
const pipelineProgress =
await this.prismaService.pipelineProgress.findFirst({
await this.prismaService.client.pipelineProgress.findFirst({
where: args.where,
});
assert(pipelineProgress, '', NotFoundException);

View File

@ -13,10 +13,12 @@ import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { PipelineStageWhereInput } from 'src/core/@generated/pipeline-stage/pipeline-stage-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
class PipelineStageArgs {
where?: PipelineStageWhereInput;
[key: string]: any;
}
@Injectable()
@ -35,7 +37,23 @@ export class ReadPipelineStageAbilityHandler implements IAbilityHandler {
@Injectable()
export class CreatePipelineStageAbilityHandler implements IAbilityHandler {
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'PipelineStage',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'PipelineStage');
}
}
@ -47,11 +65,23 @@ export class UpdatePipelineStageAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<PipelineStageArgs>();
const pipelineStage = await this.prismaService.pipelineStage.findFirst({
where: args.where,
});
const pipelineStage =
await this.prismaService.client.pipelineStage.findFirst({
where: args.where,
});
assert(pipelineStage, '', NotFoundException);
const allowed = await relationAbilityChecker(
'PipelineStage',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(
AbilityAction.Update,
subject('PipelineStage', pipelineStage),
@ -66,9 +96,10 @@ export class DeletePipelineStageAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<PipelineStageArgs>();
const pipelineStage = await this.prismaService.pipelineStage.findFirst({
where: args.where,
});
const pipelineStage =
await this.prismaService.client.pipelineStage.findFirst({
where: args.where,
});
assert(pipelineStage, '', NotFoundException);
return ability.can(

View File

@ -13,10 +13,12 @@ import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { PipelineWhereInput } from 'src/core/@generated/pipeline/pipeline-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
class PipelineArgs {
where?: PipelineWhereInput;
[key: string]: any;
}
@Injectable()
@ -35,7 +37,23 @@ export class ReadPipelineAbilityHandler implements IAbilityHandler {
@Injectable()
export class CreatePipelineAbilityHandler implements IAbilityHandler {
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'Pipeline',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'Pipeline');
}
}
@ -47,11 +65,22 @@ export class UpdatePipelineAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<PipelineArgs>();
const pipeline = await this.prismaService.pipeline.findFirst({
const pipeline = await this.prismaService.client.pipeline.findFirst({
where: args.where,
});
assert(pipeline, '', NotFoundException);
const allowed = await relationAbilityChecker(
'Pipeline',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Update, subject('Pipeline', pipeline));
}
}
@ -63,7 +92,7 @@ export class DeletePipelineAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<PipelineArgs>();
const pipeline = await this.prismaService.pipeline.findFirst({
const pipeline = await this.prismaService.client.pipeline.findFirst({
where: args.where,
});
assert(pipeline, '', NotFoundException);

View File

@ -13,10 +13,12 @@ import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { RefreshTokenWhereInput } from 'src/core/@generated/refresh-token/refresh-token-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
class RefreshTokenArgs {
where?: RefreshTokenWhereInput;
[key: string]: any;
}
@Injectable()
@ -35,7 +37,23 @@ export class ReadRefreshTokenAbilityHandler implements IAbilityHandler {
@Injectable()
export class CreateRefreshTokenAbilityHandler implements IAbilityHandler {
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'RefreshToken',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'RefreshToken');
}
}
@ -47,11 +65,24 @@ export class UpdateRefreshTokenAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<RefreshTokenArgs>();
const refreshToken = await this.prismaService.refreshToken.findFirst({
where: args.where,
});
const refreshToken = await this.prismaService.client.refreshToken.findFirst(
{
where: args.where,
},
);
assert(refreshToken, '', NotFoundException);
const allowed = await relationAbilityChecker(
'RefreshToken',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(
AbilityAction.Update,
subject('RefreshToken', refreshToken),
@ -66,9 +97,11 @@ export class DeleteRefreshTokenAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<RefreshTokenArgs>();
const refreshToken = await this.prismaService.refreshToken.findFirst({
where: args.where,
});
const refreshToken = await this.prismaService.client.refreshToken.findFirst(
{
where: args.where,
},
);
assert(refreshToken, '', NotFoundException);
return ability.can(

View File

@ -12,11 +12,13 @@ import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interfac
import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { assert } from 'src/utils/assert';
import { UserWhereInput } from 'src/core/@generated/user/user-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
class UserArgs {
where?: UserWhereInput;
[key: string]: any;
}
@Injectable()
@ -35,7 +37,23 @@ export class ReadUserAbilityHandler implements IAbilityHandler {
@Injectable()
export class CreateUserAbilityHandler implements IAbilityHandler {
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'User',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'User');
}
}
@ -47,11 +65,22 @@ export class UpdateUserAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<UserArgs>();
const user = await this.prismaService.user.findFirst({
const user = await this.prismaService.client.user.findFirst({
where: args.where,
});
assert(user, '', NotFoundException);
const allowed = await relationAbilityChecker(
'User',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Update, subject('User', user));
}
}
@ -63,7 +92,7 @@ export class DeleteUserAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<UserArgs>();
const user = await this.prismaService.user.findFirst({
const user = await this.prismaService.client.user.findFirst({
where: args.where,
});
assert(user, '', NotFoundException);

View File

@ -13,10 +13,12 @@ import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { WorkspaceMemberWhereInput } from 'src/core/@generated/workspace-member/workspace-member-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
class WorksapceMemberArgs {
class WorkspaceMemberArgs {
where?: WorkspaceMemberWhereInput;
[key: string]: any;
}
@Injectable()
@ -35,7 +37,23 @@ export class ReadWorkspaceMemberAbilityHandler implements IAbilityHandler {
@Injectable()
export class CreateWorkspaceMemberAbilityHandler implements IAbilityHandler {
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'WorkspaceMember',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'WorkspaceMember');
}
}
@ -46,12 +64,24 @@ export class UpdateWorkspaceMemberAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<WorksapceMemberArgs>();
const workspaceMember = await this.prismaService.workspaceMember.findFirst({
where: args.where,
});
const args = gqlContext.getArgs<WorkspaceMemberArgs>();
const workspaceMember =
await this.prismaService.client.workspaceMember.findFirst({
where: args.where,
});
assert(workspaceMember, '', NotFoundException);
const allowed = await relationAbilityChecker(
'WorkspaceMember',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(
AbilityAction.Update,
subject('WorkspaceMember', workspaceMember),
@ -65,10 +95,11 @@ export class DeleteWorkspaceMemberAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<WorksapceMemberArgs>();
const workspaceMember = await this.prismaService.workspaceMember.findFirst({
where: args.where,
});
const args = gqlContext.getArgs<WorkspaceMemberArgs>();
const workspaceMember =
await this.prismaService.client.workspaceMember.findFirst({
where: args.where,
});
assert(workspaceMember, '', NotFoundException);
return ability.can(

View File

@ -1,24 +1,22 @@
import {
ExecutionContext,
ForbiddenException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { subject } from '@casl/ability';
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
import { PrismaService } from 'src/database/prisma.service';
import { AbilityAction } from 'src/ability/ability.action';
import { AppAbility } from 'src/ability/ability.factory';
import { WorkspaceWhereInput } from 'src/core/@generated/workspace/workspace-where.input';
import { relationAbilityChecker } from 'src/ability/ability.util';
import { assert } from 'src/utils/assert';
import { getRequest } from 'src/utils/extract-request';
class WorksapceArgs {
class WorkspaceArgs {
where?: WorkspaceWhereInput;
[key: string]: any;
}
@Injectable()
@ -37,7 +35,23 @@ export class ReadWorkspaceAbilityHandler implements IAbilityHandler {
@Injectable()
export class CreateWorkspaceAbilityHandler implements IAbilityHandler {
handle(ability: AppAbility) {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs();
const allowed = await relationAbilityChecker(
'Workspace',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Create, 'Workspace');
}
}
@ -47,15 +61,25 @@ export class UpdateWorkspaceAbilityHandler implements IAbilityHandler {
constructor(private readonly prismaService: PrismaService) {}
async handle(ability: AppAbility, context: ExecutionContext) {
const request = getRequest(context);
assert(request.user.workspace.id, '', ForbiddenException);
const workspace = await this.prismaService.workspace.findUnique({
where: { id: request.user.workspace.id },
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<WorkspaceArgs>();
const workspace = await this.prismaService.client.workspace.findFirst({
where: args.where,
});
assert(workspace, '', NotFoundException);
return ability.can(AbilityAction.Update, subject('Workspace', workspace));
const allowed = await relationAbilityChecker(
'Workspace',
ability,
this.prismaService.client,
args,
);
if (!allowed) {
return false;
}
return ability.can(AbilityAction.Update, 'Workspace');
}
}
@ -65,12 +89,12 @@ export class DeleteWorkspaceAbilityHandler implements IAbilityHandler {
async handle(ability: AppAbility, context: ExecutionContext) {
const gqlContext = GqlExecutionContext.create(context);
const args = gqlContext.getArgs<WorksapceArgs>();
const workspace = await this.prismaService.workspace.findFirst({
const args = gqlContext.getArgs<WorkspaceArgs>();
const workspace = await this.prismaService.client.workspace.findFirst({
where: args.where,
});
assert(workspace, '', NotFoundException);
return ability.can(AbilityAction.Delete, subject('Workspace', workspace));
return ability.can(AbilityAction.Delete, 'Workspace');
}
}