@things-factory/auth-base 10.1.198 → 10.1.203

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist-client/tsconfig.tsbuildinfo +1 -1
  2. package/dist-server/ai-entry/access-assistant.js +4 -3
  3. package/dist-server/ai-entry/access-assistant.js.map +1 -1
  4. package/dist-server/controllers/access-request.d.ts +8 -2
  5. package/dist-server/controllers/access-request.js +33 -6
  6. package/dist-server/controllers/access-request.js.map +1 -1
  7. package/dist-server/service/access-request/access-request-query.d.ts +7 -2
  8. package/dist-server/service/access-request/access-request-query.js +25 -4
  9. package/dist-server/service/access-request/access-request-query.js.map +1 -1
  10. package/dist-server/service/access-request/access-request-types.d.ts +7 -0
  11. package/dist-server/service/access-request/access-request-types.js +27 -1
  12. package/dist-server/service/access-request/access-request-types.js.map +1 -1
  13. package/dist-server/service/access-request/access-request.d.ts +1 -0
  14. package/dist-server/service/access-request/access-request.js +5 -0
  15. package/dist-server/service/access-request/access-request.js.map +1 -1
  16. package/dist-server/service/access-request/index.d.ts +1 -1
  17. package/dist-server/service/audit/index.d.ts +1 -1
  18. package/dist-server/service/index.d.ts +1 -1
  19. package/dist-server/service/open-fields.js +1 -0
  20. package/dist-server/service/open-fields.js.map +1 -1
  21. package/dist-server/service/role-template/index.d.ts +1 -1
  22. package/dist-server/service/role-template/role-task.d.ts +7 -0
  23. package/dist-server/service/role-template/role-task.js +16 -0
  24. package/dist-server/service/role-template/role-task.js.map +1 -1
  25. package/dist-server/tsconfig.tsbuildinfo +1 -1
  26. package/dist-server/utils/access-request-review.d.ts +12 -2
  27. package/dist-server/utils/access-request-review.js +13 -3
  28. package/dist-server/utils/access-request-review.js.map +1 -1
  29. package/package.json +2 -2
  30. package/tests/access-assistant-db.test.ts +1 -1
  31. package/tests/access-request-db.test.ts +71 -0
  32. package/tests/access-request-review.test.ts +30 -0
  33. package/translations/en.json +2 -1
  34. package/translations/es.json +2 -1
  35. package/translations/ja.json +2 -1
  36. package/translations/ko.json +2 -1
  37. package/translations/ms.json +2 -1
  38. package/translations/zh.json +2 -1
  39. package/dist-server/service/login-history/index.d.ts +0 -4
  40. package/dist-server/service/login-history/index.js +0 -8
  41. package/dist-server/service/login-history/index.js.map +0 -1
  42. package/dist-server/service/login-history/login-history-query.d.ts +0 -10
  43. package/dist-server/service/login-history/login-history-query.js +0 -80
  44. package/dist-server/service/login-history/login-history-query.js.map +0 -1
  45. package/dist-server/service/login-history/login-history-type.d.ts +0 -5
  46. package/dist-server/service/login-history/login-history-type.js +0 -21
  47. package/dist-server/service/login-history/login-history-type.js.map +0 -1
  48. package/dist-server/service/login-history/login-history.d.ts +0 -12
  49. package/dist-server/service/login-history/login-history.js +0 -58
  50. package/dist-server/service/login-history/login-history.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"access-request.js","sourceRoot":"","sources":["../../server/controllers/access-request.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { applyDomainInheritanceFilter, Domain, getRepository, InheritedValueType, Refusal, whenCommitted } from '@things-factory/shell'\n\nimport { AccessRequest, AccessRequestStatus } from '../service/access-request/access-request.js'\nimport { Privilege } from '../service/privilege/privilege.js'\nimport { Role } from '../service/role/role.js'\nimport { User, UserStatus } from '../service/user/user.js'\nimport {\n AccessDecisionKind,\n DecisionFacts,\n DecisionIssue,\n effectiveState,\n expiryFrom,\n isBlocked,\n reviewAccessDecision,\n ROLE_GRANTING_PRIVILEGE\n} from '../utils/access-request-review.js'\nimport { assignRoles, findAssignableRoles, mayChangeParentRoles, whereAssignableBy } from '../utils/assignable-roles.js'\nimport { judgePermission } from '../utils/check-permission.js'\nimport { findDomainOwnerUserIds, isDomainOwner } from '../utils/domain-ownership.js'\n\n/*\n * Access requests - asking, withdrawing, reviewing and deciding (ADR-0103, docs/design/access-request.md).\n *\n * Every door reads the domain and the person from the request, never from an argument.\n */\n\nexport type NewAccessRequestInput = { category: string; privilege: string; operationName?: string; reason?: string }\nexport type AccessDecisionInput = { requestId: string; decision: AccessDecisionKind; roleId?: string; reason?: string }\n\n/** Refuses a decision whose review blocks it - the first blocking issue names the refusal, all codes go along. */\nexport class AccessDecisionRefused extends Refusal {\n constructor(readonly issues: DecisionIssue[]) {\n const first = issues.find(issue => issue.blocking)\n super(`access-decision-${first.code}`, { codes: issues.filter(i => i.blocking).map(i => i.code).join(',') }, `The decision cannot be made: ${first.code}.`)\n }\n}\n\n/* ── asking ──────────────────────────────────────────────────────────────── */\n\n/**\n * The caller asks for `category:privilege` in this domain.\n *\n * The server judges again whether a role would open it - the `because` a screen got with its refusal is not\n * taken on trust (ADR-0103 decision 2). Asking again while one is pending adds to that one.\n */\nexport async function requestAccess(input: NewAccessRequestInput, context: ResolverContext): Promise<AccessRequest> {\n const { domain, user, tx, unsafeIP, prohibitedPrivileges } = context.state\n const { category, privilege } = input\n const now = new Date()\n\n if (!category || !privilege) {\n throw new Refusal('access-request-names-nothing', {}, 'An access request names a category and a privilege.')\n }\n\n const known = await getRepository(Privilege, tx).findOneBy({ category, name: privilege })\n if (!known) {\n throw new Refusal('access-request-unknown-privilege', { privilege: `${category}:${privilege}` }, `There is no privilege ${category}:${privilege}.`)\n }\n\n /* Asking is not a reflex - the reason is what the person deciding weighs, so there is no request without one. */\n const reason = input.reason?.trim()\n if (!reason) {\n throw new Refusal('access-request-needs-reason', {}, 'Write why you need it.')\n }\n\n const judgement = await judgePermission({ category, privilege }, user, domain, unsafeIP, prohibitedPrivileges)\n if (judgement.permitted === true) {\n throw new Refusal('access-request-already-holds', { privilege: `${category}:${privilege}` }, `You already have ${category}:${privilege}.`)\n }\n if (judgement.because !== 'role') {\n throw new Refusal(\n 'access-request-not-by-role',\n { privilege: `${category}:${privilege}`, because: judgement.because },\n `A role would not open ${category}:${privilege} here (${judgement.because}).`\n )\n }\n\n await holdRequester(user.id, tx)\n\n /*\n * Denied lately: asking again is allowed, the same words are not - the person deciding has already answered them.\n * The screen shows that answer before the person writes (ACCESS_REQUEST_DENIAL_DAYS).\n */\n const denied = await latestDenial(domain, user.id, category, privilege, now, tx)\n if (denied && (denied.reason || '').trim() === reason) {\n throw new Refusal(\n 'access-request-same-reason-as-denied',\n { reason: denied.decisionReason || '' },\n 'This was denied for the same reason lately - write what has changed.'\n )\n }\n\n const repository = getRepository(AccessRequest, tx)\n const pending = await findMine(repository, domain.id, user.id, category, privilege, AccessRequestStatus.PENDING, tx)\n\n if (pending && effectiveState(pending.state, pending.expiresAt, now) === AccessRequestStatus.PENDING) {\n pending.count = (pending.count || 1) + 1\n pending.lastRequestedAt = now\n pending.reason = reason\n if (input.operationName) pending.operationName = input.operationName\n /* Merged - the people deciding were told once, when it was first asked. */\n return await repository.save(pending)\n }\n\n if (pending) {\n pending.state = AccessRequestStatus.EXPIRED\n await repository.save(pending)\n }\n\n /*\n * Withdrawn lately: the same request comes back - one row, and nobody is told again. Asking and withdrawing in a loop\n * would otherwise make a row and a notification to every decider on each turn.\n */\n const withdrawn = await findMine(repository, domain.id, user.id, category, privilege, AccessRequestStatus.WITHDRAWN, tx)\n if (withdrawn && now.getTime() - new Date(withdrawn.updatedAt).getTime() <= ACCESS_REQUEST_QUIET_HOURS * 60 * 60 * 1000) {\n withdrawn.state = AccessRequestStatus.PENDING\n withdrawn.count = (withdrawn.count || 1) + 1\n withdrawn.lastRequestedAt = now\n withdrawn.reason = reason\n withdrawn.expiresAt = expiryFrom(now)\n if (input.operationName) withdrawn.operationName = input.operationName\n return await repository.save(withdrawn)\n }\n\n const created = await repository.save({\n domain,\n requester: user,\n category,\n privilege,\n operationName: input.operationName,\n reason,\n count: 1,\n lastRequestedAt: now,\n state: AccessRequestStatus.PENDING,\n expiresAt: expiryFrom(now)\n } as Partial<AccessRequest>)\n\n const deciders = await findDeciderIds(domain, user.id, tx)\n tellLater(context, 'notify the people who decide access requests', async () =>\n notifyEach(context, deciders, 'access-request.notify.asked', {\n name: user.name || user.email || user.username,\n privilege: `${category}:${privilege}`\n }, `access-requests?id=${created.id}`)\n )\n\n return created\n}\n\n/** A request withdrawn within this many hours comes back as the same row, without telling anyone again. */\nexport const ACCESS_REQUEST_QUIET_HOURS = 24\n\n/**\n * The caller's latest request of one privilege in one state. On a database with row locks it is a locking read: after\n * `holdRequester` waits, a plain read under REPEATABLE READ (MySQL's default) would still see the snapshot taken before\n * the other ask committed, and insert a second pending row. A locking read sees the committed row.\n */\nasync function findMine(repository: any, domainId: string, requesterId: string, category: string, privilege: string, state: AccessRequestStatus, tx?: EntityManager) {\n const qb = repository\n .createQueryBuilder('mine')\n .leftJoinAndSelect('mine.domain', 'mine_domain')\n .leftJoinAndSelect('mine.requester', 'mine_requester')\n .where('mine.domain = :domainId AND mine.requester = :requesterId', { domainId, requesterId })\n .andWhere('mine.category = :category AND mine.privilege = :privilege AND mine.state = :state', { category, privilege, state })\n .orderBy('mine.updatedAt', 'DESC')\n /* Lowercase alias: postgres folds the unquoted name in `FOR UPDATE OF`, and only the request row is locked - not the outer-joined ones. */\n if (tx && !(tx.connection?.options?.type as string)?.includes('sqlite')) qb.setLock('pessimistic_write', undefined, ['mine'])\n return await qb.getOne()\n}\n\n/**\n * At most one pending request per person and privilege, under concurrency. A partial unique index would say it and\n * is not portable, so the person's own row is locked for the rest of the transaction: a second ask of the same\n * moment waits, then finds the first and adds to it (measured on postgres - access-request-pg.test.ts).\n *\n * sqlite has no row locks and one connection, so its transactions already run one after another.\n */\nasync function holdRequester(userId: string, tx?: EntityManager) {\n if (!tx || (tx.connection?.options?.type as string)?.includes('sqlite')) return\n await tx.getRepository(User).createQueryBuilder('HELD').setLock('pessimistic_write').where('HELD.id = :id', { id: userId }).getOne()\n}\n\n/** How long a denial is shown back to the person and holds against the same words (questions Q9 · 5). */\nexport const ACCESS_REQUEST_DENIAL_DAYS = 7\n\n/** The latest denial of this person and privilege within ACCESS_REQUEST_DENIAL_DAYS, if any. */\nexport async function latestDenial(domain: Domain, requesterId: string, category: string, privilege: string, now: Date, tx?: EntityManager) {\n const denied = await getRepository(AccessRequest, tx).findOne({\n where: { domain: { id: domain.id }, requester: { id: requesterId }, category, privilege, state: AccessRequestStatus.DENIED },\n order: { decidedAt: 'DESC' }\n })\n if (!denied?.decidedAt) return null\n return now.getTime() - new Date(denied.decidedAt).getTime() <= ACCESS_REQUEST_DENIAL_DAYS * 24 * 60 * 60 * 1000 ? denied : null\n}\n\n/** The requester takes back their own pending request. */\nexport async function withdrawAccessRequest(id: string, context: ResolverContext): Promise<AccessRequest> {\n const { domain, user, tx } = context.state\n const repository = getRepository(AccessRequest, tx)\n\n const updated = await repository\n .createQueryBuilder()\n .update(AccessRequest)\n .set({ state: AccessRequestStatus.WITHDRAWN })\n .where('id = :id AND domain_id = :domainId AND requester_id = :userId AND state = :pending', {\n id,\n domainId: domain.id,\n userId: user.id,\n pending: AccessRequestStatus.PENDING\n })\n .execute()\n\n if (!updated.affected) {\n throw new Refusal('access-request-not-withdrawable', { id }, 'There is no pending request of yours to withdraw.')\n }\n return await repository.findOne({ where: { id }, relations: ['domain', 'requester'] })\n}\n\n/* ── who decides ─────────────────────────────────────────────────────────── */\n\n/**\n * The people who can decide: domain owners and holders of the role-granting privilege - the same pair the\n * decision door declares (`@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)`).\n * The requester is never one of them.\n */\nexport async function findDeciderIds(domain: Domain, requesterId: string, tx?: EntityManager): Promise<string[]> {\n const owners = await findDomainOwnerUserIds(domain, tx)\n\n const qb = getRepository(User, tx)\n .createQueryBuilder('USER')\n .select('USER.id', 'id')\n .innerJoin('USER.domains', 'MEMBER', 'MEMBER.id = :memberOf', { memberOf: domain.id })\n .innerJoin('USER.roles', 'role')\n .innerJoin('role.privileges', 'PRIVILEGE')\n .where('PRIVILEGE.category = :grantCategory', { grantCategory: ROLE_GRANTING_PRIVILEGE.category })\n .andWhere('PRIVILEGE.name = :grantName', { grantName: ROLE_GRANTING_PRIVILEGE.privilege })\n .andWhere('USER.status = :active', { active: UserStatus.ACTIVATED })\n applyDomainInheritanceFilter(qb, 'role', domain, InheritedValueType.Include)\n const holders = (await qb.getRawMany()).map(row => row.id)\n\n return [...new Set([...owners, ...holders])].filter(id => id && id !== requesterId).sort()\n}\n\n/* ── reviewing ───────────────────────────────────────────────────────────── */\n\n/** Every assignable role that grants `category:privilege`, smallest first. */\nexport async function rolesGranting(category: string, privilege: string, domain: Domain, tx?: EntityManager) {\n const qb = getRepository(Role, tx)\n .createQueryBuilder('ROLE')\n .innerJoin('ROLE.privileges', 'ASKED', 'ASKED.category = :askedCategory AND ASKED.name = :askedName', {\n askedCategory: category,\n askedName: privilege\n })\n const roles = await whereAssignableBy(qb, 'ROLE', domain).getMany()\n if (roles.length === 0) return []\n\n const withPrivileges = await getRepository(Role, tx).find({ where: { id: In(roles.map(role => role.id)) }, relations: ['privileges'] })\n return withPrivileges\n .map(role => ({ id: role.id, name: role.name, privilegeCount: role.privileges?.length ?? 0, role }))\n .sort((a, b) => a.privilegeCount - b.privilegeCount || a.name.localeCompare(b.name))\n}\n\n/** The facts `reviewAccessDecision` reads - collected as the person deciding, inside this domain. */\nexport async function collectDecisionFacts(input: AccessDecisionInput, context: ResolverContext): Promise<DecisionFacts> {\n const { domain, user: decider, tx } = context.state\n const now = new Date()\n\n const request = await getRepository(AccessRequest, tx).findOne({\n where: { id: input.requestId, domain: { id: domain.id } },\n relations: ['requester', 'requester.domains']\n })\n\n const base: DecisionFacts = {\n request: null,\n deciderId: decider.id,\n decision: input.decision,\n reason: input.reason,\n requester: null,\n requesterHoldsIt: false,\n candidates: [],\n deciderPrivileges: [],\n deciderIsOwner: false,\n now\n }\n if (!request) return base\n\n const requester = request.requester\n const [candidates, chosen, deciderIsOwner, deciderPrivileges, requesterHoldsIt, deniedBefore] = await Promise.all([\n rolesGranting(request.category, request.privilege, domain, tx),\n input.roleId ? findAssignableRoles([input.roleId], domain, tx) : Promise.resolve([]),\n isDomainOwner(domain, decider, tx),\n User.getPrivilegesByDomain(decider, domain),\n requester ? User.hasPrivilege(request.privilege, request.category, domain, requester) : Promise.resolve(false),\n getRepository(AccessRequest, tx).findOne({\n where: {\n domain: { id: domain.id },\n requester: { id: requester?.id },\n category: request.category,\n privilege: request.privilege,\n state: AccessRequestStatus.DENIED\n },\n relations: ['decidedBy'],\n order: { decidedAt: 'DESC' }\n })\n ])\n\n const role = chosen[0]\n ? await getRepository(Role, tx).findOne({ where: { id: chosen[0].id }, relations: ['privileges'] })\n : null\n const roleIsParents = !!role && !!domain.parentId && role.domainId === domain.parentId\n const deciderMayChangeParentRoles = roleIsParents ? await mayChangeParentRoles(decider, domain, tx) : false\n\n return {\n ...base,\n request: {\n state: request.state as any,\n requesterId: requester?.id,\n category: request.category,\n privilege: request.privilege,\n expiresAt: request.expiresAt\n },\n role: role\n ? { id: role.id, name: role.name, privileges: (role.privileges || []).map(p => ({ category: p.category, privilege: p.name })) }\n : null,\n roleNamedButNotAssignable: !!input.roleId && !role,\n roleIsParents,\n deciderMayChangeParentRoles,\n requester: requester\n ? {\n active: requester.status === UserStatus.ACTIVATED,\n inDomain: (requester.domains || []).some(d => d.id === domain.id)\n }\n : null,\n requesterHoldsIt,\n candidates: candidates.map(({ id, name, privilegeCount }) => ({ id, name, privilegeCount })),\n deciderIsOwner,\n deciderPrivileges: (deciderPrivileges || []).map((p: any) => ({ category: p.category, privilege: p.privilege })),\n deniedBefore:\n deniedBefore && deniedBefore.id !== request.id\n ? {\n by: deniedBefore.decidedBy?.name || deniedBefore.decidedBy?.email || '',\n at: deniedBefore.decidedAt,\n reason: deniedBefore.decisionReason\n }\n : null\n }\n}\n\nexport async function reviewDecision(input: AccessDecisionInput, context: ResolverContext): Promise<DecisionIssue[]> {\n return reviewAccessDecision(await collectDecisionFacts(input, context))\n}\n\n/* ── deciding ────────────────────────────────────────────────────────────── */\n\n/**\n * Grants or denies. The review runs again here - the card and the screen were shown a moment ago, and another\n * person may have decided since. The state moves PENDING → decided in one conditional update, so two people\n * deciding at once cannot both win.\n */\nexport async function decideAccessRequest(input: AccessDecisionInput, context: ResolverContext): Promise<AccessRequest> {\n const { domain, user: decider, tx } = context.state\n const now = new Date()\n\n const issues = await reviewDecision(input, context)\n if (isBlocked(issues)) {\n throw new AccessDecisionRefused(issues)\n }\n\n const repository = getRepository(AccessRequest, tx)\n const granting = input.decision === 'GRANT'\n\n const moved = await repository\n .createQueryBuilder()\n .update(AccessRequest)\n .set({\n state: granting ? AccessRequestStatus.GRANTED : AccessRequestStatus.DENIED,\n decidedBy: { id: decider.id } as User,\n decidedAt: now,\n decisionReason: input.reason?.trim() || null,\n grantedRole: granting ? ({ id: input.roleId } as Role) : null\n })\n .where('id = :id AND domain_id = :domainId AND state = :pending', {\n id: input.requestId,\n domainId: domain.id,\n pending: AccessRequestStatus.PENDING\n })\n .execute()\n\n if (!moved.affected) {\n throw new AccessDecisionRefused([{ code: 'request-not-pending', blocking: true, field: 'request' }])\n }\n\n const request = await repository.findOne({ where: { id: input.requestId }, relations: ['domain', 'requester', 'requester.roles', 'grantedRole'] })\n const requester = request.requester\n\n if (granting) {\n await assignRoles(requester, { add: [input.roleId] }, domain, tx, decider)\n await closeWhatTheGrantOpened(request, context)\n }\n\n tellLater(context, 'notify the requester of the decision', async () =>\n notifyEach(\n context,\n [requester.id],\n granting ? 'access-request.notify.granted' : 'access-request.notify.denied',\n { privilege: `${request.category}:${request.privilege}`, role: request.grantedRole?.name ?? '', reason: request.decisionReason ?? '' },\n /* The requester cannot read the list of everyone's requests - their own is where the answer is. */\n 'my-access-requests'\n )\n )\n\n return request\n}\n\n/**\n * Other pending requests of the same person that the role just given now answers - closed as granted with it.\n *\n * Judged by the granted role's own privileges, read in this transaction. `User.hasPrivilege` reads through the default\n * connection, which on a pooled database does not see the role added a moment ago in this uncommitted transaction.\n */\nasync function closeWhatTheGrantOpened(granted: AccessRequest, context: ResolverContext) {\n const { domain, user: decider, tx } = context.state\n const repository = getRepository(AccessRequest, tx)\n const role = await getRepository(Role, tx).findOne({ where: { id: granted.grantedRole?.id }, relations: ['privileges'] })\n const gives = new Set((role?.privileges || []).map(p => `${p.category}:${p.name}`))\n if (gives.size === 0) return\n\n const others = await repository.find({\n where: { domain: { id: domain.id }, requester: { id: granted.requester.id }, state: AccessRequestStatus.PENDING }\n })\n\n for (const other of others) {\n if (other.id === granted.id || !gives.has(`${other.category}:${other.privilege}`)) continue\n await repository.update(\n { id: other.id, state: AccessRequestStatus.PENDING },\n { state: AccessRequestStatus.GRANTED, decidedBy: { id: decider.id } as User, decidedAt: new Date(), grantedRole: { id: role.id } as Role }\n )\n }\n}\n\n/* ── telling ─────────────────────────────────────────────────────────────── */\n\n/** After the commit when there is a transaction, at once otherwise - a failed notification never fails the decision. */\nfunction tellLater(context: ResolverContext, name: string, run: () => Promise<void>) {\n const deferred = whenCommitted(context.state.afterCommit, name, run)\n if (!deferred) void run().catch(() => {})\n}\n\n/**\n * One notification per person, in that person's language (`User.locale`), linking to where they can act on it. `notify` is optional: it is there when\n * the notification module is loaded, and nothing happens otherwise - the request list is the record.\n */\nasync function notifyEach(\n context: ResolverContext,\n userIds: string[],\n key: string,\n params: Record<string, string>,\n url: string\n) {\n const notify = (context.state as any).notify\n if (typeof notify !== 'function' || userIds.length === 0) return\n\n const people = await getRepository(User).find({ where: { id: In(userIds) } })\n const byLocale = new Map<string, string[]>()\n for (const person of people) {\n const locale = person.locale || ''\n byLocale.set(locale, [...(byLocale.get(locale) || []), person.id])\n }\n\n for (const [locale, receivers] of byLocale) {\n const say = (suffix: string) => translate(context, `${key}.${suffix}`, params, locale)\n await notify({\n receivers,\n type: 'info',\n subject: 'access-request',\n title: say('title'),\n body: say('body'),\n url,\n mode: 'background'\n })\n }\n}\n\nfunction translate(context: any, key: string, params: Record<string, string>, locale: string): string {\n const i18next = context?.i18next\n if (locale && i18next?.t) return i18next.t(key, { ...params, lng: locale })\n return typeof context?.t === 'function' ? context.t(key, { ...params }) : key\n}\n"]}
