@things-factory/auth-base 10.1.192 → 10.1.202
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.
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/ai-entry/access-assistant.js +4 -3
- package/dist-server/ai-entry/access-assistant.js.map +1 -1
- package/dist-server/controllers/access-request.d.ts +8 -2
- package/dist-server/controllers/access-request.js +33 -6
- package/dist-server/controllers/access-request.js.map +1 -1
- package/dist-server/controllers/signin.d.ts +5 -0
- package/dist-server/controllers/signin.js +25 -0
- package/dist-server/controllers/signin.js.map +1 -1
- package/dist-server/index.js +7 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/middlewares/audit-scope-middleware.d.ts +7 -0
- package/dist-server/middlewares/audit-scope-middleware.js +15 -0
- package/dist-server/middlewares/audit-scope-middleware.js.map +1 -0
- package/dist-server/middlewares/index.js +3 -0
- package/dist-server/middlewares/index.js.map +1 -1
- package/dist-server/role-tasks.d.ts +2 -0
- package/dist-server/role-tasks.js +20 -0
- package/dist-server/role-tasks.js.map +1 -0
- package/dist-server/router/auth-checkin-router.js +3 -5
- package/dist-server/router/auth-checkin-router.js.map +1 -1
- package/dist-server/router/auth-public-process-router.js +7 -1
- package/dist-server/router/auth-public-process-router.js.map +1 -1
- package/dist-server/router/site-root-router.d.ts +2 -0
- package/dist-server/router/site-root-router.js +2 -0
- package/dist-server/router/site-root-router.js.map +1 -1
- package/dist-server/service/access-request/access-request-query.d.ts +7 -2
- package/dist-server/service/access-request/access-request-query.js +25 -4
- package/dist-server/service/access-request/access-request-query.js.map +1 -1
- package/dist-server/service/access-request/access-request-types.d.ts +7 -0
- package/dist-server/service/access-request/access-request-types.js +27 -1
- package/dist-server/service/access-request/access-request-types.js.map +1 -1
- package/dist-server/service/access-request/access-request.d.ts +1 -0
- package/dist-server/service/access-request/access-request.js +5 -0
- package/dist-server/service/access-request/access-request.js.map +1 -1
- package/dist-server/service/audit/audit-action.d.ts +36 -0
- package/dist-server/service/audit/audit-action.js +137 -0
- package/dist-server/service/audit/audit-action.js.map +1 -0
- package/dist-server/service/audit/audit-kinds.d.ts +19 -0
- package/dist-server/service/audit/audit-kinds.js +70 -0
- package/dist-server/service/audit/audit-kinds.js.map +1 -0
- package/dist-server/service/audit/audit-query.d.ts +11 -0
- package/dist-server/service/audit/audit-query.js +295 -0
- package/dist-server/service/audit/audit-query.js.map +1 -0
- package/dist-server/service/audit/audit-recorder.d.ts +77 -0
- package/dist-server/service/audit/audit-recorder.js +247 -0
- package/dist-server/service/audit/audit-recorder.js.map +1 -0
- package/dist-server/service/audit/audit-request.d.ts +28 -0
- package/dist-server/service/audit/audit-request.js +96 -0
- package/dist-server/service/audit/audit-request.js.map +1 -0
- package/dist-server/service/audit/audit-retention.d.ts +12 -0
- package/dist-server/service/audit/audit-retention.js +42 -0
- package/dist-server/service/audit/audit-retention.js.map +1 -0
- package/dist-server/service/audit/audit-subscriber.d.ts +39 -0
- package/dist-server/service/audit/audit-subscriber.js +191 -0
- package/dist-server/service/audit/audit-subscriber.js.map +1 -0
- package/dist-server/service/audit/audit-target.d.ts +19 -0
- package/dist-server/service/audit/audit-target.js +46 -0
- package/dist-server/service/audit/audit-target.js.map +1 -0
- package/dist-server/service/audit/audit-types.d.ts +44 -0
- package/dist-server/service/audit/audit-types.js +161 -0
- package/dist-server/service/audit/audit-types.js.map +1 -0
- package/dist-server/service/audit/index.d.ts +6 -0
- package/dist-server/service/audit/index.js +11 -0
- package/dist-server/service/audit/index.js.map +1 -0
- package/dist-server/service/domain-owner/domain-owner-mutation.js +3 -0
- package/dist-server/service/domain-owner/domain-owner-mutation.js.map +1 -1
- package/dist-server/service/index.d.ts +9 -2
- package/dist-server/service/index.js +19 -4
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/open-fields.js +2 -1
- package/dist-server/service/open-fields.js.map +1 -1
- package/dist-server/service/privilege/privilege-directive.js +25 -1
- package/dist-server/service/privilege/privilege-directive.js.map +1 -1
- package/dist-server/service/role/role-mutation.js +8 -1
- package/dist-server/service/role/role-mutation.js.map +1 -1
- package/dist-server/service/role-template/role-task.d.ts +81 -0
- package/dist-server/service/role-template/role-task.js +129 -0
- package/dist-server/service/role-template/role-task.js.map +1 -0
- package/dist-server/service/role-template/role-template-gap-resolver.d.ts +9 -5
- package/dist-server/service/role-template/role-template-gap-resolver.js +41 -13
- package/dist-server/service/role-template/role-template-gap-resolver.js.map +1 -1
- package/dist-server/service/role-template/role-template-mutation.js +10 -1
- package/dist-server/service/role-template/role-template-mutation.js.map +1 -1
- package/dist-server/service/role-template/role-template-query.js +4 -2
- package/dist-server/service/role-template/role-template-query.js.map +1 -1
- package/dist-server/service/role-template/role-template-types.d.ts +24 -0
- package/dist-server/service/role-template/role-template-types.js +63 -1
- package/dist-server/service/role-template/role-template-types.js.map +1 -1
- package/dist-server/service/role-template/role-template.d.ts +63 -10
- package/dist-server/service/role-template/role-template.js +92 -18
- package/dist-server/service/role-template/role-template.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/utils/access-request-review.d.ts +12 -2
- package/dist-server/utils/access-request-review.js +13 -3
- package/dist-server/utils/access-request-review.js.map +1 -1
- package/dist-server/utils/check-permission.d.ts +6 -0
- package/dist-server/utils/check-permission.js +23 -8
- package/dist-server/utils/check-permission.js.map +1 -1
- package/package.json +3 -2
- package/tests/access-assistant-db.test.ts +1 -1
- package/tests/access-request-db.test.ts +71 -0
- package/tests/access-request-review.test.ts +30 -0
- package/tests/audit-db.test.ts +394 -0
- package/tests/auth-base-role-tasks.test.ts +58 -0
- package/tests/list-inheritance-db.test.ts +0 -7
- package/tests/open-doors.test.ts +2 -2
- package/tests/refusal-cause.test.ts +1 -1
- package/tests/role-task.test.ts +168 -0
- package/tests/role-template-gap-db.test.ts +77 -2
- package/translations/en.json +14 -1
- package/translations/es.json +14 -1
- package/translations/ja.json +14 -1
- package/translations/ko.json +14 -1
- package/translations/ms.json +14 -1
- package/translations/zh.json +14 -1
- package/dist-server/service/login-history/index.d.ts +0 -4
- package/dist-server/service/login-history/index.js +0 -8
- package/dist-server/service/login-history/index.js.map +0 -1
- package/dist-server/service/login-history/login-history-query.d.ts +0 -10
- package/dist-server/service/login-history/login-history-query.js +0 -80
- package/dist-server/service/login-history/login-history-query.js.map +0 -1
- package/dist-server/service/login-history/login-history-type.d.ts +0 -5
- package/dist-server/service/login-history/login-history-type.js +0 -21
- package/dist-server/service/login-history/login-history-type.js.map +0 -1
- package/dist-server/service/login-history/login-history.d.ts +0 -12
- package/dist-server/service/login-history/login-history.js +0 -58
- 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,4 +1,9 @@
|
|
|
1
1
|
import { User } from '../service/user/user.js';
|
|
2
|
+
/**
|
|
3
|
+
* A sign-in, and its audit (ADR-0141 §3.3): a success or a failure with its code. A failure is
|
|
4
|
+
* written on its own and stays when the request fails (§4-3). An account that does not exist leaves
|
|
5
|
+
* no actor - the name typed in is not a person.
|
|
6
|
+
*/
|
|
2
7
|
export declare function signin(attrs: {
|
|
3
8
|
username: string;
|
|
4
9
|
password: string;
|
|
@@ -8,7 +8,31 @@ const auth_error_js_1 = require("../errors/auth-error.js");
|
|
|
8
8
|
const user_js_1 = require("../service/user/user.js");
|
|
9
9
|
const account_lock_rule_js_1 = require("../utils/account-lock-rule.js");
|
|
10
10
|
const lock_duration_js_1 = require("../utils/lock-duration.js");
|
|
11
|
+
const audit_recorder_js_1 = require("../service/audit/audit-recorder.js");
|
|
12
|
+
const audit_target_js_1 = require("../service/audit/audit-target.js");
|
|
13
|
+
/**
|
|
14
|
+
* A sign-in, and its audit (ADR-0141 §3.3): a success or a failure with its code. A failure is
|
|
15
|
+
* written on its own and stays when the request fails (§4-3). An account that does not exist leaves
|
|
16
|
+
* no actor - the name typed in is not a person.
|
|
17
|
+
*/
|
|
11
18
|
async function signin(attrs, context) {
|
|
19
|
+
const attempt = {};
|
|
20
|
+
const domainId = context?.state?.domain?.id ?? null;
|
|
21
|
+
/*
|
|
22
|
+
* Not awaited: the answer does not wait on its record, and a write that fails is counted, never
|
|
23
|
+
* thrown (`recordAuditAction` without a transaction).
|
|
24
|
+
*/
|
|
25
|
+
try {
|
|
26
|
+
const answer = await signinOnce(attrs, context, attempt);
|
|
27
|
+
void (0, audit_recorder_js_1.recordAuditAction)({ kind: 'auth.signin', outcome: 'ok' }, { actorId: answer.user.id, domainId });
|
|
28
|
+
return answer;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
void (0, audit_recorder_js_1.recordAuditAction)({ kind: 'auth.signin', outcome: 'error', code: (0, audit_target_js_1.errorCodeOf)(error) }, { actorId: attempt.userId ?? null, domainId });
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function signinOnce(attrs, context, attempt) {
|
|
12
36
|
const { domain } = context?.state || {};
|
|
13
37
|
const { username } = attrs;
|
|
14
38
|
const repository = (0, shell_1.getRepository)(user_js_1.User);
|
|
@@ -28,6 +52,7 @@ async function signin(attrs, context) {
|
|
|
28
52
|
throw new auth_error_js_1.AuthError({
|
|
29
53
|
errorCode: auth_error_js_1.AuthError.ERROR_CODES.USER_NOT_FOUND
|
|
30
54
|
});
|
|
55
|
+
attempt.userId = user.id;
|
|
31
56
|
if (user.status == user_js_1.UserStatus.DELETED) {
|
|
32
57
|
throw new auth_error_js_1.AuthError({
|
|
33
58
|
errorCode: auth_error_js_1.AuthError.ERROR_CODES.USER_DELETED
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signin.js","sourceRoot":"","sources":["../../server/controllers/signin.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"signin.js","sourceRoot":"","sources":["../../server/controllers/signin.ts"],"names":[],"mappings":"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","sourcesContent":["import { ILike } from 'typeorm'\nimport { getRepository } from '@things-factory/shell'\n\nimport { sendUnlockUserEmail } from '../controllers/unlock-user.js'\nimport { AuthError } from '../errors/auth-error.js'\nimport { User, UserStatus } from '../service/user/user.js'\nimport { isAccountLocked } from '../utils/account-lock-rule.js'\nimport { lockDurationMinutes } from '../utils/lock-duration.js'\nimport { recordAuditAction } from '../service/audit/audit-recorder.js'\nimport { errorCodeOf } from '../service/audit/audit-target.js'\n\n/**\n * A sign-in, and its audit (ADR-0141 §3.3): a success or a failure with its code. A failure is\n * written on its own and stays when the request fails (§4-3). An account that does not exist leaves\n * no actor - the name typed in is not a person.\n */\nexport async function signin(attrs: { username: string; password: string }, context?) {\n const attempt: { userId?: string } = {}\n const domainId = context?.state?.domain?.id ?? null\n\n /*\n * Not awaited: the answer does not wait on its record, and a write that fails is counted, never\n * thrown (`recordAuditAction` without a transaction).\n */\n try {\n const answer = await signinOnce(attrs, context, attempt)\n void recordAuditAction({ kind: 'auth.signin', outcome: 'ok' }, { actorId: answer.user.id, domainId })\n return answer\n } catch (error) {\n void recordAuditAction({ kind: 'auth.signin', outcome: 'error', code: errorCodeOf(error) }, { actorId: attempt.userId ?? null, domainId })\n throw error\n }\n}\n\nasync function signinOnce(attrs: { username: string; password: string }, context, attempt: { userId?: string }) {\n const { domain } = context?.state || {}\n const { username } = attrs\n\n const repository = getRepository(User)\n\n var user = await repository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await repository.findOne({\n where: {\n email: ILike(username)\n },\n relations: ['domains']\n })\n }\n\n if (!user)\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_FOUND\n })\n\n attempt.userId = user.id\n\n if (user.status == UserStatus.DELETED) {\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_DELETED\n })\n }\n\n if (user.status == UserStatus.LOCKED) {\n /* 시계는 한 번만 읽는다 — 두 번 읽으면 경계에 걸린 잠금이 두 판정 사이에서 갈린다. */\n const now = new Date()\n\n if (isAccountLocked(user, now, lockDurationMinutes())) {\n sendUnlockUserEmail({\n user,\n context\n })\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_LOCKED,\n detail: {\n username: user.username,\n email: user.email\n }\n })\n }\n\n /*\n * 시간이 지나 스스로 풀렸다 — 그 자리에서 행을 되돌리고 계속 진행한다. 주기 작업으로\n * 풀지 않는 이유는 `account-lock-rule.ts` 에 있다(계정이 10만이면 그것이 따로 일이 된다).\n *\n * `failCount` 도 함께 0 으로 둔다. 그래서 **잠금이 곧 실패 셈의 창**이 된다 — 이 계정이\n * 다시 잠기려면 지금부터 다섯 번을 틀려야 한다.\n */\n user.status = UserStatus.ACTIVATED\n user.failCount = 0\n user.lockedAt = null\n\n await repository.save(user)\n }\n\n if (!User.verify(user.password, attrs.password, user.salt)) {\n user.failCount++\n if (user.failCount >= 5) {\n user.status = UserStatus.LOCKED\n /* 언제 풀리나를 계산할 수 있게 시각을 남긴다. 이 칸이 비면 판정 규칙이\n 잠금을 유지하는 쪽으로 기운다. */\n user.lockedAt = new Date()\n }\n await repository.save(user)\n if (user.status == UserStatus.LOCKED) {\n sendUnlockUserEmail({\n user,\n context\n })\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_LOCKED,\n detail: {\n username: user.username,\n email: user.email\n }\n })\n }\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.PASSWORD_NOT_MATCHED,\n detail: {\n username: user.username,\n email: user.email,\n failCount: user.failCount\n }\n })\n } else {\n user.failCount = 0\n await repository.save(user)\n }\n\n if (user.status == UserStatus.INACTIVE) {\n throw new AuthError({\n errorCode: AuthError.ERROR_CODES.USER_NOT_ACTIVATED,\n detail: {\n username: user.username,\n email: user.email\n }\n })\n }\n\n return {\n user,\n token: await user.sign({ subdomain: domain?.subdomain }),\n domains: user.domains || []\n }\n}\n"]}
|
package/dist-server/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const tslib_1 = require("tslib");
|
|
|
4
4
|
const privilege_sync_js_1 = require("./service/privilege/privilege-sync.js");
|
|
5
5
|
const privilege_axis_js_1 = require("./service/privilege/privilege-axis.js");
|
|
6
6
|
const role_template_js_1 = require("./service/role-template/role-template.js");
|
|
7
|
+
const audit_retention_js_1 = require("./service/audit/audit-retention.js");
|
|
7
8
|
require("./routes.js");
|
|
8
9
|
tslib_1.__exportStar(require("./service/index.js"), exports);
|
|
9
10
|
/* AI 입력 declarations - plain data, registered by an application that runs the assistant (ADR-0103). */
|
|
@@ -44,5 +45,11 @@ process.on('bootstrap-module-start', async ({ app, config, client }) => {
|
|
|
44
45
|
* declared exactly these".
|
|
45
46
|
*/
|
|
46
47
|
(0, privilege_axis_js_1.reportDeclaredAxes)();
|
|
48
|
+
/* Audit rows past the retention go, once a day, on one instance (ADR-0141 §7). */
|
|
49
|
+
(0, audit_retention_js_1.startAuditRetention)();
|
|
47
50
|
});
|
|
51
|
+
const role_task_js_1 = require("./service/role-template/role-task.js");
|
|
52
|
+
const role_tasks_js_1 = require("./role-tasks.js");
|
|
53
|
+
for (const task of role_tasks_js_1.ROLE_TASKS)
|
|
54
|
+
(0, role_task_js_1.registerRoleTask)(task);
|
|
48
55
|
//# sourceMappingURL=index.js.map
|
package/dist-server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,6EAA6E;AAC7E,6EAA0E;AAC1E,+EAAsF;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,6EAA6E;AAC7E,6EAA0E;AAC1E,+EAAsF;AACtF,2EAAwE;AAExE,uBAAoB;AAEpB,6DAAkC;AAClC,uGAAuG;AACvG,6EAAkD;AAClD,yEAA8C;AAE9C,gEAAqC;AAErC,iEAAsC;AACtC,4DAAiC;AACjC,sDAA2B;AAE3B,sEAA2C;AAC3C,sEAA2C;AAC3C,yEAA8C;AAC9C,6EAAkD;AAClD,sEAA2C;AAC3C,gEAAqC;AACrC,+EAAoD;AACpD,yEAA8C;AAC9C,mEAAwC;AACxC,sEAA2C;AAC3C,yEAA8C;AAC9C,oEAAyC;AAEzC,4DAAiC;AAEjC,qDAA0B;AAE1B,OAAO,CAAC,EAAE,CAAC,wBAA+B,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAO,EAAE,EAAE;IACjF,MAAM,IAAA,yCAAqB,EAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAqC,CAAC,CAAC,CAAA;IAErG,OAAO,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAA;IAExF;;;;;OAKG;IACH,IAAA,8CAA2B,GAAE,CAAA;IAE7B;;;;;OAKG;IACH,IAAA,sCAAkB,GAAE,CAAA;IAEpB,kFAAkF;IAClF,IAAA,wCAAmB,GAAE,CAAA;AACvB,CAAC,CAAC,CAAA;AAEF,uEAAuE;AACvE,mDAA4C;AAE5C,KAAK,MAAM,IAAI,IAAI,0BAAU;IAAE,IAAA,+BAAgB,EAAC,IAAI,CAAC,CAAA","sourcesContent":["import { synchronizePrivileges } from './service/privilege/privilege-sync.js'\nimport { reportDeclaredAxes } from './service/privilege/privilege-axis.js'\nimport { reportUnknownTemplateGrants } from './service/role-template/role-template.js'\nimport { startAuditRetention } from './service/audit/audit-retention.js'\n\nimport './routes.js'\n\nexport * from './service/index.js'\n/* AI 입력 declarations - plain data, registered by an application that runs the assistant (ADR-0103). */\nexport * from './ai-entry/access-entry-targets.js'\nexport * from './ai-entry/access-assistant.js'\nexport type { EntryTargetDeclaration as AuthEntryTarget } from './ai-entry/entry-target-shape.js'\nexport * from './migrations/index.js'\n\nexport * from './middlewares/index.js'\nexport * from './router/index.js'\nexport * from './routes.js'\n\nexport * from './utils/get-domain-users.js'\nexport * from './utils/get-user-domains.js'\nexport * from './utils/checkin-domain-rule.js'\nexport * from './utils/can-user-checkin-domain.js'\nexport * from './utils/domain-ownership.js'\nexport * from './utils/get-secret.js'\nexport * from './utils/check-user-belongs-domain.js'\nexport * from './utils/access-token-cookie.js'\nexport * from './utils/encrypt-state.js'\nexport * from './utils/check-permission.js'\nexport * from './utils/check-user-has-role.js'\nexport * from './utils/build-auth-url.js'\n\nexport * from './errors/index.js'\n\nexport * from './types.js'\n\nprocess.on('bootstrap-module-start' as any, async ({ app, config, client }: any) => {\n await synchronizePrivileges(Object.values(process['PRIVILEGES'] as Record<string, [string, string]>))\n\n console.log('[auth-base:bootstrap] Synchronization for privilege master has just done.')\n\n /*\n * Templates name privileges by (category, axis), and a category can move. A template\n * that survives a rename becomes a role that grants nothing — the administrator picks\n * \"quality inspector\", gets a role, and no door opens. Check the names once, here, where\n * the privilege registry has just been filled.\n */\n reportUnknownTemplateGrants()\n\n /*\n * Say which axis vocabulary this installation ended up with. It cannot go in the SDL —\n * it differs per installation, which is the whole point — so a boot line is the honest\n * substitute, and it separates \"nobody declared, these are the defaults\" from \"someone\n * declared exactly these\".\n */\n reportDeclaredAxes()\n\n /* Audit rows past the retention go, once a day, on one instance (ADR-0141 §7). */\n startAuditRetention()\n})\n\nimport { registerRoleTask } from './service/role-template/role-task.js'\nimport { ROLE_TASKS } from './role-tasks.js'\n\nfor (const task of ROLE_TASKS) registerRoleTask(task)\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every request runs as one audited request (ADR-0141 §3.1). A GraphQL request always leaves its
|
|
3
|
+
* line; any other request only when something in it was recorded - a sign-in, a check-in.
|
|
4
|
+
*
|
|
5
|
+
* Installed first, so the sign-in routes and the GraphQL authentication after it are inside.
|
|
6
|
+
*/
|
|
7
|
+
export declare function auditScopeMiddleware(context: any, next: () => Promise<any>): Promise<any>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.auditScopeMiddleware = auditScopeMiddleware;
|
|
4
|
+
const audit_recorder_js_1 = require("../service/audit/audit-recorder.js");
|
|
5
|
+
/**
|
|
6
|
+
* Every request runs as one audited request (ADR-0141 §3.1). A GraphQL request always leaves its
|
|
7
|
+
* line; any other request only when something in it was recorded - a sign-in, a check-in.
|
|
8
|
+
*
|
|
9
|
+
* Installed first, so the sign-in routes and the GraphQL authentication after it are inside.
|
|
10
|
+
*/
|
|
11
|
+
async function auditScopeMiddleware(context, next) {
|
|
12
|
+
const channel = context.method == 'POST' && context.path.startsWith('/graphql') ? 'graphql' : 'http';
|
|
13
|
+
return await (0, audit_recorder_js_1.runInAuditScope)(context, channel, next);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=audit-scope-middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-scope-middleware.js","sourceRoot":"","sources":["../../server/middlewares/audit-scope-middleware.ts"],"names":[],"mappings":";;AAQA,oDAIC;AAZD,0EAAoE;AAEpE;;;;;GAKG;AACI,KAAK,UAAU,oBAAoB,CAAC,OAAY,EAAE,IAAwB;IAC/E,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAA;IAEpG,OAAO,MAAM,IAAA,mCAAe,EAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;AACtD,CAAC","sourcesContent":["import { runInAuditScope } from '../service/audit/audit-recorder.js'\n\n/**\n * Every request runs as one audited request (ADR-0141 §3.1). A GraphQL request always leaves its\n * line; any other request only when something in it was recorded - a sign-in, a check-in.\n *\n * Installed first, so the sign-in routes and the GraphQL authentication after it are inside.\n */\nexport async function auditScopeMiddleware(context: any, next: () => Promise<any>) {\n const channel = context.method == 'POST' && context.path.startsWith('/graphql') ? 'graphql' : 'http'\n\n return await runInAuditScope(context, channel, next)\n}\n"]}
|
|
@@ -11,10 +11,13 @@ const domain_authenticate_middleware_js_1 = require("./domain-authenticate-middl
|
|
|
11
11
|
const graphql_authenticate_middleware_js_1 = require("./graphql-authenticate-middleware.js");
|
|
12
12
|
const jwt_authenticate_middleware_js_1 = require("./jwt-authenticate-middleware.js");
|
|
13
13
|
const bypass_signin_middleware_js_1 = require("./bypass-signin-middleware.js");
|
|
14
|
+
const audit_scope_middleware_js_1 = require("./audit-scope-middleware.js");
|
|
14
15
|
const user_js_1 = require("../service/user/user.js");
|
|
15
16
|
const max_age_js_1 = require("../constants/max-age.js");
|
|
16
17
|
const bypassUserSigninProcess = env_1.config.get('bypassUserSigninProcess', false);
|
|
17
18
|
function initMiddlewares(app) {
|
|
19
|
+
/* Before everything else here, so sign-in and the GraphQL authentication run inside the request's audit. */
|
|
20
|
+
app.use(audit_scope_middleware_js_1.auditScopeMiddleware);
|
|
18
21
|
if (bypassUserSigninProcess) {
|
|
19
22
|
app.use(bypass_signin_middleware_js_1.bypassSigninMiddleware);
|
|
20
23
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/middlewares/index.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/middlewares/index.ts"],"names":[],"mappings":";;AAmBA,0CAgDC;;AAlED,sEAAiC;AACjC,wEAAmC;AAEnC,6CAA4C;AAC5C,0DAA+C;AAE/C,qFAA4E;AAC5E,2FAAkF;AAClF,6FAAoF;AACpF,qFAA4E;AAC5E,+EAAsE;AACtE,2EAAkE;AAElE,qDAA8C;AAC9C,wDAAiD;AAEjD,MAAM,uBAAuB,GAAG,YAAM,CAAC,GAAG,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAA;AAE5E,SAAgB,eAAe,CAAC,GAAQ;IACtC,4GAA4G;IAC5G,GAAG,CAAC,GAAG,CAAC,gDAAoB,CAAC,CAAA;IAE7B,IAAI,uBAAuB,EAAE,CAAC;QAC5B,GAAG,CAAC,GAAG,CAAC,oDAAsB,CAAC,CAAA;QAC/B,OAAM;IACR,CAAC;IAED,yDAAyD;IACzD,GAAG,CAAC,IAAI,GAAG,CAAC,sBAAM,CAAC,CAAA;IACnB,GAAG,CAAC,GAAG,CACL,IAAA,qBAAO,EACL;QACE,GAAG,EAAE,WAAW;QAChB,MAAM,EAAE,oBAAO;QACf,SAAS,EAAE,IAAI;QACf,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,KAAK;KACb,EACD,GAAG,CACJ,CACF,CAAA;IAED,sBAAQ,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE;QACvC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACrB,CAAC,CAAC,CAAA;IAEF,sBAAQ,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAC/C,IAAI,CAAC,IAAI,EAAE,MAAM,cAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;IAEF,yBAAyB;IACzB,GAAG,CAAC,GAAG,CAAC,sBAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;IAE9B,kDAAkD;IAClD,GAAG,CAAC,GAAG,CAAC,sBAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IAE3B,mCAAmC;IACnC,GAAG,CAAC,GAAG,CAAC,0DAAyB,CAAC,CAAA;IAElC;;;OAGG;IACH,GAAG,CAAC,GAAG,CAAC,kEAA6B,CAAC,CAAA;AACxC,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,+BAAsC,EAAE,CAAC,GAAG,EAAE,sBAAsB,EAAE,EAAE;IACjF,sBAAsB,CAAC,IAAI,CAAC,0DAAyB,EAAE,gEAA4B,CAAC,CAAA;AACtF,CAAC,CAAC,CAAA;AAEF,2EAAgD;AAChD,8EAAmD;AACnD,iEAAsC;AACtC,mEAAwC;AACxC,2EAAgD","sourcesContent":["import Koa from 'koa'\nimport session from 'koa-session'\nimport passport from 'koa-passport'\n\nimport { config } from '@things-factory/env'\nimport { SECRET } from '../utils/get-secret.js'\n\nimport { authenticate401Middleware } from './authenticate-401-middleware.js'\nimport { domainAuthenticateMiddleware } from './domain-authenticate-middleware.js'\nimport { graphqlAuthenticateMiddleware } from './graphql-authenticate-middleware.js'\nimport { jwtAuthenticateMiddleware } from './jwt-authenticate-middleware.js'\nimport { bypassSigninMiddleware } from './bypass-signin-middleware.js'\nimport { auditScopeMiddleware } from './audit-scope-middleware.js'\n\nimport { User } from '../service/user/user.js'\nimport { MAX_AGE } from '../constants/max-age.js'\n\nconst bypassUserSigninProcess = config.get('bypassUserSigninProcess', false)\n\nexport function initMiddlewares(app: Koa) {\n /* Before everything else here, so sign-in and the GraphQL authentication run inside the request's audit. */\n app.use(auditScopeMiddleware)\n\n if (bypassUserSigninProcess) {\n app.use(bypassSigninMiddleware)\n return\n }\n\n /* oauth2orize-koa 에서 oauth 트랜잭션 관리를 위해서 session을 사용함. */\n app.keys = [SECRET]\n app.use(\n session(\n {\n key: 'tfsession',\n maxAge: MAX_AGE,\n overwrite: true,\n httpOnly: true,\n signed: true,\n rolling: false,\n renew: false\n },\n app\n )\n )\n\n passport.serializeUser((profile, done) => {\n done(null, profile)\n })\n\n passport.deserializeUser(async (profile, done) => {\n done(null, await User.checkAuth(profile))\n })\n\n /* passport initialize */\n app.use(passport.initialize())\n\n /* passport use session - for oauth transaction */\n app.use(passport.session())\n\n /* authentication error handling */\n app.use(authenticate401Middleware)\n\n /*\n * post:graphql 에 대해서는 graphqlAuthenticationMiddleware를 적용한다.\n * graphql app을 router에 적용하지 못하기 때문임.\n */\n app.use(graphqlAuthenticateMiddleware)\n}\n\nprocess.on('bootstrap-module-subscription' as any, (app, subscriptionMiddleware) => {\n subscriptionMiddleware.push(jwtAuthenticateMiddleware, domainAuthenticateMiddleware)\n})\n\nexport * from './jwt-authenticate-middleware.js'\nexport * from './domain-authenticate-middleware.js'\nexport * from './signin-middleware.js'\nexport * from './webauthn-middleware.js'\nexport * from './authenticate-401-middleware.js'\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* The work this module names, for the seats applications build (ADR-0139 decision 11).
|
|
4
|
+
*
|
|
5
|
+
* A task holds whole privileges: each opens everything its doors open, and the words under `task.<id>` in this
|
|
6
|
+
* module's translations say what that is. Seats are put together by applications (`RoleTemplate.tasks`).
|
|
7
|
+
*
|
|
8
|
+
* Data only - `server/index.ts` registers it - so an application's tests read it without starting the framework.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.ROLE_TASKS = void 0;
|
|
12
|
+
exports.ROLE_TASKS = [
|
|
13
|
+
{ id: 'auth-base.users-and-roles', grants: [{ category: 'user', axes: ['query', 'mutation'] }] },
|
|
14
|
+
{ id: 'auth-base.privilege-reading', grants: [{ category: 'privilege', axes: ['query'] }] },
|
|
15
|
+
{ id: 'auth-base.security', grants: [{ category: 'security', axes: ['query', 'mutation'] }] },
|
|
16
|
+
{ id: 'auth-base.env-vars', grants: [{ category: 'env-var', axes: ['query', 'mutation'] }] },
|
|
17
|
+
/* Everybody's audit record in the domain (ADR-0141 §5). */
|
|
18
|
+
{ id: 'auth-base.audit-reading', grants: [{ category: 'audit', axes: ['query'] }] }
|
|
19
|
+
];
|
|
20
|
+
//# sourceMappingURL=role-tasks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-tasks.js","sourceRoot":"","sources":["../server/role-tasks.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAIU,QAAA,UAAU,GAAe;IACpC,EAAE,EAAE,EAAE,2BAA2B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE;IAChG,EAAE,EAAE,EAAE,6BAA6B,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;IAC3F,EAAE,EAAE,EAAE,oBAAoB,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE;IAC7F,EAAE,EAAE,EAAE,oBAAoB,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE;IAC5F,2DAA2D;IAC3D,EAAE,EAAE,EAAE,yBAAyB,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;CACpF,CAAA","sourcesContent":["/*\n * The work this module names, for the seats applications build (ADR-0139 decision 11).\n *\n * A task holds whole privileges: each opens everything its doors open, and the words under `task.<id>` in this\n * module's translations say what that is. Seats are put together by applications (`RoleTemplate.tasks`).\n *\n * Data only - `server/index.ts` registers it - so an application's tests read it without starting the framework.\n */\n\nimport type { RoleTask } from './service/role-template/role-task.js'\n\nexport const ROLE_TASKS: RoleTask[] = [\n { id: 'auth-base.users-and-roles', grants: [{ category: 'user', axes: ['query', 'mutation'] }] },\n { id: 'auth-base.privilege-reading', grants: [{ category: 'privilege', axes: ['query'] }] },\n { id: 'auth-base.security', grants: [{ category: 'security', axes: ['query', 'mutation'] }] },\n { id: 'auth-base.env-vars', grants: [{ category: 'env-var', axes: ['query', 'mutation'] }] },\n /* Everybody's audit record in the domain (ADR-0141 §5). */\n { id: 'auth-base.audit-reading', grants: [{ category: 'audit', axes: ['query'] }] }\n]\n"]}
|
|
@@ -5,7 +5,7 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const koa_router_1 = tslib_1.__importDefault(require("koa-router"));
|
|
6
6
|
const env_1 = require("@things-factory/env");
|
|
7
7
|
const shell_1 = require("@things-factory/shell");
|
|
8
|
-
const
|
|
8
|
+
const audit_recorder_js_1 = require("../service/audit/audit-recorder.js");
|
|
9
9
|
const accepts_js_1 = require("../utils/accepts.js");
|
|
10
10
|
const access_token_cookie_js_1 = require("../utils/access-token-cookie.js");
|
|
11
11
|
const get_user_domains_js_1 = require("../utils/get-user-domains.js");
|
|
@@ -89,10 +89,8 @@ exports.authCheckinRouter.get('/auth/checkin/:subdomain?', async (context, next)
|
|
|
89
89
|
// })
|
|
90
90
|
async function checkIn(checkInDomain, redirectTo, context) {
|
|
91
91
|
const { user } = context.state;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
: context.req.connection.remoteAddress;
|
|
95
|
-
await login_history_js_1.LoginHistory.stamp(checkInDomain, user, remoteAddress);
|
|
92
|
+
/* The check-in is the audit's (ADR-0141 §8): its address and browser are the request line's. */
|
|
93
|
+
await (0, audit_recorder_js_1.recordAuditAction)({ kind: 'auth.checkin', outcome: 'ok' }, { actorId: user.id, domainId: checkInDomain.id });
|
|
96
94
|
if (redirectTo) {
|
|
97
95
|
return context.redirect((0, shell_1.getRedirectSubdomainPath)(context, checkInDomain, redirectTo));
|
|
98
96
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-checkin-router.js","sourceRoot":"","sources":["../../server/router/auth-checkin-router.ts"],"names":[],"mappings":";;;;AAAA,oEAA+B;AAE/B,6CAA4C;AAC5C,iDAA+F;AAE/F,
|
|
1
|
+
{"version":3,"file":"auth-checkin-router.js","sourceRoot":"","sources":["../../server/router/auth-checkin-router.ts"],"names":[],"mappings":"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","sourcesContent":["import Router from 'koa-router'\n\nimport { config } from '@things-factory/env'\nimport { Domain, findSubdomainFromPath, getRedirectSubdomainPath } from '@things-factory/shell'\n\nimport { recordAuditAction } from '../service/audit/audit-recorder.js'\nimport { User } from '../service/user/user.js'\nimport { accepts } from '../utils/accepts.js'\nimport { clearAccessTokenCookie } from '../utils/access-token-cookie.js'\nimport { getUserDomains } from '../utils/get-user-domains.js'\nimport { sameOriginRedirectPath } from '../utils/same-origin-redirect.js'\n\nconst domainTypes = config.get('domainTypes')\n\nexport const authCheckinRouter = new Router()\n\nauthCheckinRouter.get('/auth/checkin/:subdomain?', async (context, next) => {\n const { request, t } = context\n const header = request.header\n const { user } = context.state\n let { subdomain } = context.params\n\n let domains: Partial<Domain>[] = await getUserDomains(user)\n\n if (!accepts(header.accept, ['text/html', '*/*'])) {\n // When request expects non html response\n try {\n if (!subdomain) throw new Error(t('error.domain not specified', { subdomain })) // When params doesn't have subdomain\n const checkInDomain: Partial<Domain> | undefined = domains.find(d => d.subdomain === subdomain) // When no matched domain with subdomain\n if (!checkInDomain) throw new Error(t('error.domain not specified', { subdomain }))\n\n await checkIn(checkInDomain, null, context)\n context.body = true\n } catch (e) {\n clearAccessTokenCookie(context)\n throw e\n }\n } else {\n // When request expects html response\n const redirectTo = sameOriginRedirectPath(context.query.redirect_to, context)\n\n try {\n let message: string\n\n if (!subdomain) {\n /* try to find domain from redirectTo path */\n subdomain = findSubdomainFromPath(context, redirectTo)\n }\n\n let checkInDomain: Partial<Domain>\n if (subdomain) {\n checkInDomain = domains.find(d => d.subdomain == subdomain)\n if (!checkInDomain) message = t('error.domain not allowed', { subdomain })\n } else if (domains.length === 1) {\n checkInDomain = domains[0]\n }\n\n if (checkInDomain) {\n return await checkIn(checkInDomain, redirectTo, context)\n }\n\n await context.render('auth-page', {\n pageElement: 'auth-checkin',\n elementScript: '/auth/checkin.js',\n data: {\n user: {\n username: user.username,\n email: user.email,\n locale: user.locale,\n name: user.name,\n userType: user.userType\n },\n domains,\n domainTypes,\n redirectTo,\n message\n }\n })\n } catch (e) {\n clearAccessTokenCookie(context)\n context.redirect(\n `/auth/signin?username=${encodeURIComponent(user.username)}&redirect_to=${encodeURIComponent(redirectTo)}`\n )\n }\n }\n})\n\n// authCheckinRouter.get('/auth/domains', async context => {\n// const { user } = context.state\n// var domains = await getUserDomains(user)\n// if (domainTypes) {\n// domains = domains.filter(d => d.extType == domainTypes)\n// }\n\n// context.body = domains\n// })\n\nasync function checkIn(\n checkInDomain: Partial<Domain>,\n redirectTo: string | null,\n context: ResolverContext\n): Promise<void> {\n const { user }: { user: User } = context.state\n\n /* The check-in is the audit's (ADR-0141 §8): its address and browser are the request line's. */\n await recordAuditAction({ kind: 'auth.checkin', outcome: 'ok' }, { actorId: user.id, domainId: checkInDomain.id })\n\n if (redirectTo) {\n return context.redirect(getRedirectSubdomainPath(context, checkInDomain, redirectTo))\n }\n}\n"]}
|
|
@@ -12,6 +12,8 @@ const unlock_user_js_1 = require("../controllers/unlock-user.js");
|
|
|
12
12
|
const verification_js_1 = require("../controllers/verification.js");
|
|
13
13
|
const user_js_1 = require("../service/user/user.js");
|
|
14
14
|
const accepts_js_1 = require("../utils/accepts.js");
|
|
15
|
+
const audit_recorder_js_1 = require("../service/audit/audit-recorder.js");
|
|
16
|
+
const site_root_router_js_1 = require("./site-root-router.js");
|
|
15
17
|
const access_token_cookie_js_1 = require("../utils/access-token-cookie.js");
|
|
16
18
|
const disableUserSignupProcess = env_1.config.get('disableUserSignupProcess', false);
|
|
17
19
|
const disableUserFavoredLanguage = env_1.config.get('i18n/disableUserFavoredLanguage', false);
|
|
@@ -50,9 +52,13 @@ exports.authPublicProcessRouter.post('/join', async (context, next) => {
|
|
|
50
52
|
context.redirect(`/auth/signup?username=${email}&email=${email}`);
|
|
51
53
|
}
|
|
52
54
|
});
|
|
53
|
-
exports.authPublicProcessRouter.all('/signout', async (context, next) => {
|
|
55
|
+
exports.authPublicProcessRouter.all('/signout', site_root_router_js_1.findAuth, async (context, next) => {
|
|
54
56
|
const { header, t } = context;
|
|
55
57
|
(0, access_token_cookie_js_1.clearAccessTokenCookie)(context);
|
|
58
|
+
/* After the cookie is gone: an audit failure never keeps anyone signed in (ADR-0141 §4-2). */
|
|
59
|
+
if (context.state.user) {
|
|
60
|
+
await (0, audit_recorder_js_1.recordAuditAction)({ kind: 'auth.signout', outcome: 'ok' }, { domainId: null });
|
|
61
|
+
}
|
|
56
62
|
context.body = t('text.signout successfully');
|
|
57
63
|
if ((0, accepts_js_1.accepts)(header.accept, ['text/html', '*/*'])) {
|
|
58
64
|
context.redirect((0, shell_1.getSiteRootPath)(context));
|