forked from wangziqi/gongxue-base
51 lines
1.7 KiB
TypeScript
51 lines
1.7 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { CaslAbilityFactory } from '../authorization/casl-ability.factory';
|
|
import { CaslAction, SubjectName } from '../authorization/casl.constants';
|
|
import type { AgentToolContext } from '../agent-tools/agent-tool.types';
|
|
import type { StudentAccessScope } from './student-access-scope';
|
|
|
|
/**
|
|
* Constructs a {@link StudentAccessScope} from the trusted server-side
|
|
* principal carried in {@link AgentToolContext}.
|
|
*
|
|
* ## Scope rules
|
|
*
|
|
* | Condition | Scope |
|
|
* |---|---|
|
|
* | `isSuperAdmin` | `manageAll` |
|
|
* | `class:edit` domain ability (`Update Class`) | `manageAll` |
|
|
* | Everything else | `teacher(userId)` |
|
|
*
|
|
* These rules mirror the HTTP-layer logic so agent tools are consistent
|
|
* with the web dashboard. The ability is constructed fresh from the
|
|
* principal each time — callers cannot pre-forge it.
|
|
*/
|
|
@Injectable()
|
|
export class StudentAccessScopeFactory {
|
|
constructor(private readonly abilityFactory: CaslAbilityFactory) {}
|
|
|
|
/**
|
|
* Build a scope from the authenticated context.
|
|
*
|
|
* The ability is constructed from the principal fields inside the context
|
|
* — every call is a fresh derivation.
|
|
*/
|
|
buildScope(context: AgentToolContext): StudentAccessScope {
|
|
const ability = this.abilityFactory.createForUser({
|
|
permissions: context.permissions,
|
|
isSuperAdmin: context.isSuperAdmin,
|
|
});
|
|
|
|
if (context.isSuperAdmin) {
|
|
return { type: 'manageAll' };
|
|
}
|
|
|
|
// class:edit (Update Class) grants full student scope
|
|
if (ability.can(CaslAction.Update, SubjectName.Class)) {
|
|
return { type: 'manageAll' };
|
|
}
|
|
|
|
return { type: 'teacher', userId: context.userId };
|
|
}
|
|
}
|