Archived
org domain:claims 归一、角色→端与锁死不变量、名册差异、测试账号策略(ADR-012,纯函数)
为统一鉴权中心(Keycloak)落地铺地基:五个零 IO 的纯函数文件,后续 org 用例 重写(Task 8)直接复用。本任务只加不删 —— 旧的岗位授权 / 口令会话 / 停用检查 原样保留,现役 org-service 仍在用它们。 - claims.ts:token claims → Identity,滤掉三种 Keycloak 家具角色 - ends.ts:角色 → 端的并集,并守住「改完之后至少一人能进运营中心」这条锁死不变量 - roster-diff.ts:与 Keycloak 名册的插/改/停/复活差异(远端说了算) - test-account.ts:测试账号判定与独立工作组构造(ADR-011 语义原样搬) - in-flight.ts:停用前置检查要看的在途工作(与旧 disable-check.ts 并存一轮) contract.ts / port.ts / index.ts / seed-data.ts 均为文件末尾追加,契约变更已 经 pnpm api:check --update 刷新快照。
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* token claims → 一个人(纯函数,零 IO;ADR-012)。
|
||||
*
|
||||
* 只认手册 §17 列出的那几个 claim;`name` 缺了回退用户名(服务账号常常没有 name)。
|
||||
* Keycloak 给每个用户自动挂的三个「家具角色」不是业务角色,这里滤掉:
|
||||
* `default-roles-<realm>` / `offline_access` / `uma_authorization`。
|
||||
*/
|
||||
import type { Identity } from '../../contract.ts';
|
||||
import type { VerifiedClaims } from '../../port.ts';
|
||||
|
||||
const NOISE_ROLES: ReadonlySet<string> = new Set(['offline_access', 'uma_authorization']);
|
||||
|
||||
export function isNoiseRole(role: string): boolean {
|
||||
return NOISE_ROLES.has(role) || role.startsWith('default-roles-');
|
||||
}
|
||||
|
||||
/** 去重、滤噪声、排序 —— 角色快照要能逐字比对(「变了没有」靠它) */
|
||||
export function normalizeRoles(roles: readonly string[]): string[] {
|
||||
return [...new Set(roles.filter((r) => r.trim() !== '' && !isNoiseRole(r)))].sort();
|
||||
}
|
||||
|
||||
export function identityOf(claims: VerifiedClaims): Identity {
|
||||
const account = claims.preferred_username;
|
||||
const name = claims.name === undefined || claims.name.trim() === '' ? account : claims.name.trim();
|
||||
const email = claims.email === undefined || claims.email.trim() === '' ? undefined : claims.email.trim();
|
||||
return {
|
||||
sub: claims.sub,
|
||||
account,
|
||||
name,
|
||||
roles: normalizeRoles(claims.realm_access?.roles ?? []),
|
||||
...(email === undefined ? {} : { email }),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user