forked from wangziqi/gongxue-base
96 lines
3.2 KiB
TypeScript
96 lines
3.2 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { ForbiddenException } from '@nestjs/common';
|
|
import { CaslAbilityFactory } from './casl-ability.factory';
|
|
import { AppAbility, AppSubject, AuthorizationRequest } from './interfaces';
|
|
import { CaslAction, permissionCodeSubject } from './casl.constants';
|
|
|
|
/**
|
|
* Generic authorization service usable both inside and outside of HTTP
|
|
* request context.
|
|
*
|
|
* ### HTTP use
|
|
* Inject `AuthorizationService` into controllers/services and call
|
|
* `abilityForRequest(req)` to get the current user's ability.
|
|
*
|
|
* ### Non-HTTP use (Agent Tool, background job, etc.)
|
|
* Build an ability via `abilityFactory.createForUser(user)` and pass it
|
|
* to `assert` / `can` directly.
|
|
*/
|
|
@Injectable()
|
|
export class AuthorizationService {
|
|
constructor(private readonly abilityFactory: CaslAbilityFactory) {}
|
|
|
|
/**
|
|
* Build an {@link AppAbility} for the current HTTP request.
|
|
*
|
|
* @param req — Express/NestJS request with `req.user` populated by JWT.
|
|
*/
|
|
abilityForRequest(req: AuthorizationRequest): AppAbility {
|
|
if (!req.user) {
|
|
throw new ForbiddenException('缺少可信授权身份');
|
|
}
|
|
return this.abilityFactory.createForUser(req.user);
|
|
}
|
|
|
|
/**
|
|
* Assert that the given ability allows the action on the subject.
|
|
* Throws `ForbiddenException` on denial.
|
|
*/
|
|
assertAbility(ability: AppAbility, action: CaslAction, subject: AppSubject): void {
|
|
if (!ability.can(action, subject)) {
|
|
throw new ForbiddenException(
|
|
`权限不足:${action} ${typeof subject === 'string' ? subject : 'resource'}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check whether the given ability allows the action on the subject.
|
|
* Returns boolean — never throws.
|
|
*/
|
|
canAbility(ability: AppAbility, action: CaslAction, subject: AppSubject): boolean {
|
|
return ability.can(action, subject);
|
|
}
|
|
|
|
/**
|
|
* Check whether the given ability allows the exact permission code.
|
|
* Uses `CaslAction.Access` with `permissionCodeSubject(code)` — the same
|
|
* mechanism as {@link PermissionGuard}.
|
|
*
|
|
* Returns boolean — never throws.
|
|
*/
|
|
canPermission(ability: AppAbility, permissionCode: string): boolean {
|
|
return ability.can(CaslAction.Access, permissionCodeSubject(permissionCode));
|
|
}
|
|
|
|
/**
|
|
* Assert that the given ability allows the exact permission code.
|
|
* Throws `ForbiddenException` on denial.
|
|
*/
|
|
assertPermission(ability: AppAbility, permissionCode: string): void {
|
|
if (!this.canPermission(ability, permissionCode)) {
|
|
throw new ForbiddenException(
|
|
`权限不足:缺少权限码 ${permissionCode}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Assert that the user (from request) can perform an action.
|
|
* Convenience shorthand — builds ability from request.
|
|
*/
|
|
assert(req: AuthorizationRequest, action: CaslAction, subject: AppSubject): void {
|
|
const ability = this.abilityForRequest(req);
|
|
this.assertAbility(ability, action, subject);
|
|
}
|
|
|
|
/**
|
|
* Check that the user (from request) can perform an action.
|
|
* Convenience shorthand — builds ability from request.
|
|
*/
|
|
can(req: AuthorizationRequest, action: CaslAction, subject: AppSubject): boolean {
|
|
const ability = this.abilityForRequest(req);
|
|
return this.canAbility(ability, action, subject);
|
|
}
|
|
}
|