1
+ {"version":3,"file":"access-request.js","sourceRoot":"","sources":["../../server/controllers/access-request.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { applyDomainInheritanceFilter, Domain, getRepository, InheritedValueType, Refusal, whenCommitted } from '@things-factory/shell'\n\nimport { AccessRequest, AccessRequestStatus } from '../service/access-request/access-request.js'\nimport { Privilege } from '../service/privilege/privilege.js'\nimport { Role } from '../service/role/role.js'\nimport { User, UserStatus } from '../service/user/user.js'\nimport {\n AccessDecisionKind,\n DecisionFacts,\n DecisionIssue,\n effectiveState,\n expiryFrom,\n isBlocked,\n reviewAccessDecision,\n ROLE_GRANTING_PRIVILEGE\n} from '../utils/access-request-review.js'\nimport { assignRoles, findAssignableRoles, mayChangeParentRoles, whereAssignableBy } from '../utils/assignable-roles.js'\nimport { judgePermission } from '../utils/check-permission.js'\nimport { declaredTaskKeys, roleTaskById } from '../service/role-template/role-task.js'\nimport { findDomainOwnerUserIds, isDomainOwner } from '../utils/domain-ownership.js'\n\n/*\n * Access requests - asking, withdrawing, reviewing and deciding (ADR-0103, docs/design/access-request.md).\n *\n * Every door reads the domain and the person from the request, never from an argument.\n */\n\nexport type NewAccessRequestInput = { category: string; privilege: string; operationName?: string; taskId?: string; reason?: string }\nexport type AccessDecisionInput = { requestId: string; decision: AccessDecisionKind; roleId?: string; reason?: string }\n\n/** Refuses a decision whose review blocks it - the first blocking issue names the refusal, all codes go along. */\nexport class AccessDecisionRefused extends Refusal {\n constructor(readonly issues: DecisionIssue[]) {\n const first = issues.find(issue => issue.blocking)\n super(`access-decision-${first.code}`, { codes: issues.filter(i => i.blocking).map(i => i.code).join(',') }, `The decision cannot be made: ${first.code}.`)\n }\n}\n\n/* ── asking ──────────────────────────────────────────────────────────────── */\n\n/**\n * The caller asks for `category:privilege` in this domain.\n *\n * The server judges again whether a role would open it - the `because` a screen got with its refusal is not\n * taken on trust (ADR-0103 decision 2). Asking again while one is pending adds to that one.\n */\nexport async function requestAccess(input: NewAccessRequestInput, context: ResolverContext): Promise<AccessRequest> {\n const { domain, user, tx, unsafeIP, prohibitedPrivileges } = context.state\n const { category, privilege } = input\n const now = new Date()\n\n if (!category || !privilege) {\n throw new Refusal('access-request-names-nothing', {}, 'An access request names a category and a privilege.')\n }\n\n const known = await getRepository(Privilege, tx).findOneBy({ category, name: privilege })\n if (!known) {\n throw new Refusal('access-request-unknown-privilege', { privilege: `${category}:${privilege}` }, `There is no privilege ${category}:${privilege}.`)\n }\n\n /* The task asked for is one that holds the refused privilege - a request for other work is not this request. */\n const taskId = input.taskId || undefined\n if (taskId) {\n const task = roleTaskById(taskId)\n if (!task || !task.grants.some(grant => grant.category === category && grant.axes.includes(privilege))) {\n throw new Refusal('access-request-task-mismatch', { task: taskId, privilege: `${category}:${privilege}` }, `The task ${taskId} does not hold ${category}:${privilege}.`)\n }\n }\n\n /* Asking is not a reflex - the reason is what the person deciding weighs, so there is no request without one. */\n const reason = input.reason?.trim()\n if (!reason) {\n throw new Refusal('access-request-needs-reason', {}, 'Write why you need it.')\n }\n\n const judgement = await judgePermission({ category, privilege }, user, domain, unsafeIP, prohibitedPrivileges)\n if (judgement.permitted === true) {\n throw new Refusal('access-request-already-holds', { privilege: `${category}:${privilege}` }, `You already have ${category}:${privilege}.`)\n }\n if (judgement.because !== 'role') {\n throw new Refusal(\n 'access-request-not-by-role',\n { privilege: `${category}:${privilege}`, because: judgement.because },\n `A role would not open ${category}:${privilege} here (${judgement.because}).`\n )\n }\n\n await holdRequester(user.id, tx)\n\n /*\n * Denied lately: asking again is allowed, the same words are not - the person deciding has already answered them.\n * The screen shows that answer before the person writes (ACCESS_REQUEST_DENIAL_DAYS).\n */\n const denied = await latestDenial(domain, user.id, category, privilege, now, tx)\n if (denied && (denied.reason || '').trim() === reason) {\n throw new Refusal(\n 'access-request-same-reason-as-denied',\n { reason: denied.decisionReason || '' },\n 'This was denied for the same reason lately - write what has changed.'\n )\n }\n\n const repository = getRepository(AccessRequest, tx)\n const pending = await findMine(repository, domain.id, user.id, category, privilege, AccessRequestStatus.PENDING, tx)\n\n if (pending && effectiveState(pending.state, pending.expiresAt, now) === AccessRequestStatus.PENDING) {\n pending.count = (pending.count || 1) + 1\n pending.lastRequestedAt = now\n pending.reason = reason\n if (input.operationName) pending.operationName = input.operationName\n /* Merged - the people deciding were told once, when it was first asked. */\n return await repository.save(pending)\n }\n\n if (pending) {\n pending.state = AccessRequestStatus.EXPIRED\n await repository.save(pending)\n }\n\n /*\n * Withdrawn lately: the same request comes back - one row, and nobody is told again. Asking and withdrawing in a loop\n * would otherwise make a row and a notification to every decider on each turn.\n */\n const withdrawn = await findMine(repository, domain.id, user.id, category, privilege, AccessRequestStatus.WITHDRAWN, tx)\n if (withdrawn && now.getTime() - new Date(withdrawn.updatedAt).getTime() <= ACCESS_REQUEST_QUIET_HOURS * 60 * 60 * 1000) {\n withdrawn.state = AccessRequestStatus.PENDING\n withdrawn.count = (withdrawn.count || 1) + 1\n withdrawn.lastRequestedAt = now\n withdrawn.reason = reason\n withdrawn.expiresAt = expiryFrom(now)\n if (input.operationName) withdrawn.operationName = input.operationName\n return await repository.save(withdrawn)\n }\n\n const created = await repository.save({\n domain,\n requester: user,\n category,\n privilege,\n operationName: input.operationName,\n taskId,\n reason,\n count: 1,\n lastRequestedAt: now,\n state: AccessRequestStatus.PENDING,\n expiresAt: expiryFrom(now)\n } as Partial<AccessRequest>)\n\n const deciders = await findDeciderIds(domain, user.id, tx)\n tellLater(context, 'notify the people who decide access requests', async () =>\n notifyEach(context, deciders, 'access-request.notify.asked', {\n name: user.name || user.email || user.username,\n privilege: `${category}:${privilege}`\n }, `access-requests?id=${created.id}`)\n )\n\n return created\n}\n\n/** A request withdrawn within this many hours comes back as the same row, without telling anyone again. */\nexport const ACCESS_REQUEST_QUIET_HOURS = 24\n\n/**\n * The caller's latest request of one privilege in one state. On a database with row locks it is a locking read: after\n * `holdRequester` waits, a plain read under REPEATABLE READ (MySQL's default) would still see the snapshot taken before\n * the other ask committed, and insert a second pending row. A locking read sees the committed row.\n */\nasync function findMine(repository: any, domainId: string, requesterId: string, category: string, privilege: string, state: AccessRequestStatus, tx?: EntityManager) {\n const qb = repository\n .createQueryBuilder('mine')\n .leftJoinAndSelect('mine.domain', 'mine_domain')\n .leftJoinAndSelect('mine.requester', 'mine_requester')\n .where('mine.domain = :domainId AND mine.requester = :requesterId', { domainId, requesterId })\n .andWhere('mine.category = :category AND mine.privilege = :privilege AND mine.state = :state', { category, privilege, state })\n .orderBy('mine.updatedAt', 'DESC')\n /* Lowercase alias: postgres folds the unquoted name in `FOR UPDATE OF`, and only the request row is locked - not the outer-joined ones. */\n if (tx && !(tx.connection?.options?.type as string)?.includes('sqlite')) qb.setLock('pessimistic_write', undefined, ['mine'])\n return await qb.getOne()\n}\n\n/**\n * At most one pending request per person and privilege, under concurrency. A partial unique index would say it and\n * is not portable, so the person's own row is locked for the rest of the transaction: a second ask of the same\n * moment waits, then finds the first and adds to it (measured on postgres - access-request-pg.test.ts).\n *\n * sqlite has no row locks and one connection, so its transactions already run one after another.\n */\nasync function holdRequester(userId: string, tx?: EntityManager) {\n if (!tx || (tx.connection?.options?.type as string)?.includes('sqlite')) return\n await tx.getRepository(User).createQueryBuilder('HELD').setLock('pessimistic_write').where('HELD.id = :id', { id: userId }).getOne()\n}\n\n/** How long a denial is shown back to the person and holds against the same words (questions Q9 · 5). */\nexport const ACCESS_REQUEST_DENIAL_DAYS = 7\n\n/** The latest denial of this person and privilege within ACCESS_REQUEST_DENIAL_DAYS, if any. */\nexport async function latestDenial(domain: Domain, requesterId: string, category: string, privilege: string, now: Date, tx?: EntityManager) {\n const denied = await getRepository(AccessRequest, tx).findOne({\n where: { domain: { id: domain.id }, requester: { id: requesterId }, category, privilege, state: AccessRequestStatus.DENIED },\n order: { decidedAt: 'DESC' }\n })\n if (!denied?.decidedAt) return null\n return now.getTime() - new Date(denied.decidedAt).getTime() <= ACCESS_REQUEST_DENIAL_DAYS * 24 * 60 * 60 * 1000 ? denied : null\n}\n\n/** The requester takes back their own pending request. */\nexport async function withdrawAccessRequest(id: string, context: ResolverContext): Promise<AccessRequest> {\n const { domain, user, tx } = context.state\n const repository = getRepository(AccessRequest, tx)\n\n const updated = await repository\n .createQueryBuilder()\n .update(AccessRequest)\n .set({ state: AccessRequestStatus.WITHDRAWN })\n .where('id = :id AND domain_id = :domainId AND requester_id = :userId AND state = :pending', {\n id,\n domainId: domain.id,\n userId: user.id,\n pending: AccessRequestStatus.PENDING\n })\n .execute()\n\n if (!updated.affected) {\n throw new Refusal('access-request-not-withdrawable', { id }, 'There is no pending request of yours to withdraw.')\n }\n return await repository.findOne({ where: { id }, relations: ['domain', 'requester'] })\n}\n\n/* ── who decides ─────────────────────────────────────────────────────────── */\n\n/**\n * The people who can decide: domain owners and holders of the role-granting privilege - the same pair the\n * decision door declares (`@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)`).\n * The requester is never one of them.\n */\nexport async function findDeciderIds(domain: Domain, requesterId: string, tx?: EntityManager): Promise<string[]> {\n const owners = await findDomainOwnerUserIds(domain, tx)\n\n const qb = getRepository(User, tx)\n .createQueryBuilder('USER')\n .select('USER.id', 'id')\n .innerJoin('USER.domains', 'MEMBER', 'MEMBER.id = :memberOf', { memberOf: domain.id })\n .innerJoin('USER.roles', 'role')\n .innerJoin('role.privileges', 'PRIVILEGE')\n .where('PRIVILEGE.category = :grantCategory', { grantCategory: ROLE_GRANTING_PRIVILEGE.category })\n .andWhere('PRIVILEGE.name = :grantName', { grantName: ROLE_GRANTING_PRIVILEGE.privilege })\n .andWhere('USER.status = :active', { active: UserStatus.ACTIVATED })\n applyDomainInheritanceFilter(qb, 'role', domain, InheritedValueType.Include)\n const holders = (await qb.getRawMany()).map(row => row.id)\n\n return [...new Set([...owners, ...holders])].filter(id => id && id !== requesterId).sort()\n}\n\n/* ── reviewing ───────────────────────────────────────────────────────────── */\n\n/**\n * Every assignable role that grants `category:privilege`. With a task asked for (ADR-0103 decision 9), the roles that\n * hold the whole task come first; then fewest privileges, then name. One order for the screen, the review and the AI\n * draft card.\n */\nexport async function rolesGranting(category: string, privilege: string, domain: Domain, tx?: EntityManager, taskId?: string | null) {\n const qb = getRepository(Role, tx)\n .createQueryBuilder('ROLE')\n .innerJoin('ROLE.privileges', 'ASKED', 'ASKED.category = :askedCategory AND ASKED.name = :askedName', {\n askedCategory: category,\n askedName: privilege\n })\n const roles = await whereAssignableBy(qb, 'ROLE', domain).getMany()\n if (roles.length === 0) return []\n\n const withPrivileges = await getRepository(Role, tx).find({ where: { id: In(roles.map(role => role.id)) }, relations: ['privileges'] })\n const task = taskId ? roleTaskById(taskId) : undefined\n const wanted = task ? declaredTaskKeys(task) : []\n return withPrivileges\n .map(role => {\n const held = new Set((role.privileges || []).map(p => `${p.category} ${p.name}`))\n return { id: role.id, name: role.name, privilegeCount: role.privileges?.length ?? 0, holdsTask: wanted.length > 0 && wanted.every(key => held.has(key)), role }\n })\n .sort((a, b) => Number(b.holdsTask) - Number(a.holdsTask) || a.privilegeCount - b.privilegeCount || a.name.localeCompare(b.name))\n}\n\n/** The task asked for, as the review reads it - nothing when none was asked or it is no longer registered. */\nfunction taskFacts(taskId?: string | null): DecisionFacts['task'] {\n const task = taskId ? roleTaskById(taskId) : undefined\n if (!task) return null\n return { id: task.id, privileges: declaredTaskKeys(task).map(key => ({ category: key.split(' ')[0], privilege: key.split(' ')[1] })) }\n}\n\n/** The facts `reviewAccessDecision` reads - collected as the person deciding, inside this domain. */\nexport async function collectDecisionFacts(input: AccessDecisionInput, context: ResolverContext): Promise<DecisionFacts> {\n const { domain, user: decider, tx } = context.state\n const now = new Date()\n\n const request = await getRepository(AccessRequest, tx).findOne({\n where: { id: input.requestId, domain: { id: domain.id } },\n relations: ['requester', 'requester.domains']\n })\n\n const base: DecisionFacts = {\n request: null,\n deciderId: decider.id,\n decision: input.decision,\n reason: input.reason,\n requester: null,\n requesterHoldsIt: false,\n candidates: [],\n deciderPrivileges: [],\n deciderIsOwner: false,\n now\n }\n if (!request) return base\n\n const requester = request.requester\n const [candidates, chosen, deciderIsOwner, deciderPrivileges, requesterHoldsIt, deniedBefore] = await Promise.all([\n rolesGranting(request.category, request.privilege, domain, tx, request.taskId),\n input.roleId ? findAssignableRoles([input.roleId], domain, tx) : Promise.resolve([]),\n isDomainOwner(domain, decider, tx),\n User.getPrivilegesByDomain(decider, domain),\n requester ? User.hasPrivilege(request.privilege, request.category, domain, requester) : Promise.resolve(false),\n getRepository(AccessRequest, tx).findOne({\n where: {\n domain: { id: domain.id },\n requester: { id: requester?.id },\n category: request.category,\n privilege: request.privilege,\n state: AccessRequestStatus.DENIED\n },\n relations: ['decidedBy'],\n order: { decidedAt: 'DESC' }\n })\n ])\n\n const role = chosen[0]\n ? await getRepository(Role, tx).findOne({ where: { id: chosen[0].id }, relations: ['privileges'] })\n : null\n const roleIsParents = !!role && !!domain.parentId && role.domainId === domain.parentId\n const deciderMayChangeParentRoles = roleIsParents ? await mayChangeParentRoles(decider, domain, tx) : false\n\n return {\n ...base,\n request: {\n state: request.state as any,\n requesterId: requester?.id,\n category: request.category,\n privilege: request.privilege,\n expiresAt: request.expiresAt\n },\n role: role\n ? { id: role.id, name: role.name, privileges: (role.privileges || []).map(p => ({ category: p.category, privilege: p.name })) }\n : null,\n roleNamedButNotAssignable: !!input.roleId && !role,\n roleIsParents,\n deciderMayChangeParentRoles,\n requester: requester\n ? {\n active: requester.status === UserStatus.ACTIVATED,\n inDomain: (requester.domains || []).some(d => d.id === domain.id)\n }\n : null,\n requesterHoldsIt,\n candidates: candidates.map(({ id, name, privilegeCount, holdsTask }) => ({ id, name, privilegeCount, holdsTask })),\n task: taskFacts(request.taskId),\n deciderIsOwner,\n deciderPrivileges: (deciderPrivileges || []).map((p: any) => ({ category: p.category, privilege: p.privilege })),\n deniedBefore:\n deniedBefore && deniedBefore.id !== request.id\n ? {\n by: deniedBefore.decidedBy?.name || deniedBefore.decidedBy?.email || '',\n at: deniedBefore.decidedAt,\n reason: deniedBefore.decisionReason\n }\n : null\n }\n}\n\nexport async function reviewDecision(input: AccessDecisionInput, context: ResolverContext): Promise<DecisionIssue[]> {\n return reviewAccessDecision(await collectDecisionFacts(input, context))\n}\n\n/* ── deciding ────────────────────────────────────────────────────────────── */\n\n/**\n * Grants or denies. The review runs again here - the card and the screen were shown a moment ago, and another\n * person may have decided since. The state moves PENDING → decided in one conditional update, so two people\n * deciding at once cannot both win.\n */\nexport async function decideAccessRequest(input: AccessDecisionInput, context: ResolverContext): Promise<AccessRequest> {\n const { domain, user: decider, tx } = context.state\n const now = new Date()\n\n const issues = await reviewDecision(input, context)\n if (isBlocked(issues)) {\n throw new AccessDecisionRefused(issues)\n }\n\n const repository = getRepository(AccessRequest, tx)\n const granting = input.decision === 'GRANT'\n\n const moved = await repository\n .createQueryBuilder()\n .update(AccessRequest)\n .set({\n state: granting ? AccessRequestStatus.GRANTED : AccessRequestStatus.DENIED,\n decidedBy: { id: decider.id } as User,\n decidedAt: now,\n decisionReason: input.reason?.trim() || null,\n grantedRole: granting ? ({ id: input.roleId } as Role) : null\n })\n .where('id = :id AND domain_id = :domainId AND state = :pending', {\n id: input.requestId,\n domainId: domain.id,\n pending: AccessRequestStatus.PENDING\n })\n .execute()\n\n if (!moved.affected) {\n throw new AccessDecisionRefused([{ code: 'request-not-pending', blocking: true, field: 'request' }])\n }\n\n const request = await repository.findOne({ where: { id: input.requestId }, relations: ['domain', 'requester', 'requester.roles', 'grantedRole'] })\n const requester = request.requester\n\n if (granting) {\n await assignRoles(requester, { add: [input.roleId] }, domain, tx, decider)\n await closeWhatTheGrantOpened(request, context)\n }\n\n tellLater(context, 'notify the requester of the decision', async () =>\n notifyEach(\n context,\n [requester.id],\n granting ? 'access-request.notify.granted' : 'access-request.notify.denied',\n { privilege: `${request.category}:${request.privilege}`, role: request.grantedRole?.name ?? '', reason: request.decisionReason ?? '' },\n /* The requester cannot read the list of everyone's requests - their own is where the answer is. */\n 'my-access-requests'\n )\n )\n\n return request\n}\n\n/**\n * Other pending requests of the same person that the role just given now answers - closed as granted with it.\n *\n * Judged by the granted role's own privileges, read in this transaction. `User.hasPrivilege` reads through the default\n * connection, which on a pooled database does not see the role added a moment ago in this uncommitted transaction.\n */\nasync function closeWhatTheGrantOpened(granted: AccessRequest, context: ResolverContext) {\n const { domain, user: decider, tx } = context.state\n const repository = getRepository(AccessRequest, tx)\n const role = await getRepository(Role, tx).findOne({ where: { id: granted.grantedRole?.id }, relations: ['privileges'] })\n const gives = new Set((role?.privileges || []).map(p => `${p.category}:${p.name}`))\n if (gives.size === 0) return\n\n const others = await repository.find({\n where: { domain: { id: domain.id }, requester: { id: granted.requester.id }, state: AccessRequestStatus.PENDING }\n })\n\n for (const other of others) {\n if (other.id === granted.id || !gives.has(`${other.category}:${other.privilege}`)) continue\n await repository.update(\n { id: other.id, state: AccessRequestStatus.PENDING },\n { state: AccessRequestStatus.GRANTED, decidedBy: { id: decider.id } as User, decidedAt: new Date(), grantedRole: { id: role.id } as Role }\n )\n }\n}\n\n/* ── telling ─────────────────────────────────────────────────────────────── */\n\n/** After the commit when there is a transaction, at once otherwise - a failed notification never fails the decision. */\nfunction tellLater(context: ResolverContext, name: string, run: () => Promise<void>) {\n const deferred = whenCommitted(context.state.afterCommit, name, run)\n if (!deferred) void run().catch(() => {})\n}\n\n/**\n * One notification per person, in that person's language (`User.locale`), linking to where they can act on it. `notify` is optional: it is there when\n * the notification module is loaded, and nothing happens otherwise - the request list is the record.\n */\nasync function notifyEach(\n context: ResolverContext,\n userIds: string[],\n key: string,\n params: Record<string, string>,\n url: string\n) {\n const notify = (context.state as any).notify\n if (typeof notify !== 'function' || userIds.length === 0) return\n\n const people = await getRepository(User).find({ where: { id: In(userIds) } })\n const byLocale = new Map<string, string[]>()\n for (const person of people) {\n const locale = person.locale || ''\n byLocale.set(locale, [...(byLocale.get(locale) || []), person.id])\n }\n\n for (const [locale, receivers] of byLocale) {\n const say = (suffix: string) => translate(context, `${key}.${suffix}`, params, locale)\n await notify({\n receivers,\n type: 'info',\n subject: 'access-request',\n title: say('title'),\n body: say('body'),\n url,\n mode: 'background'\n })\n }\n}\n\nfunction translate(context: any, key: string, params: Record<string, string>, locale: string): string {\n const i18next = context?.i18next\n if (locale && i18next?.t) return i18next.t(key, { ...params, lng: locale })\n return typeof context?.t === 'function' ? context.t(key, { ...params }) : key\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { ListParam } from '@things-factory/shell';
2
2
  import { AccessRequest } from './access-request.js';
3
- import { AccessDecisionInput, AccessDecisionIssue, AccessRequestList, AccessRoleCandidate } from './access-request-types.js';
3
+ import { AccessDecisionInput, AccessDecisionIssue, AccessRequestList, AccessRoleCandidate, AccessTaskChoice } from './access-request-types.js';
4
4
  export declare class AccessRequestQuery {
5
5
  accessRequests(params: ListParam, context: ResolverContext): Promise<AccessRequestList>;
6
6
  /** Open by design: the caller's own requests - where they see what became of them. */
@@ -10,7 +10,12 @@ export declare class AccessRequestQuery {
10
10
  * as what it is. A number, never who.
11
11
  */
12
12
  accessRequestDeciderCount(context: ResolverContext): Promise<number>;
13
- rolesGranting(category: string, privilege: string, context: ResolverContext): Promise<AccessRoleCandidate[]>;
13
+ rolesGranting(category: string, privilege: string, context: ResolverContext, taskId?: string): Promise<AccessRoleCandidate[]>;
14
+ /**
15
+ * Open by design: the tasks a person refused may ask for - the code catalog of work, nothing of this domain's data.
16
+ * The person asking is refused by definition, so no privilege can close it (ADR-0103 decision 9).
17
+ */
18
+ tasksGranting(category: string, privilege: string): Promise<AccessTaskChoice[]>;
14
19
  /** The roles of this domain's assignable scope (own, parent, granted) with exactly this name - what a decision may name. */
15
20
  assignableRolesNamed(name: string, context: ResolverContext): Promise<AccessRoleCandidate[]>;
16
21
  /** The same review the decision runs - the list screen and the AI draft card show this (ADR-0103 decision 4). */
@@ -7,6 +7,7 @@ const shell_1 = require("@things-factory/shell");
7
7
  const access_request_js_1 = require("../../controllers/access-request.js");
8
8
  const assignable_roles_js_1 = require("../../utils/assignable-roles.js");
9
9
  const access_request_js_2 = require("./access-request.js");
10
+ const role_task_js_1 = require("../role-template/role-task.js");
10
11
  const access_request_types_js_1 = require("./access-request-types.js");
11
12
  /* The review and the candidates serve a decision - the deciding declaration. */
12
13
  const DECIDE = '@privilege(category: "user", privilege: "mutation", domainOwnerGranted: true)';
@@ -46,13 +47,24 @@ let AccessRequestQuery = class AccessRequestQuery {
46
47
  const { domain, user } = context.state;
47
48
  return (await (0, access_request_js_1.findDeciderIds)(domain, user.id)).length;
48
49
  }
49
- async rolesGranting(category, privilege, context) {
50
- return (await (0, access_request_js_1.rolesGranting)(category, privilege, context.state.domain)).map(({ id, name, privilegeCount }) => ({ id, name, privilegeCount }));
50
+ async rolesGranting(category, privilege, context, taskId) {
51
+ return (await (0, access_request_js_1.rolesGranting)(category, privilege, context.state.domain, undefined, taskId)).map(({ id, name, privilegeCount, holdsTask }) => ({ id, name, privilegeCount, holdsTask }));
52
+ }
53
+ /**
54
+ * Open by design: the tasks a person refused may ask for - the code catalog of work, nothing of this domain's data.
55
+ * The person asking is refused by definition, so no privilege can close it (ADR-0103 decision 9).
56
+ */
57
+ async tasksGranting(category, privilege) {
58
+ return (0, role_task_js_1.tasksHolding)(category, privilege).map(task => ({
59
+ taskId: task.id,
60
+ module: task.id.split('.')[0],
61
+ privilegeCount: task.grants.reduce((count, grant) => count + grant.axes.length, 0)
62
+ }));
51
63
  }
52
64
  /** The roles of this domain's assignable scope (own, parent, granted) with exactly this name - what a decision may name. */
53
65
  async assignableRolesNamed(name, context) {
54
66
  const roles = await (0, assignable_roles_js_1.findAssignableRolesNamed)(name, context.state.domain);
55
- return roles.map(role => ({ id: role.id, name: role.name, privilegeCount: role.privileges?.length ?? 0 }));
67
+ return roles.map(role => ({ id: role.id, name: role.name, privilegeCount: role.privileges?.length ?? 0, holdsTask: false }));
56
68
  }
57
69
  /** The same review the decision runs - the list screen and the AI draft card show this (ADR-0103 decision 4). */
58
70
  async reviewAccessDecision(decision, context) {
@@ -89,10 +101,19 @@ tslib_1.__decorate([
89
101
  tslib_1.__param(0, (0, type_graphql_1.Arg)('category')),
90
102
  tslib_1.__param(1, (0, type_graphql_1.Arg)('privilege')),
91
103
  tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
104
+ tslib_1.__param(3, (0, type_graphql_1.Arg)('taskId', { nullable: true, description: 'The task asked for - roles holding all of it come first.' })),
92
105
  tslib_1.__metadata("design:type", Function),
93
- tslib_1.__metadata("design:paramtypes", [String, String, Object]),
106
+ tslib_1.__metadata("design:paramtypes", [String, String, Object, String]),
94
107
  tslib_1.__metadata("design:returntype", Promise)
95
108
  ], AccessRequestQuery.prototype, "rolesGranting", null);
109
+ tslib_1.__decorate([
110
+ (0, type_graphql_1.Query)(returns => [access_request_types_js_1.AccessTaskChoice], { description: 'To list the tasks that hold a privilege, fewest privileges first - what a person refused may ask for.' }),
111
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('category')),
112
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('privilege')),
113
+ tslib_1.__metadata("design:type", Function),
114
+ tslib_1.__metadata("design:paramtypes", [String, String]),
115
+ tslib_1.__metadata("design:returntype", Promise)
116
+ ], AccessRequestQuery.prototype, "tasksGranting", null);
96
117
  tslib_1.__decorate([
97
118
  (0, type_graphql_1.Directive)(DECIDE),
98
119
  (0, type_graphql_1.Query)(returns => [access_request_types_js_1.AccessRoleCandidate], { description: 'To find, by exact name, the roles this domain can assign.' }),
@@ -1 +1 @@
1
- {"version":3,"file":"access-request-query.js","sourceRoot":"","sources":["../../../server/service/access-request/access-request-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Args, Ctx, Directive, Int, Query, Resolver } from 'type-graphql'\n\nimport { getQueryBuilderFromListParams, getRepository, InheritedValueType, ListParam } from '@things-factory/shell'\n\nimport { findDeciderIds, reviewDecision, rolesGranting } from '../../controllers/access-request.js'\nimport { findAssignableRolesNamed } from '../../utils/assignable-roles.js'\nimport { AccessRequest } from './access-request.js'\nimport { AccessDecisionInput, AccessDecisionIssue, AccessRequestList, AccessRoleCandidate } from './access-request-types.js'\n\n/* The review and the candidates serve a decision - the deciding declaration. */\nconst DECIDE = '@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)'\n\n@Resolver(AccessRequest)\nexport class AccessRequestQuery {\n /*\n * A decider's list - the same declaration as the decision, so the people told of a request can open it (ADR-0103).\n * This domain's rows only: `inherited` is pinned, a parent domain's requests are not this domain's to read.\n */\n @Directive(DECIDE)\n @Query(returns => AccessRequestList, { description: \"To fetch this domain's access requests.\" })\n async accessRequests(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<AccessRequestList> {\n const { domain } = context.state\n const queryBuilder = getQueryBuilderFromListParams({\n domain,\n params: { ...params, inherited: InheritedValueType.None },\n repository: getRepository(AccessRequest),\n alias: 'accessRequest',\n searchables: ['category', 'privilege', 'reason']\n })\n queryBuilder.leftJoinAndSelect('accessRequest.requester', 'requester').leftJoinAndSelect('accessRequest.decidedBy', 'decidedBy').leftJoinAndSelect('accessRequest.grantedRole', 'grantedRole')\n const [items, total] = await queryBuilder.getManyAndCount()\n return { items, total }\n }\n\n /** Open by design: the caller's own requests - where they see what became of them. */\n @Query(returns => [AccessRequest], { description: \"To fetch the caller's own access requests in this domain, newest first.\" })\n async myAccessRequests(@Ctx() context: ResolverContext): Promise<AccessRequest[]> {\n const { domain, user } = context.state\n return await getRepository(AccessRequest).find({\n where: { domain: { id: domain.id }, requester: { id: user.id } },\n relations: ['domain', 'requester', 'grantedRole'],\n order: { createdAt: 'DESC' },\n take: 100\n })\n }\n\n /**\n * Open by design: how many people a request would reach - the popup says it before the person sends, so asking reads\n * as what it is. A number, never who.\n */\n @Query(returns => Int, { description: 'To count the people who would be told of an access request from the caller in this domain.' })\n async accessRequestDeciderCount(@Ctx() context: ResolverContext): Promise<number> {\n const { domain, user } = context.state\n return (await findDeciderIds(domain, user.id)).length\n }\n\n @Directive(DECIDE)\n @Query(returns => [AccessRoleCandidate], { description: 'To list the roles this domain can assign that grant a privilege, fewest privileges first.' })\n async rolesGranting(@Arg('category') category: string, @Arg('privilege') privilege: string, @Ctx() context: ResolverContext): Promise<AccessRoleCandidate[]> {\n return (await rolesGranting(category, privilege, context.state.domain)).map(({ id, name, privilegeCount }) => ({ id, name, privilegeCount }))\n }\n\n /** The roles of this domain's assignable scope (own, parent, granted) with exactly this name - what a decision may name. */\n @Directive(DECIDE)\n @Query(returns => [AccessRoleCandidate], { description: 'To find, by exact name, the roles this domain can assign.' })\n async assignableRolesNamed(@Arg('name') name: string, @Ctx() context: ResolverContext): Promise<AccessRoleCandidate[]> {\n const roles = await findAssignableRolesNamed(name, context.state.domain)\n return roles.map(role => ({ id: role.id, name: role.name, privilegeCount: role.privileges?.length ?? 0 }))\n }\n\n /** The same review the decision runs - the list screen and the AI draft card show this (ADR-0103 decision 4). */\n @Directive(DECIDE)\n @Query(returns => [AccessDecisionIssue], { description: 'To review a decision before it is made: what blocks it and what the person deciding should know.' })\n async reviewAccessDecision(@Arg('decision', type => AccessDecisionInput) decision: AccessDecisionInput, @Ctx() context: ResolverContext): Promise<AccessDecisionIssue[]> {\n return (await reviewDecision(decision as any, context)) as AccessDecisionIssue[]\n }\n}\n"]}
1
+ {"version":3,"file":"access-request-query.js","sourceRoot":"","sources":["../../../server/service/access-request/access-request-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Args, Ctx, Directive, Int, Query, Resolver } from 'type-graphql'\n\nimport { getQueryBuilderFromListParams, getRepository, InheritedValueType, ListParam } from '@things-factory/shell'\n\nimport { findDeciderIds, reviewDecision, rolesGranting } from '../../controllers/access-request.js'\nimport { findAssignableRolesNamed } from '../../utils/assignable-roles.js'\nimport { AccessRequest } from './access-request.js'\nimport { tasksHolding } from '../role-template/role-task.js'\nimport { AccessDecisionInput, AccessDecisionIssue, AccessRequestList, AccessRoleCandidate, AccessTaskChoice } from './access-request-types.js'\n\n/* The review and the candidates serve a decision - the deciding declaration. */\nconst DECIDE = '@privilege(category: \"user\", privilege: \"mutation\", domainOwnerGranted: true)'\n\n@Resolver(AccessRequest)\nexport class AccessRequestQuery {\n /*\n * A decider's list - the same declaration as the decision, so the people told of a request can open it (ADR-0103).\n * This domain's rows only: `inherited` is pinned, a parent domain's requests are not this domain's to read.\n */\n @Directive(DECIDE)\n @Query(returns => AccessRequestList, { description: \"To fetch this domain's access requests.\" })\n async accessRequests(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<AccessRequestList> {\n const { domain } = context.state\n const queryBuilder = getQueryBuilderFromListParams({\n domain,\n params: { ...params, inherited: InheritedValueType.None },\n repository: getRepository(AccessRequest),\n alias: 'accessRequest',\n searchables: ['category', 'privilege', 'reason']\n })\n queryBuilder.leftJoinAndSelect('accessRequest.requester', 'requester').leftJoinAndSelect('accessRequest.decidedBy', 'decidedBy').leftJoinAndSelect('accessRequest.grantedRole', 'grantedRole')\n const [items, total] = await queryBuilder.getManyAndCount()\n return { items, total }\n }\n\n /** Open by design: the caller's own requests - where they see what became of them. */\n @Query(returns => [AccessRequest], { description: \"To fetch the caller's own access requests in this domain, newest first.\" })\n async myAccessRequests(@Ctx() context: ResolverContext): Promise<AccessRequest[]> {\n const { domain, user } = context.state\n return await getRepository(AccessRequest).find({\n where: { domain: { id: domain.id }, requester: { id: user.id } },\n relations: ['domain', 'requester', 'grantedRole'],\n order: { createdAt: 'DESC' },\n take: 100\n })\n }\n\n /**\n * Open by design: how many people a request would reach - the popup says it before the person sends, so asking reads\n * as what it is. A number, never who.\n */\n @Query(returns => Int, { description: 'To count the people who would be told of an access request from the caller in this domain.' })\n async accessRequestDeciderCount(@Ctx() context: ResolverContext): Promise<number> {\n const { domain, user } = context.state\n return (await findDeciderIds(domain, user.id)).length\n }\n\n @Directive(DECIDE)\n @Query(returns => [AccessRoleCandidate], { description: 'To list the roles this domain can assign that grant a privilege, fewest privileges first.' })\n async rolesGranting(\n @Arg('category') category: string,\n @Arg('privilege') privilege: string,\n @Ctx() context: ResolverContext,\n @Arg('taskId', { nullable: true, description: 'The task asked for - roles holding all of it come first.' }) taskId?: string\n ): Promise<AccessRoleCandidate[]> {\n return (await rolesGranting(category, privilege, context.state.domain, undefined, taskId)).map(({ id, name, privilegeCount, holdsTask }) => ({ id, name, privilegeCount, holdsTask }))\n }\n\n /**\n * Open by design: the tasks a person refused may ask for - the code catalog of work, nothing of this domain's data.\n * The person asking is refused by definition, so no privilege can close it (ADR-0103 decision 9).\n */\n @Query(returns => [AccessTaskChoice], { description: 'To list the tasks that hold a privilege, fewest privileges first - what a person refused may ask for.' })\n async tasksGranting(@Arg('category') category: string, @Arg('privilege') privilege: string): Promise<AccessTaskChoice[]> {\n return tasksHolding(category, privilege).map(task => ({\n taskId: task.id,\n module: task.id.split('.')[0],\n privilegeCount: task.grants.reduce((count, grant) => count + grant.axes.length, 0)\n }))\n }\n\n /** The roles of this domain's assignable scope (own, parent, granted) with exactly this name - what a decision may name. */\n @Directive(DECIDE)\n @Query(returns => [AccessRoleCandidate], { description: 'To find, by exact name, the roles this domain can assign.' })\n async assignableRolesNamed(@Arg('name') name: string, @Ctx() context: ResolverContext): Promise<AccessRoleCandidate[]> {\n const roles = await findAssignableRolesNamed(name, context.state.domain)\n return roles.map(role => ({ id: role.id, name: role.name, privilegeCount: role.privileges?.length ?? 0, holdsTask: false }))\n }\n\n /** The same review the decision runs - the list screen and the AI draft card show this (ADR-0103 decision 4). */\n @Directive(DECIDE)\n @Query(returns => [AccessDecisionIssue], { description: 'To review a decision before it is made: what blocks it and what the person deciding should know.' })\n async reviewAccessDecision(@Arg('decision', type => AccessDecisionInput) decision: AccessDecisionInput, @Ctx() context: ResolverContext): Promise<AccessDecisionIssue[]> {\n return (await reviewDecision(decision as any, context)) as AccessDecisionIssue[]\n }\n}\n"]}
@@ -7,6 +7,7 @@ export declare class NewAccessRequest {
7
7
  category: string;
8
8
  privilege: string;
9
9
  operationName?: string;
10
+ taskId?: string;
10
11
  reason?: string;
11
12
  }
12
13
  export declare class AccessDecisionInput {
@@ -25,6 +26,12 @@ export declare class AccessRoleCandidate {
25
26
  id: string;
26
27
  name: string;
27
28
  privilegeCount: number;
29
+ holdsTask: boolean;
30
+ }
31
+ export declare class AccessTaskChoice {
32
+ taskId: string;
33
+ module: string;
34
+ privilegeCount: number;
28
35
  }
29
36
  export declare class AccessRequestList {
30
37
  items: AccessRequest[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AccessRequestList = exports.AccessRoleCandidate = exports.AccessDecisionIssue = exports.AccessDecisionInput = exports.NewAccessRequest = exports.AccessDecision = void 0;
3
+ exports.AccessRequestList = exports.AccessTaskChoice = exports.AccessRoleCandidate = exports.AccessDecisionIssue = exports.AccessDecisionInput = exports.NewAccessRequest = exports.AccessDecision = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
6
  const shell_1 = require("@things-factory/shell");
@@ -29,6 +29,10 @@ tslib_1.__decorate([
29
29
  (0, type_graphql_1.Field)({ nullable: true, description: 'The operation that was refused, shown to the person deciding.' }),
30
30
  tslib_1.__metadata("design:type", String)
31
31
  ], NewAccessRequest.prototype, "operationName", void 0);
32
+ tslib_1.__decorate([
33
+ (0, type_graphql_1.Field)({ nullable: true, description: 'The task asked for - one of the tasks that hold the refused privilege (tasksGranting).' }),
34
+ tslib_1.__metadata("design:type", String)
35
+ ], NewAccessRequest.prototype, "taskId", void 0);
32
36
  tslib_1.__decorate([
33
37
  (0, type_graphql_1.Field)({ nullable: true, description: "The requester's reason." }),
34
38
  tslib_1.__metadata("design:type", String)
@@ -95,9 +99,31 @@ tslib_1.__decorate([
95
99
  (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'How many privileges the role carries - fewer is closer to what was asked.' }),
96
100
  tslib_1.__metadata("design:type", Number)
97
101
  ], AccessRoleCandidate.prototype, "privilegeCount", void 0);
102
+ tslib_1.__decorate([
103
+ (0, type_graphql_1.Field)({ description: 'Whether the role holds every privilege of the task asked for. False when no task was asked.' }),
104
+ tslib_1.__metadata("design:type", Boolean)
105
+ ], AccessRoleCandidate.prototype, "holdsTask", void 0);
98
106
  exports.AccessRoleCandidate = AccessRoleCandidate = tslib_1.__decorate([
99
107
  (0, type_graphql_1.ObjectType)({ description: 'A role this domain can assign that grants the asked privilege.' })
100
108
  ], AccessRoleCandidate);
109
+ let AccessTaskChoice = class AccessTaskChoice {
110
+ };
111
+ exports.AccessTaskChoice = AccessTaskChoice;
112
+ tslib_1.__decorate([
113
+ (0, type_graphql_1.Field)({ description: 'The task id, `<module>.<work>`.' }),
114
+ tslib_1.__metadata("design:type", String)
115
+ ], AccessTaskChoice.prototype, "taskId", void 0);
116
+ tslib_1.__decorate([
117
+ (0, type_graphql_1.Field)({ description: 'The module or application that declares the task.' }),
118
+ tslib_1.__metadata("design:type", String)
119
+ ], AccessTaskChoice.prototype, "module", void 0);
120
+ tslib_1.__decorate([
121
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'How many privileges the task holds - fewer is closer to what was refused.' }),
122
+ tslib_1.__metadata("design:type", Number)
123
+ ], AccessTaskChoice.prototype, "privilegeCount", void 0);
124
+ exports.AccessTaskChoice = AccessTaskChoice = tslib_1.__decorate([
125
+ (0, type_graphql_1.ObjectType)({ description: 'A task that holds a refused privilege - what a person refused may ask for. Words are task.<id>.name and .description.' })
126
+ ], AccessTaskChoice);
101
127
  let AccessRequestList = class AccessRequestList {
102
128
  };
103
129
  exports.AccessRequestList = AccessRequestList;
@@ -1 +1 @@
1
- {"version":3,"file":"access-request-types.js","sourceRoot":"","sources":["../../../server/service/access-request/access-request-types.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, InputType, Int, ObjectType, registerEnumType } from 'type-graphql'\nimport { ScalarObject } from '@things-factory/shell'\n\nimport { AccessRequest } from './access-request.js'\n\nexport enum AccessDecision {\n GRANT = 'GRANT',\n DENY = 'DENY'\n}\n\nregisterEnumType(AccessDecision, {\n name: 'AccessDecision',\n description: 'What the person deciding an access request does: put the requester into a role, or deny with a reason.'\n})\n\n@InputType({ description: 'An access request for one privilege in this domain - what a refusal carried, and why it is needed.' })\nexport class NewAccessRequest {\n @Field({ description: \"The category of the refused privilege, from the refusal's extensions.requires.\" })\n category: string\n\n @Field({ description: \"The refused privilege, from the refusal's extensions.requires.\" })\n privilege: string\n\n @Field({ nullable: true, description: 'The operation that was refused, shown to the person deciding.' })\n operationName?: string\n\n @Field({ nullable: true, description: \"The requester's reason.\" })\n reason?: string\n}\n\n@InputType({ description: 'A decision on one access request.' })\nexport class AccessDecisionInput {\n @Field({ description: 'The access request decided.' })\n requestId: string\n\n @Field(type => AccessDecision, { description: 'Grant or deny.' })\n decision: AccessDecision\n\n @Field({ nullable: true, description: 'The role to put the requester into - required to grant, one this domain can assign that holds the privilege.' })\n roleId?: string\n\n @Field({ nullable: true, description: 'Why - required to deny.' })\n reason?: string\n}\n\n@ObjectType({ description: 'One finding of the access decision review - blocking ones keep the decision from being made.' })\nexport class AccessDecisionIssue {\n @Field({ description: 'What was found, as a code the screen words.' })\n code: string\n\n @Field({ description: 'Whether it keeps the decision from being made.' })\n blocking: boolean\n\n @Field({ nullable: true, description: 'The part of the decision it is about: request, decision, role or reason.' })\n field?: string\n\n @Field(type => ScalarObject, { nullable: true, description: 'The values the sentence is built from.' })\n params?: Record<string, unknown>\n}\n\n@ObjectType({ description: 'A role this domain can assign that grants the asked privilege.' })\nexport class AccessRoleCandidate {\n @Field({ description: 'The role id.' })\n id: string\n\n @Field({ description: 'The role name.' })\n name: string\n\n @Field(type => Int, { description: 'How many privileges the role carries - fewer is closer to what was asked.' })\n privilegeCount: number\n}\n\n@ObjectType({ description: 'A paginated list of access requests.' })\nexport class AccessRequestList {\n @Field(type => [AccessRequest], { nullable: true, description: 'The access requests.' })\n items: AccessRequest[]\n\n @Field(type => Int, { nullable: true, description: 'The total number of access requests.' })\n total: number\n}\n"]}
1
+ {"version":3,"file":"access-request-types.js","sourceRoot":"","sources":["../../../server/service/access-request/access-request-types.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, InputType, Int, ObjectType, registerEnumType } from 'type-graphql'\nimport { ScalarObject } from '@things-factory/shell'\n\nimport { AccessRequest } from './access-request.js'\n\nexport enum AccessDecision {\n GRANT = 'GRANT',\n DENY = 'DENY'\n}\n\nregisterEnumType(AccessDecision, {\n name: 'AccessDecision',\n description: 'What the person deciding an access request does: put the requester into a role, or deny with a reason.'\n})\n\n@InputType({ description: 'An access request for one privilege in this domain - what a refusal carried, and why it is needed.' })\nexport class NewAccessRequest {\n @Field({ description: \"The category of the refused privilege, from the refusal's extensions.requires.\" })\n category: string\n\n @Field({ description: \"The refused privilege, from the refusal's extensions.requires.\" })\n privilege: string\n\n @Field({ nullable: true, description: 'The operation that was refused, shown to the person deciding.' })\n operationName?: string\n\n @Field({ nullable: true, description: 'The task asked for - one of the tasks that hold the refused privilege (tasksGranting).' })\n taskId?: string\n\n @Field({ nullable: true, description: \"The requester's reason.\" })\n reason?: string\n}\n\n@InputType({ description: 'A decision on one access request.' })\nexport class AccessDecisionInput {\n @Field({ description: 'The access request decided.' })\n requestId: string\n\n @Field(type => AccessDecision, { description: 'Grant or deny.' })\n decision: AccessDecision\n\n @Field({ nullable: true, description: 'The role to put the requester into - required to grant, one this domain can assign that holds the privilege.' })\n roleId?: string\n\n @Field({ nullable: true, description: 'Why - required to deny.' })\n reason?: string\n}\n\n@ObjectType({ description: 'One finding of the access decision review - blocking ones keep the decision from being made.' })\nexport class AccessDecisionIssue {\n @Field({ description: 'What was found, as a code the screen words.' })\n code: string\n\n @Field({ description: 'Whether it keeps the decision from being made.' })\n blocking: boolean\n\n @Field({ nullable: true, description: 'The part of the decision it is about: request, decision, role or reason.' })\n field?: string\n\n @Field(type => ScalarObject, { nullable: true, description: 'The values the sentence is built from.' })\n params?: Record<string, unknown>\n}\n\n@ObjectType({ description: 'A role this domain can assign that grants the asked privilege.' })\nexport class AccessRoleCandidate {\n @Field({ description: 'The role id.' })\n id: string\n\n @Field({ description: 'The role name.' })\n name: string\n\n @Field(type => Int, { description: 'How many privileges the role carries - fewer is closer to what was asked.' })\n privilegeCount: number\n\n @Field({ description: 'Whether the role holds every privilege of the task asked for. False when no task was asked.' })\n holdsTask: boolean\n}\n\n@ObjectType({ description: 'A task that holds a refused privilege - what a person refused may ask for. Words are task.<id>.name and .description.' })\nexport class AccessTaskChoice {\n @Field({ description: 'The task id, `<module>.<work>`.' })\n taskId: string\n\n @Field({ description: 'The module or application that declares the task.' })\n module: string\n\n @Field(type => Int, { description: 'How many privileges the task holds - fewer is closer to what was refused.' })\n privilegeCount: number\n}\n\n@ObjectType({ description: 'A paginated list of access requests.' })\nexport class AccessRequestList {\n @Field(type => [AccessRequest], { nullable: true, description: 'The access requests.' })\n items: AccessRequest[]\n\n @Field(type => Int, { nullable: true, description: 'The total number of access requests.' })\n total: number\n}\n"]}
@@ -17,6 +17,7 @@ export declare class AccessRequest {
17
17
  category: string;
18
18
  privilege: string;
19
19
  operationName?: string;
20
+ taskId?: string;
20
21
  reason?: string;
21
22
  count: number;
22
23
  lastRequestedAt?: Date;
@@ -75,6 +75,11 @@ tslib_1.__decorate([
75
75
  (0, type_graphql_1.Field)({ nullable: true, description: 'The operation that was refused - shown to the person deciding.' }),
76
76
  tslib_1.__metadata("design:type", String)
77
77
  ], AccessRequest.prototype, "operationName", void 0);
78
+ tslib_1.__decorate([
79
+ (0, typeorm_1.Column)({ nullable: true }),
80
+ (0, type_graphql_1.Field)({ nullable: true, description: 'The task the requester asked for - one that holds the refused privilege (`<module>.<work>`).' }),
81
+ tslib_1.__metadata("design:type", String)
82
+ ], AccessRequest.prototype, "taskId", void 0);
78
83
  tslib_1.__decorate([
79
84
  (0, typeorm_1.Column)({ nullable: true }),
80
85
  (0, type_graphql_1.Field)({ nullable: true, description: "The requester's reason." }),
@@ -1 +1 @@
1
- {"version":3,"file":"access-request.js","sourceRoot":"","sources":["../../../server/service/access-request/access-request.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Field, ID, Int, ObjectType, registerEnumType } from 'type-graphql'\n\nimport { config } from '@things-factory/env'\nimport { Domain } from '@things-factory/shell'\n\nimport { Role } from '../role/role.js'\nimport { User } from '../user/user.js'\n\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\nexport enum AccessRequestStatus {\n PENDING = 'PENDING',\n GRANTED = 'GRANTED',\n DENIED = 'DENIED',\n WITHDRAWN = 'WITHDRAWN',\n EXPIRED = 'EXPIRED'\n}\n\nregisterEnumType(AccessRequestStatus, {\n name: 'AccessRequestStatus',\n description: 'Where an access request stands: waiting, granted, denied, withdrawn by the requester, or expired unanswered.'\n})\n\n/*\n * A person refused for want of a role asks for the privilege (ADR-0103, docs/design/access-request.md).\n *\n * ── The row is the record ────────────────────────────────────────────────\n * Who asked, for what, why, who decided, which role, why not - all here, and never deleted. A notification\n * is a courtesy on top: it is not stored per person and does nothing where FCM is not set up.\n *\n * ── At most one pending per person and privilege ─────────────────────────\n * Asking again bumps `count` and `lastRequestedAt` instead of adding a row. A partial unique index would say\n * it and is not portable across the databases this framework supports, so it is held where the request is\n * made (as `Invitation` does). The index below is for reading.\n */\n@Entity()\n@Index('ix_access_request_0', (request: AccessRequest) => [request.domain, request.state])\n@Index('ix_access_request_1', (request: AccessRequest) => [request.domain, request.requester, request.category, request.privilege])\n@ObjectType({ description: 'A request by a person refused for want of a role, for one privilege in this domain.' })\nexport class AccessRequest {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the access request.' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { description: 'The domain the privilege is asked in.' })\n domain: Domain\n\n @RelationId((request: AccessRequest) => request.domain)\n domainId: string\n\n @ManyToOne(type => User)\n @Field(type => User, { description: 'The person who asked.' })\n requester: User\n\n @RelationId((request: AccessRequest) => request.requester)\n requesterId: string\n\n @Column()\n @Field({ description: 'The category of the privilege asked for, as the refusal carried it.' })\n category: string\n\n @Column()\n @Field({ description: 'The privilege asked for, as the refusal carried it.' })\n privilege: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The operation that was refused - shown to the person deciding.' })\n operationName?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: \"The requester's reason.\" })\n reason?: string\n\n @Column({ type: 'int', default: 1 })\n @Field(type => Int, { description: 'How many times it was asked while pending.' })\n count: number\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When it was last asked.' })\n lastRequestedAt?: Date\n\n /* The same column shape as Invitation.status - an enum where the database has one, a string elsewhere. */\n @Column({\n type:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'enum'\n : DATABASE_TYPE == 'oracle'\n ? 'varchar2'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n enum:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? AccessRequestStatus : undefined,\n length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined : 32,\n default: AccessRequestStatus.PENDING\n })\n @Field(type => AccessRequestStatus, { description: 'Where the request stands.' })\n state: AccessRequestStatus\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When a pending request expires unanswered.' })\n expiresAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who decided it.' })\n decidedBy?: User\n\n @RelationId((request: AccessRequest) => request.decidedBy)\n decidedById?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When it was decided.' })\n decidedAt?: Date\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: \"The decider's reason - required to deny.\" })\n decisionReason?: string\n\n @ManyToOne(type => Role, { nullable: true })\n @Field(type => Role, { nullable: true, description: 'The role the requester was put into.' })\n grantedRole?: Role\n\n @RelationId((request: AccessRequest) => request.grantedRole)\n grantedRoleId?: string\n\n @CreateDateColumn()\n @Field({ description: 'When it was first asked.' })\n createdAt: Date\n\n @UpdateDateColumn()\n @Field({ description: 'When it last changed.' })\n updatedAt: Date\n}\n"]}
1
+ {"version":3,"file":"access-request.js","sourceRoot":"","sources":["../../../server/service/access-request/access-request.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Field, ID, Int, ObjectType, registerEnumType } from 'type-graphql'\n\nimport { config } from '@things-factory/env'\nimport { Domain } from '@things-factory/shell'\n\nimport { Role } from '../role/role.js'\nimport { User } from '../user/user.js'\n\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\nexport enum AccessRequestStatus {\n PENDING = 'PENDING',\n GRANTED = 'GRANTED',\n DENIED = 'DENIED',\n WITHDRAWN = 'WITHDRAWN',\n EXPIRED = 'EXPIRED'\n}\n\nregisterEnumType(AccessRequestStatus, {\n name: 'AccessRequestStatus',\n description: 'Where an access request stands: waiting, granted, denied, withdrawn by the requester, or expired unanswered.'\n})\n\n/*\n * A person refused for want of a role asks for the privilege (ADR-0103, docs/design/access-request.md).\n *\n * ── The row is the record ────────────────────────────────────────────────\n * Who asked, for what, why, who decided, which role, why not - all here, and never deleted. A notification\n * is a courtesy on top: it is not stored per person and does nothing where FCM is not set up.\n *\n * ── At most one pending per person and privilege ─────────────────────────\n * Asking again bumps `count` and `lastRequestedAt` instead of adding a row. A partial unique index would say\n * it and is not portable across the databases this framework supports, so it is held where the request is\n * made (as `Invitation` does). The index below is for reading.\n */\n@Entity()\n@Index('ix_access_request_0', (request: AccessRequest) => [request.domain, request.state])\n@Index('ix_access_request_1', (request: AccessRequest) => [request.domain, request.requester, request.category, request.privilege])\n@ObjectType({ description: 'A request by a person refused for want of a role, for one privilege in this domain.' })\nexport class AccessRequest {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for the access request.' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { description: 'The domain the privilege is asked in.' })\n domain: Domain\n\n @RelationId((request: AccessRequest) => request.domain)\n domainId: string\n\n @ManyToOne(type => User)\n @Field(type => User, { description: 'The person who asked.' })\n requester: User\n\n @RelationId((request: AccessRequest) => request.requester)\n requesterId: string\n\n @Column()\n @Field({ description: 'The category of the privilege asked for, as the refusal carried it.' })\n category: string\n\n @Column()\n @Field({ description: 'The privilege asked for, as the refusal carried it.' })\n privilege: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The operation that was refused - shown to the person deciding.' })\n operationName?: string\n\n /*\n * The work the person asked for, by task id (ADR-0103 decision 9) - their own words for the refused privilege,\n * picked from the tasks that hold it. The request is still keyed by the refused privilege; this explains it. A task is\n * a code catalog and may be gone later: the screen then draws the id, and the review says nothing of it.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The task the requester asked for - one that holds the refused privilege (`<module>.<work>`).' })\n taskId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: \"The requester's reason.\" })\n reason?: string\n\n @Column({ type: 'int', default: 1 })\n @Field(type => Int, { description: 'How many times it was asked while pending.' })\n count: number\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When it was last asked.' })\n lastRequestedAt?: Date\n\n /* The same column shape as Invitation.status - an enum where the database has one, a string elsewhere. */\n @Column({\n type:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'enum'\n : DATABASE_TYPE == 'oracle'\n ? 'varchar2'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'varchar',\n enum:\n DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? AccessRequestStatus : undefined,\n length: DATABASE_TYPE == 'postgres' || DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb' ? undefined : 32,\n default: AccessRequestStatus.PENDING\n })\n @Field(type => AccessRequestStatus, { description: 'Where the request stands.' })\n state: AccessRequestStatus\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When a pending request expires unanswered.' })\n expiresAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who decided it.' })\n decidedBy?: User\n\n @RelationId((request: AccessRequest) => request.decidedBy)\n decidedById?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When it was decided.' })\n decidedAt?: Date\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: \"The decider's reason - required to deny.\" })\n decisionReason?: string\n\n @ManyToOne(type => Role, { nullable: true })\n @Field(type => Role, { nullable: true, description: 'The role the requester was put into.' })\n grantedRole?: Role\n\n @RelationId((request: AccessRequest) => request.grantedRole)\n grantedRoleId?: string\n\n @CreateDateColumn()\n @Field({ description: 'When it was first asked.' })\n createdAt: Date\n\n @UpdateDateColumn()\n @Field({ description: 'When it last changed.' })\n updatedAt: Date\n}\n"]}
@@ -2,4 +2,4 @@ import { AccessRequest } from './access-request.js';
2
2
  import { AccessRequestMutation } from './access-request-mutation.js';
3
3
  import { AccessRequestQuery } from './access-request-query.js';
4
4
  export declare const entities: (typeof AccessRequest)[];
5
- export declare const resolvers: (typeof AccessRequestQuery | typeof AccessRequestMutation)[];
5
+ export declare const resolvers: (typeof AccessRequestMutation | typeof AccessRequestQuery)[];
@@ -1,6 +1,6 @@
1
1
  import { AuditAction } from './audit-action.js';
2
2
  import { AuditQuery } from './audit-query.js';
3
3
  import { AuditRequest } from './audit-request.js';
4
- export declare const entities: (typeof AuditRequest | typeof AuditAction)[];
4
+ export declare const entities: (typeof AuditAction | typeof AuditRequest)[];
5
5
  export declare const resolvers: (typeof AuditQuery)[];
6
6
  export declare const subscribers: (typeof import("./audit-subscriber.js").GrantAuditSubscriber | typeof import("./audit-subscriber.js").UserAuditSubscriber | typeof import("./audit-subscriber.js").DomainOwnerAuditSubscriber | typeof import("./audit-subscriber.js").InvitationAuditSubscriber | typeof import("./audit-subscriber.js").WebAuthCredentialAuditSubscriber)[];
@@ -38,7 +38,7 @@ export * from './role/role-types.js';
38
38
  export * from './user/user-types.js';
39
39
  export * from './domain-link/domain-link-types.js';
40
40
  export * from './access-request/access-request-types.js';
41
- export declare const entities: (typeof import("./users-auth-providers/users-auth-providers.js").UsersAuthProviders | typeof import("./auth-provider/auth-provider.js").AuthProvider | typeof import("./application/application.js").Application | typeof import("./appliance/appliance.js").Appliance | typeof import("./privilege/privilege.js").Privilege | typeof import("./role/role.js").Role | typeof import("./user/user.js").User | typeof import("./partner/partner.js").Partner | typeof import("./granted-role/granted-role.js").GrantedRole | typeof import("./invitation/invitation.js").Invitation | typeof import("./password-history/password-history.js").PasswordHistory | typeof import("./verification-token/verification-token.js").VerificationToken | typeof import("./verification-token/verification-token.js").VerificationTokenType | typeof import("./audit/audit-request.js").AuditRequest | typeof import("./audit/audit-action.js").AuditAction | typeof import("./web-auth-credential/web-auth-credential.js").WebAuthCredential | typeof import("./domain-link/domain-link.js").DomainLink | typeof import("./domain-owner/domain-owner.js").DomainOwner | typeof import("./access-request/access-request.js").AccessRequest)[];
41
+ export declare const entities: (typeof import("./web-auth-credential/web-auth-credential.js").WebAuthCredential | typeof import("./user/user.js").User | typeof import("./auth-provider/auth-provider.js").AuthProvider | typeof import("./users-auth-providers/users-auth-providers.js").UsersAuthProviders | typeof import("./domain-owner/domain-owner.js").DomainOwner | typeof import("./granted-role/granted-role.js").GrantedRole | typeof import("./role/role.js").Role | typeof import("./privilege/privilege.js").Privilege | typeof import("./audit/audit-action.js").AuditAction | typeof import("./audit/audit-request.js").AuditRequest | typeof import("./password-history/password-history.js").PasswordHistory | typeof import("./verification-token/verification-token.js").VerificationToken | typeof import("./verification-token/verification-token.js").VerificationTokenType | typeof import("./invitation/invitation.js").Invitation | typeof import("./application/application.js").Application | typeof import("./access-request/access-request.js").AccessRequest | typeof import("./appliance/appliance.js").Appliance | typeof import("./partner/partner.js").Partner | typeof import("./domain-link/domain-link.js").DomainLink)[];
42
42
  export declare const subscribers: (typeof import("./audit/audit-subscriber.js").GrantAuditSubscriber | typeof import("./audit/audit-subscriber.js").UserAuditSubscriber | typeof import("./audit/audit-subscriber.js").DomainOwnerAuditSubscriber | typeof import("./audit/audit-subscriber.js").InvitationAuditSubscriber | typeof import("./audit/audit-subscriber.js").WebAuthCredentialAuditSubscriber)[];
43
43
  export declare const schema: {
44
44
  typeDefs: {
@@ -22,6 +22,7 @@ exports.openFields = {
22
22
  'Query.myAccessRequests': "the caller's own access requests",
23
23
  'Query.mayChangeParentRoles': "whether the caller may give the parent domain's roles here - about the caller only",
24
24
  'Query.accessRequestDeciderCount': 'how many people an access request from the caller would reach - a number, never who',
25
+ 'Query.tasksGranting': 'the tasks a refused person may ask for - the code catalog of work only, nothing of the domain (ADR-0103 decision 9)',
25
26
  'Mutation.requestAccess': 'asked by someone refused for want of a role - a gate here would need the privilege being asked for. The door asks only for the caller and judges again that a role would open it (ADR-0103)',
26
27
  'Mutation.withdrawAccessRequest': "withdraws the caller's own pending request and nobody else's"
27
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"open-fields.js","sourceRoot":"","sources":["../../server/service/open-fields.ts"],"names":[],"mappings":";;;AAEA;;;;;;GAMG;AACU,QAAA,UAAU,GAA0B;IAC/C,QAAQ,EAAE;QACR,sBAAsB,EAAE,4DAA4D;QACpF,eAAe,EAAE,wBAAwB;QACzC,oBAAoB,EAAE,0EAA0E;QAChG,4BAA4B,EAAE,sCAAsC;QACpE,8BAA8B,EAAE,2CAA2C;QAC3E,wCAAwC,EAAE,iCAAiC;QAC3E,4BAA4B,EAAE,qDAAqD;QACnF,oBAAoB,EAClB,2JAA2J;QAC7J,yBAAyB,EAAE,qFAAqF;QAChH,wBAAwB,EAAE,kCAAkC;QAC5D,4BAA4B,EAAE,oFAAoF;QAClH,iCAAiC,EAAE,qFAAqF;QACxH,wBAAwB,EACtB,6LAA6L;QAC/L,gCAAgC,EAAE,8DAA8D;KACjG;CACF,CAAA","sourcesContent":["import type { OpenFieldsDeclaration } from './privilege/open-field-rule.js'\n\n/**\n * auth-base's root fields that declare no `@privilege` — every one of them, and why.\n *\n * There is no `notYetDeclared` here. The twelve that were open without a reason were closed on\n * 2026-09-20 (`c02ee9bdc1`); what is left answers about the caller, or about the installation\n * rather than anybody's data. An addition to this list needs its reason written first.\n */\nexport const openFields: OpenFieldsDeclaration = {\n byDesign: {\n 'Query.myAuditActions': \"the caller's own actions - the actor is the session's user\",\n 'Query.myRoles': \"the caller's own roles\",\n 'Query.hasPrivilege': 'whether the caller has one — a gate here would need the answer to itself',\n 'Query.domainsWithPrivilege': 'the domains where the caller has one',\n 'Query.checkUserBelongsDomain': 'whether the caller belongs to this domain',\n 'Query.checkResettablePasswordToDefault': \"about the caller's own password\",\n 'Query.checkDefaultPassword': 'whether the caller is still on the default password',\n 'Query.passwordRule':\n \"the installation's password policy. The change-password screen needs it before the caller has any privilege — and after a forced reset they may have none\",\n 'Query.authProviderTypes': \"which provider types this build supports. A catalogue, not a tenant's configuration\",\n 'Query.myAccessRequests': \"the caller's own access requests\",\n 'Query.mayChangeParentRoles': \"whether the caller may give the parent domain's roles here - about the caller only\",\n 'Query.accessRequestDeciderCount': 'how many people an access request from the caller would reach - a number, never who',\n 'Mutation.requestAccess':\n 'asked by someone refused for want of a role - a gate here would need the privilege being asked for. The door asks only for the caller and judges again that a role would open it (ADR-0103)',\n 'Mutation.withdrawAccessRequest': \"withdraws the caller's own pending request and nobody else's\"\n }\n}\n"]}
1
+ {"version":3,"file":"open-fields.js","sourceRoot":"","sources":["../../server/service/open-fields.ts"],"names":[],"mappings":";;;AAEA;;;;;;GAMG;AACU,QAAA,UAAU,GAA0B;IAC/C,QAAQ,EAAE;QACR,sBAAsB,EAAE,4DAA4D;QACpF,eAAe,EAAE,wBAAwB;QACzC,oBAAoB,EAAE,0EAA0E;QAChG,4BAA4B,EAAE,sCAAsC;QACpE,8BAA8B,EAAE,2CAA2C;QAC3E,wCAAwC,EAAE,iCAAiC;QAC3E,4BAA4B,EAAE,qDAAqD;QACnF,oBAAoB,EAClB,2JAA2J;QAC7J,yBAAyB,EAAE,qFAAqF;QAChH,wBAAwB,EAAE,kCAAkC;QAC5D,4BAA4B,EAAE,oFAAoF;QAClH,iCAAiC,EAAE,qFAAqF;QACxH,qBAAqB,EAAE,qHAAqH;QAC5I,wBAAwB,EACtB,6LAA6L;QAC/L,gCAAgC,EAAE,8DAA8D;KACjG;CACF,CAAA","sourcesContent":["import type { OpenFieldsDeclaration } from './privilege/open-field-rule.js'\n\n/**\n * auth-base's root fields that declare no `@privilege` — every one of them, and why.\n *\n * There is no `notYetDeclared` here. The twelve that were open without a reason were closed on\n * 2026-09-20 (`c02ee9bdc1`); what is left answers about the caller, or about the installation\n * rather than anybody's data. An addition to this list needs its reason written first.\n */\nexport const openFields: OpenFieldsDeclaration = {\n byDesign: {\n 'Query.myAuditActions': \"the caller's own actions - the actor is the session's user\",\n 'Query.myRoles': \"the caller's own roles\",\n 'Query.hasPrivilege': 'whether the caller has one — a gate here would need the answer to itself',\n 'Query.domainsWithPrivilege': 'the domains where the caller has one',\n 'Query.checkUserBelongsDomain': 'whether the caller belongs to this domain',\n 'Query.checkResettablePasswordToDefault': \"about the caller's own password\",\n 'Query.checkDefaultPassword': 'whether the caller is still on the default password',\n 'Query.passwordRule':\n \"the installation's password policy. The change-password screen needs it before the caller has any privilege — and after a forced reset they may have none\",\n 'Query.authProviderTypes': \"which provider types this build supports. A catalogue, not a tenant's configuration\",\n 'Query.myAccessRequests': \"the caller's own access requests\",\n 'Query.mayChangeParentRoles': \"whether the caller may give the parent domain's roles here - about the caller only\",\n 'Query.accessRequestDeciderCount': 'how many people an access request from the caller would reach - a number, never who',\n 'Query.tasksGranting': 'the tasks a refused person may ask for - the code catalog of work only, nothing of the domain (ADR-0103 decision 9)',\n 'Mutation.requestAccess':\n 'asked by someone refused for want of a role - a gate here would need the privilege being asked for. The door asks only for the caller and judges again that a role would open it (ADR-0103)',\n 'Mutation.withdrawAccessRequest': \"withdraws the caller's own pending request and nobody else's\"\n }\n}\n"]}
@@ -2,4 +2,4 @@ import { RoleTemplateGapResolver } from './role-template-gap-resolver.js';
2
2
  import { RoleTemplateMutation } from './role-template-mutation.js';
3
3
  import { RoleTemplateQuery } from './role-template-query.js';
4
4
  export declare const entities: any[];
5
- export declare const resolvers: (typeof RoleTemplateQuery | typeof RoleTemplateMutation | typeof RoleTemplateGapResolver)[];
5
+ export declare const resolvers: (typeof RoleTemplateGapResolver | typeof RoleTemplateMutation | typeof RoleTemplateQuery)[];