66 lines
2.7 KiB
TypeScript
66 lines
2.7 KiB
TypeScript
/**
|
||
* 能力可见性(任务 A3.5):按身份与白名单决定谁能看到哪些能力。
|
||
*
|
||
* 两条边界(架构设计 5.1):
|
||
* - 工具列表与能力清单必须来自同一次过滤,否则模型能看到用户看不到的能力
|
||
* - 权限归业务系统(D9),这里只管「平台把哪些能力暴露给这个身份」,不管业务系统内部的角色
|
||
*
|
||
* 默认策略:开发期 defaultAllow=true(未配白名单时全部可见)。接入真实身份(A4.1)后应改成 false,
|
||
* 由授权表逐条给出——这与治理模型里「未登记默认拒绝」是同一条原则。
|
||
*/
|
||
import type { Capability } from '@aps/provider-sdk';
|
||
|
||
export interface CapabilityGrant {
|
||
/** 空表示不限用户 */
|
||
userId?: string;
|
||
/** 空表示不限租户 */
|
||
tenantId?: string;
|
||
/** 允许的能力 id 前缀,如 aps.schedule 或 fake. */
|
||
allow: string[];
|
||
}
|
||
|
||
export interface CapabilityVisibility {
|
||
defaultAllow: boolean;
|
||
denyWrite: boolean;
|
||
grants: CapabilityGrant[];
|
||
}
|
||
|
||
export interface VisibilitySubject {
|
||
userId: string;
|
||
tenantId: string;
|
||
}
|
||
|
||
const matchesPrefix = (capabilityId: string, prefixes: string[]): boolean =>
|
||
prefixes.some((prefix) => (prefix.endsWith('*') ? capabilityId.startsWith(prefix.slice(0, -1)) : capabilityId === prefix));
|
||
|
||
const grantApplies = (grant: CapabilityGrant, subject: VisibilitySubject): boolean => {
|
||
if (grant.userId !== undefined && grant.userId !== subject.userId) return false;
|
||
if (grant.tenantId !== undefined && grant.tenantId !== subject.tenantId) return false;
|
||
return true;
|
||
};
|
||
|
||
/** 这个身份能看到这个能力吗 */
|
||
export function isCapabilityVisible(capability: Capability, visibility: CapabilityVisibility, subject: VisibilitySubject): boolean {
|
||
// 写能力在只读模式下对所有人不可见(演示、审计或只读部署用)
|
||
if (visibility.denyWrite && capability.effect === 'write') return false;
|
||
|
||
const granted = visibility.grants.some((grant) => grantApplies(grant, subject) && matchesPrefix(capability.id, grant.allow));
|
||
if (granted) return true;
|
||
if (!visibility.defaultAllow) return false;
|
||
|
||
// 默认允许模式下仍可用 denyWrite 收口;白名单里的 deny 规则暂不做,等 A4 的策略层
|
||
return true;
|
||
}
|
||
|
||
export function visibleCapabilities(
|
||
capabilities: Capability[],
|
||
visibility: CapabilityVisibility,
|
||
subject: VisibilitySubject,
|
||
): Capability[] {
|
||
return capabilities.filter((capability) => isCapabilityVisible(capability, visibility, subject));
|
||
}
|
||
|
||
/** 能力集的指纹:用于判断运行时会话是否需要重建(工具集变了就得重建) */
|
||
export function capabilityFingerprint(capabilities: Capability[]): string {
|
||
return [...capabilities.map((capability) => capability.id)].sort().join(',');
|
||
} |