forked from wangziqi/gongxue-base
feat: add CASL authorization and AI configuration
This commit is contained in:
95
apps/server/src/authorization/authorization.service.ts
Normal file
95
apps/server/src/authorization/authorization.service.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user