@things-factory/auth-base 10.1.192 → 10.1.198
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/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/index.d.ts +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 +1 -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/index.d.ts +1 -1
- package/dist-server/service/role-template/role-task.d.ts +74 -0
- package/dist-server/service/role-template/role-task.js +113 -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/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/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 +13 -1
- package/translations/es.json +13 -1
- package/translations/ja.json +13 -1
- package/translations/ko.json +13 -1
- package/translations/ms.json +13 -1
- package/translations/zh.json +13 -1
|
@@ -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));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-public-process-router.js","sourceRoot":"","sources":["../../server/router/auth-public-process-router.ts"],"names":[],"mappings":"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","sourcesContent":["import Router from 'koa-router'\nimport { ILike } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { getRepository, getSiteRootPath } from '@things-factory/shell'\n\nimport { acceptInvitation } from '../controllers/invitation.js'\nimport { resetPassword, sendPasswordResetEmail } from '../controllers/reset-password.js'\nimport { unlockUser } from '../controllers/unlock-user.js'\nimport { resendVerificationEmail, verify } from '../controllers/verification.js'\nimport { User } from '../service/user/user.js'\nimport { accepts } from '../utils/accepts.js'\nimport { clearAccessTokenCookie } from '../utils/access-token-cookie.js'\n\nconst disableUserSignupProcess = config.get('disableUserSignupProcess', false)\nconst disableUserFavoredLanguage = config.get('i18n/disableUserFavoredLanguage', false)\nconst languages = config.get('i18n/languages', false)\nconst passwordRule = config.get('password') || {\n lowerCase: true,\n upperCase: true,\n digit: true,\n specialCharacter: true,\n allowRepeat: false,\n useTightPattern: true,\n useLoosePattern: false,\n tightCharacterLength: 8,\n looseCharacterLength: 15\n}\n\nexport const authPublicProcessRouter = new Router({\n prefix: '/auth'\n})\n\nauthPublicProcessRouter.post('/join', async (context, next) => {\n const { email } = context.request.body || {}\n\n const repository = getRepository(User)\n\n var user = await repository.findOne({\n where: { username: email },\n relations: ['domains']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) {\n user = await repository.findOne({\n where: { email: ILike(email) },\n relations: ['domains']\n })\n }\n\n if (user) {\n context.redirect(`/auth/signin?username=${email}`)\n } else {\n context.redirect(`/auth/signup?username=${email}&email=${email}`)\n }\n})\n\nauthPublicProcessRouter.all('/signout', async (context, next) => {\n const { header, t } = context\n clearAccessTokenCookie(context)\n\n context.body = t('text.signout successfully')\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n context.redirect(getSiteRootPath(context))\n }\n})\n\nauthPublicProcessRouter.get('/forgot-password', async (context, next) => {\n const { email } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'forgot-password',\n elementScript: '/auth/forgot-password.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\n/*\n * The mailed link is `/auth/reset-password#<token>` (and the same shape for unlock and verify). The\n * page reads the token from the fragment; this request never carries it, so nothing here reads one.\n */\nauthPublicProcessRouter.get('/reset-password', async (context, next) => {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/unlock-user', async (context, next) => {\n await context.render('auth-page', {\n pageElement: 'unlock-user',\n elementScript: '/auth/unlock-user.js',\n data: {\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/activate/:email', async (context, next) => {\n const { email } = context.params\n\n await context.render('auth-page', {\n pageElement: 'auth-activate',\n elementScript: '/auth/activate.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\n/*\n * Confirming an address is a POST from the page, not the GET of the link. The link used to be\n * `/auth/verify/<token>` and opening it confirmed — so did any mail scanner that fetched it, and the\n * token sat in every access log on the way.\n */\nauthPublicProcessRouter.get('/verify', async (context, next) => {\n await context.render('auth-page', {\n pageElement: 'auth-verify',\n elementScript: '/auth/verify.js',\n data: {\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.post('/verify', async (context, next) => {\n const { header, t } = context\n const { token } = context.request.body || {}\n\n let message: string\n\n try {\n await verify(token)\n message = t('text.user activated successfully')\n } catch (e) {\n context.status = 404\n message = t('text.invalid verification token')\n }\n\n context.body = message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n\n/*\n * This and forgot-password give one answer whether or not the address has an account to mail - a\n * different one would tell anyone which addresses are registered. Only a mail that was due and did\n * not go is a failure. Both used to set nothing in those cases, and koa answered a bare 404 that the\n * page showed as \"Not Found\".\n */\nauthPublicProcessRouter.post('/resend-verification-email', async (context, next) => {\n const { t } = context\n const { email } = context.request.body || {}\n\n if ((await resendVerificationEmail(email, context)) == 'failed') {\n context.status = 503\n context.body = t('error.mail could not be sent')\n return\n }\n\n context.status = 200\n context.body = t('text.verification email sent')\n})\n\n/*\n * ── The invitation page ─────────────────────────────────────────────────────\n * Public on purpose: the person following this link is not a member of the domain yet, which is\n * the entire point of an invitation. The old link went to `/auth/accept/<token>`, a path with no\n * route on any router, so it fell through to the private history-fallback — and the domain gate\n * there turned the invitee away for not being a member. It asked somebody trying to join to\n * already have joined.\n *\n * ── Why the token is not in this path ───────────────────────────────────────\n * It rides in the link's fragment (`#<token>`), which browsers do not send. So the request line\n * here carries nothing, and the token stays out of access logs, proxy logs and `Referer`. The\n * page reads it from the fragment, clears it from the address bar, and posts it below.\n */\nauthPublicProcessRouter.get('/accept-invitation', async (context, next) => {\n await context.render('auth-page', {\n pageElement: 'auth-accept-invitation',\n elementScript: '/auth/accept-invitation.js',\n data: {\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\n/*\n * The address is asked for as well as the token. A forwarded mail is the one way the token can\n * still travel, and nothing about where it sits can prevent that — so holding the link is not\n * enough on its own.\n */\nauthPublicProcessRouter.post('/accept-invitation', async (context, next) => {\n const { token, email, name, password } = context.request.body || ({} as any)\n\n const { domain, created } = await acceptInvitation({ token, email, name, password, context })\n\n context.body = {\n subdomain: domain?.subdomain,\n created,\n redirectTo: `/auth/signin?redirect_to=${encodeURIComponent('/')}`\n }\n})\n\nauthPublicProcessRouter.post('/forgot-password', async (context, next) => {\n const { t } = context\n const { email } = context.request.body\n\n if (!email) return next()\n\n const userRepo = getRepository(User)\n const user = await userRepo.findOne({\n where: {\n email\n }\n })\n\n if (user && !(await sendPasswordResetEmail({ user, context }))) {\n context.status = 503\n context.body = t('error.mail could not be sent')\n return\n }\n\n context.status = 200\n context.body = t('text.password reset email sent')\n})\n\nauthPublicProcessRouter.post('/reset-password', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n try {\n if (!(token && password)) {\n let message = t('error.token or password is invalid')\n\n context.status = 404\n context.body = {\n message\n }\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n\n return\n }\n\n await resetPassword(token, password, context)\n\n var message = t('text.password changed successfully')\n context.body = message\n\n clearAccessTokenCookie(context)\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n } catch (e) {\n context.status = 404\n context.body = e.message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message: e.message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n }\n})\n\nauthPublicProcessRouter.post('/unlock-user', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n /* Both are required, the way `/auth/reset-password` requires them. This read `||`, so a body\n carrying a password and no token passed the guard and reached the unlock with no token —\n see the note in `unlockUser`. */\n if (!(token && password)) {\n context.status = 404\n context.body = t('error.token or password is invalid')\n\n return\n }\n\n const succeed = await unlockUser(token, password)\n\n /*\n * Say what happened. The success page used to render whether or not the unlock took, so a\n * reused link, a token for another account, or an account that was never locked all read as\n * \"account is reactivated\" — and the person then could not sign in.\n *\n * A refusal returns to the form with a reason, the shape `/auth/reset-password` already uses.\n */\n if (!succeed) {\n const message = t('text.invalid verification token')\n\n context.status = 404\n context.body = message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'unlock-user',\n elementScript: '/auth/unlock-user.js',\n data: {\n token,\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n\n return\n }\n\n context.body = t('text.password reset succeed')\n\n clearAccessTokenCookie(context)\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message: t('text.account is reactivated'),\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n"]}
|
|
1
|
+
{"version":3,"file":"auth-public-process-router.js","sourceRoot":"","sources":["../../server/router/auth-public-process-router.ts"],"names":[],"mappings":"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","sourcesContent":["import Router from 'koa-router'\nimport { ILike } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { getRepository, getSiteRootPath } from '@things-factory/shell'\n\nimport { acceptInvitation } from '../controllers/invitation.js'\nimport { resetPassword, sendPasswordResetEmail } from '../controllers/reset-password.js'\nimport { unlockUser } from '../controllers/unlock-user.js'\nimport { resendVerificationEmail, verify } from '../controllers/verification.js'\nimport { User } from '../service/user/user.js'\nimport { accepts } from '../utils/accepts.js'\nimport { recordAuditAction } from '../service/audit/audit-recorder.js'\nimport { findAuth } from './site-root-router.js'\nimport { clearAccessTokenCookie } from '../utils/access-token-cookie.js'\n\nconst disableUserSignupProcess = config.get('disableUserSignupProcess', false)\nconst disableUserFavoredLanguage = config.get('i18n/disableUserFavoredLanguage', false)\nconst languages = config.get('i18n/languages', false)\nconst passwordRule = config.get('password') || {\n lowerCase: true,\n upperCase: true,\n digit: true,\n specialCharacter: true,\n allowRepeat: false,\n useTightPattern: true,\n useLoosePattern: false,\n tightCharacterLength: 8,\n looseCharacterLength: 15\n}\n\nexport const authPublicProcessRouter = new Router({\n prefix: '/auth'\n})\n\nauthPublicProcessRouter.post('/join', async (context, next) => {\n const { email } = context.request.body || {}\n\n const repository = getRepository(User)\n\n var user = await repository.findOne({\n where: { username: email },\n relations: ['domains']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) {\n user = await repository.findOne({\n where: { email: ILike(email) },\n relations: ['domains']\n })\n }\n\n if (user) {\n context.redirect(`/auth/signin?username=${email}`)\n } else {\n context.redirect(`/auth/signup?username=${email}&email=${email}`)\n }\n})\n\nauthPublicProcessRouter.all('/signout', findAuth, async (context, next) => {\n const { header, t } = context\n clearAccessTokenCookie(context)\n\n /* After the cookie is gone: an audit failure never keeps anyone signed in (ADR-0141 §4-2). */\n if (context.state.user) {\n await recordAuditAction({ kind: 'auth.signout', outcome: 'ok' }, { domainId: null })\n }\n\n context.body = t('text.signout successfully')\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n context.redirect(getSiteRootPath(context))\n }\n})\n\nauthPublicProcessRouter.get('/forgot-password', async (context, next) => {\n const { email } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'forgot-password',\n elementScript: '/auth/forgot-password.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\n/*\n * The mailed link is `/auth/reset-password#<token>` (and the same shape for unlock and verify). The\n * page reads the token from the fragment; this request never carries it, so nothing here reads one.\n */\nauthPublicProcessRouter.get('/reset-password', async (context, next) => {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/unlock-user', async (context, next) => {\n await context.render('auth-page', {\n pageElement: 'unlock-user',\n elementScript: '/auth/unlock-user.js',\n data: {\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/activate/:email', async (context, next) => {\n const { email } = context.params\n\n await context.render('auth-page', {\n pageElement: 'auth-activate',\n elementScript: '/auth/activate.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\n/*\n * Confirming an address is a POST from the page, not the GET of the link. The link used to be\n * `/auth/verify/<token>` and opening it confirmed — so did any mail scanner that fetched it, and the\n * token sat in every access log on the way.\n */\nauthPublicProcessRouter.get('/verify', async (context, next) => {\n await context.render('auth-page', {\n pageElement: 'auth-verify',\n elementScript: '/auth/verify.js',\n data: {\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.post('/verify', async (context, next) => {\n const { header, t } = context\n const { token } = context.request.body || {}\n\n let message: string\n\n try {\n await verify(token)\n message = t('text.user activated successfully')\n } catch (e) {\n context.status = 404\n message = t('text.invalid verification token')\n }\n\n context.body = message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n\n/*\n * This and forgot-password give one answer whether or not the address has an account to mail - a\n * different one would tell anyone which addresses are registered. Only a mail that was due and did\n * not go is a failure. Both used to set nothing in those cases, and koa answered a bare 404 that the\n * page showed as \"Not Found\".\n */\nauthPublicProcessRouter.post('/resend-verification-email', async (context, next) => {\n const { t } = context\n const { email } = context.request.body || {}\n\n if ((await resendVerificationEmail(email, context)) == 'failed') {\n context.status = 503\n context.body = t('error.mail could not be sent')\n return\n }\n\n context.status = 200\n context.body = t('text.verification email sent')\n})\n\n/*\n * ── The invitation page ─────────────────────────────────────────────────────\n * Public on purpose: the person following this link is not a member of the domain yet, which is\n * the entire point of an invitation. The old link went to `/auth/accept/<token>`, a path with no\n * route on any router, so it fell through to the private history-fallback — and the domain gate\n * there turned the invitee away for not being a member. It asked somebody trying to join to\n * already have joined.\n *\n * ── Why the token is not in this path ───────────────────────────────────────\n * It rides in the link's fragment (`#<token>`), which browsers do not send. So the request line\n * here carries nothing, and the token stays out of access logs, proxy logs and `Referer`. The\n * page reads it from the fragment, clears it from the address bar, and posts it below.\n */\nauthPublicProcessRouter.get('/accept-invitation', async (context, next) => {\n await context.render('auth-page', {\n pageElement: 'auth-accept-invitation',\n elementScript: '/auth/accept-invitation.js',\n data: {\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\n/*\n * The address is asked for as well as the token. A forwarded mail is the one way the token can\n * still travel, and nothing about where it sits can prevent that — so holding the link is not\n * enough on its own.\n */\nauthPublicProcessRouter.post('/accept-invitation', async (context, next) => {\n const { token, email, name, password } = context.request.body || ({} as any)\n\n const { domain, created } = await acceptInvitation({ token, email, name, password, context })\n\n context.body = {\n subdomain: domain?.subdomain,\n created,\n redirectTo: `/auth/signin?redirect_to=${encodeURIComponent('/')}`\n }\n})\n\nauthPublicProcessRouter.post('/forgot-password', async (context, next) => {\n const { t } = context\n const { email } = context.request.body\n\n if (!email) return next()\n\n const userRepo = getRepository(User)\n const user = await userRepo.findOne({\n where: {\n email\n }\n })\n\n if (user && !(await sendPasswordResetEmail({ user, context }))) {\n context.status = 503\n context.body = t('error.mail could not be sent')\n return\n }\n\n context.status = 200\n context.body = t('text.password reset email sent')\n})\n\nauthPublicProcessRouter.post('/reset-password', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n try {\n if (!(token && password)) {\n let message = t('error.token or password is invalid')\n\n context.status = 404\n context.body = {\n message\n }\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n\n return\n }\n\n await resetPassword(token, password, context)\n\n var message = t('text.password changed successfully')\n context.body = message\n\n clearAccessTokenCookie(context)\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n } catch (e) {\n context.status = 404\n context.body = e.message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message: e.message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n }\n})\n\nauthPublicProcessRouter.post('/unlock-user', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n /* Both are required, the way `/auth/reset-password` requires them. This read `||`, so a body\n carrying a password and no token passed the guard and reached the unlock with no token —\n see the note in `unlockUser`. */\n if (!(token && password)) {\n context.status = 404\n context.body = t('error.token or password is invalid')\n\n return\n }\n\n const succeed = await unlockUser(token, password)\n\n /*\n * Say what happened. The success page used to render whether or not the unlock took, so a\n * reused link, a token for another account, or an account that was never locked all read as\n * \"account is reactivated\" — and the person then could not sign in.\n *\n * A refusal returns to the form with a reason, the shape `/auth/reset-password` already uses.\n */\n if (!succeed) {\n const message = t('text.invalid verification token')\n\n context.status = 404\n context.body = message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'unlock-user',\n elementScript: '/auth/unlock-user.js',\n data: {\n token,\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n\n return\n }\n\n context.body = t('text.password reset succeed')\n\n clearAccessTokenCookie(context)\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message: t('text.account is reactivated'),\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.siteRootRouter = void 0;
|
|
4
|
+
exports.findAuth = findAuth;
|
|
4
5
|
const tslib_1 = require("tslib");
|
|
5
6
|
const koa_router_1 = tslib_1.__importDefault(require("koa-router"));
|
|
6
7
|
const koa_passport_1 = tslib_1.__importDefault(require("koa-passport"));
|
|
@@ -10,6 +11,7 @@ const user_js_1 = require("../service/user/user.js");
|
|
|
10
11
|
const get_user_domains_js_1 = require("../utils/get-user-domains.js");
|
|
11
12
|
const PUBLIC_HOME_ROUTE = env_1.config.get('publicHomeRoute', '/public/home');
|
|
12
13
|
exports.siteRootRouter = new koa_router_1.default();
|
|
14
|
+
/** Who the access token says this is, when it says anyone - the route goes on either way. */
|
|
13
15
|
async function findAuth(context, next) {
|
|
14
16
|
return await koa_passport_1.default.authenticate('jwt', { session: false }, async (err, decoded, info) => {
|
|
15
17
|
if (decoded) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-root-router.js","sourceRoot":"","sources":["../../server/router/site-root-router.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"site-root-router.js","sourceRoot":"","sources":["../../server/router/site-root-router.ts"],"names":[],"mappings":";;;AAcA,4BAWC;;AAzBD,oEAA+B;AAC/B,wEAAmC;AAEnC,iDAAgE;AAChE,6CAA4C;AAE5C,qDAA8C;AAC9C,sEAA6D;AAE7D,MAAM,iBAAiB,GAAG,YAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAA;AAE1D,QAAA,cAAc,GAAG,IAAI,oBAAM,EAAE,CAAA;AAE1C,6FAA6F;AACtF,KAAK,UAAU,QAAQ,CAAC,OAAO,EAAE,IAAI;IAC1C,OAAO,MAAM,sBAAQ,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QACzF,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,cAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;gBAC1C,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAA;YAC3B,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC,CAAA,CAAC;QAChB,CAAC;QAED,MAAM,IAAI,EAAE,CAAA;IACd,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;AACnB,CAAC;AAED,sBAAc,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,wBAAgB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAC1E,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAEtC,MAAM,SAAS,GAAG,MAAM,EAAE,SAAS,CAAA;IAEnC,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;QACtB,MAAM,WAAW,GAAsB,MAAM,IAAA,oCAAc,EAAC,IAAI,CAAC,CAAA;QACjE,IAAI,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,IAAI,SAAS,CAAC,EAAE,CAAC;YACtE,OAAO,MAAM,IAAI,EAAE,CAAA;QACrB,CAAC;QAED,OAAO,OAAO,CAAC,QAAQ,CAAC,iBAAiB,SAAS,EAAE,CAAC,CAAA;IACvD,CAAC;IAED,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACvB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAA;QAEjC,OAAM;IACR,CAAC;IAED,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAA;AACrC,CAAC,CAAC,CAAA","sourcesContent":["import Router from 'koa-router'\nimport passport from 'koa-passport'\n\nimport { Domain, domainMiddleware } from '@things-factory/shell'\nimport { config } from '@things-factory/env'\n\nimport { User } from '../service/user/user.js'\nimport { getUserDomains } from '../utils/get-user-domains.js'\n\nconst PUBLIC_HOME_ROUTE = config.get('publicHomeRoute', '/public/home')\n\nexport const siteRootRouter = new Router()\n\n/** Who the access token says this is, when it says anyone - the route goes on either way. */\nexport async function findAuth(context, next) {\n return await passport.authenticate('jwt', { session: false }, async (err, decoded, info) => {\n if (decoded) {\n try {\n const user = await User.checkAuth(decoded)\n context.state.user = user\n } catch (e) {}\n }\n\n await next()\n })(context, next)\n}\n\nsiteRootRouter.get('/', findAuth, domainMiddleware, async (context, next) => {\n const { user, domain } = context.state\n\n const subdomain = domain?.subdomain\n\n if (user && subdomain) {\n const userDomains: Partial<Domain>[] = await getUserDomains(user)\n if (userDomains.find(userDomain => userDomain.subdomain == subdomain)) {\n return await next()\n }\n\n return context.redirect(`/auth/checkin/${subdomain}`)\n }\n\n if (user && !subdomain) {\n context.redirect('/auth/checkin')\n\n return\n }\n\n context.redirect(PUBLIC_HOME_ROUTE)\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
|
|
5
|
+
export declare const resolvers: (typeof AccessRequestQuery | typeof AccessRequestMutation)[];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Domain } from '@things-factory/shell';
|
|
2
|
+
import { User } from '../user/user.js';
|
|
3
|
+
/**
|
|
4
|
+
* One thing done, as the audit keeps it (ADR-0141 §3.2 · §3.3).
|
|
5
|
+
*
|
|
6
|
+
* `kind` says which: `door.check` (a `@privilege` judgement), `door.result` (what a passed door
|
|
7
|
+
* did), or an event a service or the audit subscriber wrote (`role.grant`, `auth.signin`, ...).
|
|
8
|
+
* Only the columns below exist, and `detail` takes only the keys its kind registered
|
|
9
|
+
* (`audit-kinds.ts`) - no business value, secret or request text has a place to go.
|
|
10
|
+
*
|
|
11
|
+
* Immutable like `AuditRequest`, and for the same reason without foreign keys.
|
|
12
|
+
*/
|
|
13
|
+
export declare class AuditAction {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
requestId?: string;
|
|
16
|
+
domain?: Domain;
|
|
17
|
+
domainId?: string;
|
|
18
|
+
actor?: User;
|
|
19
|
+
actorId?: string;
|
|
20
|
+
createdAt: Date;
|
|
21
|
+
kind: string;
|
|
22
|
+
door?: string;
|
|
23
|
+
decision?: string;
|
|
24
|
+
basis?: string;
|
|
25
|
+
outcome?: string;
|
|
26
|
+
code?: string;
|
|
27
|
+
count: number;
|
|
28
|
+
returned?: number;
|
|
29
|
+
targetKind?: string;
|
|
30
|
+
targetIds?: string[];
|
|
31
|
+
truncated: boolean;
|
|
32
|
+
targetRole?: string;
|
|
33
|
+
objectKind?: string;
|
|
34
|
+
objectId?: string;
|
|
35
|
+
detail?: Record<string, string | number | boolean | null>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuditAction = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const type_graphql_1 = require("type-graphql");
|
|
6
|
+
const typeorm_1 = require("typeorm");
|
|
7
|
+
const shell_1 = require("@things-factory/shell");
|
|
8
|
+
const user_js_1 = require("../user/user.js");
|
|
9
|
+
/**
|
|
10
|
+
* One thing done, as the audit keeps it (ADR-0141 §3.2 · §3.3).
|
|
11
|
+
*
|
|
12
|
+
* `kind` says which: `door.check` (a `@privilege` judgement), `door.result` (what a passed door
|
|
13
|
+
* did), or an event a service or the audit subscriber wrote (`role.grant`, `auth.signin`, ...).
|
|
14
|
+
* Only the columns below exist, and `detail` takes only the keys its kind registered
|
|
15
|
+
* (`audit-kinds.ts`) - no business value, secret or request text has a place to go.
|
|
16
|
+
*
|
|
17
|
+
* Immutable like `AuditRequest`, and for the same reason without foreign keys.
|
|
18
|
+
*/
|
|
19
|
+
let AuditAction = class AuditAction {
|
|
20
|
+
};
|
|
21
|
+
exports.AuditAction = AuditAction;
|
|
22
|
+
tslib_1.__decorate([
|
|
23
|
+
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
|
24
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.ID, { description: 'Unique identifier.' }),
|
|
25
|
+
tslib_1.__metadata("design:type", String)
|
|
26
|
+
], AuditAction.prototype, "id", void 0);
|
|
27
|
+
tslib_1.__decorate([
|
|
28
|
+
(0, typeorm_1.Column)({ nullable: true, length: 36 }),
|
|
29
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'The request this was done in. Empty for work outside any request.' }),
|
|
30
|
+
tslib_1.__metadata("design:type", String)
|
|
31
|
+
], AuditAction.prototype, "requestId", void 0);
|
|
32
|
+
tslib_1.__decorate([
|
|
33
|
+
(0, typeorm_1.ManyToOne)(type => shell_1.Domain, { nullable: true, createForeignKeyConstraints: false }),
|
|
34
|
+
(0, type_graphql_1.Field)(type => shell_1.Domain, { nullable: true, description: 'The domain it was done in.' }),
|
|
35
|
+
tslib_1.__metadata("design:type", shell_1.Domain)
|
|
36
|
+
], AuditAction.prototype, "domain", void 0);
|
|
37
|
+
tslib_1.__decorate([
|
|
38
|
+
(0, typeorm_1.RelationId)((action) => action.domain),
|
|
39
|
+
tslib_1.__metadata("design:type", String)
|
|
40
|
+
], AuditAction.prototype, "domainId", void 0);
|
|
41
|
+
tslib_1.__decorate([
|
|
42
|
+
(0, typeorm_1.ManyToOne)(type => user_js_1.User, { nullable: true, createForeignKeyConstraints: false }),
|
|
43
|
+
(0, type_graphql_1.Field)(type => user_js_1.User, { nullable: true, description: 'Who did it. Empty when nobody is known - a sign-in to an account that does not exist.' }),
|
|
44
|
+
tslib_1.__metadata("design:type", user_js_1.User)
|
|
45
|
+
], AuditAction.prototype, "actor", void 0);
|
|
46
|
+
tslib_1.__decorate([
|
|
47
|
+
(0, typeorm_1.RelationId)((action) => action.actor),
|
|
48
|
+
tslib_1.__metadata("design:type", String)
|
|
49
|
+
], AuditAction.prototype, "actorId", void 0);
|
|
50
|
+
tslib_1.__decorate([
|
|
51
|
+
(0, typeorm_1.CreateDateColumn)(),
|
|
52
|
+
(0, type_graphql_1.Field)({ description: 'When it was written.' }),
|
|
53
|
+
tslib_1.__metadata("design:type", Date)
|
|
54
|
+
], AuditAction.prototype, "createdAt", void 0);
|
|
55
|
+
tslib_1.__decorate([
|
|
56
|
+
(0, typeorm_1.Column)({ length: 64 }),
|
|
57
|
+
(0, type_graphql_1.Field)({ description: 'What kind of action: door.check, door.result, role.grant, auth.signin, ...' }),
|
|
58
|
+
tslib_1.__metadata("design:type", String)
|
|
59
|
+
], AuditAction.prototype, "kind", void 0);
|
|
60
|
+
tslib_1.__decorate([
|
|
61
|
+
(0, typeorm_1.Column)({ nullable: true, length: 128 }),
|
|
62
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'The door, as category:privilege, or owner / super for a door that asks ownership only.' }),
|
|
63
|
+
tslib_1.__metadata("design:type", String)
|
|
64
|
+
], AuditAction.prototype, "door", void 0);
|
|
65
|
+
tslib_1.__decorate([
|
|
66
|
+
(0, typeorm_1.Column)({ nullable: true, length: 16 }),
|
|
67
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'granted or denied - for a door check.' }),
|
|
68
|
+
tslib_1.__metadata("design:type", String)
|
|
69
|
+
], AuditAction.prototype, "decision", void 0);
|
|
70
|
+
tslib_1.__decorate([
|
|
71
|
+
(0, typeorm_1.Column)({ nullable: true, length: 32 }),
|
|
72
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Why: role, domain-owner, superuser when granted; role, address, license, ownership, unknown when denied.' }),
|
|
73
|
+
tslib_1.__metadata("design:type", String)
|
|
74
|
+
], AuditAction.prototype, "basis", void 0);
|
|
75
|
+
tslib_1.__decorate([
|
|
76
|
+
(0, typeorm_1.Column)({ nullable: true, length: 16 }),
|
|
77
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'ok or error - what was done after the door, or how an event ended.' }),
|
|
78
|
+
tslib_1.__metadata("design:type", String)
|
|
79
|
+
], AuditAction.prototype, "outcome", void 0);
|
|
80
|
+
tslib_1.__decorate([
|
|
81
|
+
(0, typeorm_1.Column)({ nullable: true, length: 64 }),
|
|
82
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'A fixed code for an error or a refusal. Never a message.' }),
|
|
83
|
+
tslib_1.__metadata("design:type", String)
|
|
84
|
+
], AuditAction.prototype, "code", void 0);
|
|
85
|
+
tslib_1.__decorate([
|
|
86
|
+
(0, typeorm_1.Column)({ type: 'int', default: 1 }),
|
|
87
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'How many times this happened in the request - equal checks are kept as one row.' }),
|
|
88
|
+
tslib_1.__metadata("design:type", Number)
|
|
89
|
+
], AuditAction.prototype, "count", void 0);
|
|
90
|
+
tslib_1.__decorate([
|
|
91
|
+
(0, typeorm_1.Column)({ type: 'int', nullable: true }),
|
|
92
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'How many items a list returned, summed over the request.' }),
|
|
93
|
+
tslib_1.__metadata("design:type", Number)
|
|
94
|
+
], AuditAction.prototype, "returned", void 0);
|
|
95
|
+
tslib_1.__decorate([
|
|
96
|
+
(0, typeorm_1.Column)({ nullable: true, length: 64 }),
|
|
97
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Kind of the target - User, Role, ...' }),
|
|
98
|
+
tslib_1.__metadata("design:type", String)
|
|
99
|
+
], AuditAction.prototype, "targetKind", void 0);
|
|
100
|
+
tslib_1.__decorate([
|
|
101
|
+
(0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
|
|
102
|
+
(0, type_graphql_1.Field)(type => [String], { nullable: true, description: 'Target ids, up to 100.' }),
|
|
103
|
+
tslib_1.__metadata("design:type", Array)
|
|
104
|
+
], AuditAction.prototype, "targetIds", void 0);
|
|
105
|
+
tslib_1.__decorate([
|
|
106
|
+
(0, typeorm_1.Column)({ default: false }),
|
|
107
|
+
(0, type_graphql_1.Field)({ description: 'Whether more targets than were kept.' }),
|
|
108
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
109
|
+
], AuditAction.prototype, "truncated", void 0);
|
|
110
|
+
tslib_1.__decorate([
|
|
111
|
+
(0, typeorm_1.Column)({ nullable: true, length: 16 }),
|
|
112
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'requested - named by the caller; handled - confirmed by the service.' }),
|
|
113
|
+
tslib_1.__metadata("design:type", String)
|
|
114
|
+
], AuditAction.prototype, "targetRole", void 0);
|
|
115
|
+
tslib_1.__decorate([
|
|
116
|
+
(0, typeorm_1.Column)({ nullable: true, length: 64 }),
|
|
117
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Kind of the second object - the Role granted, the Privilege added.' }),
|
|
118
|
+
tslib_1.__metadata("design:type", String)
|
|
119
|
+
], AuditAction.prototype, "objectKind", void 0);
|
|
120
|
+
tslib_1.__decorate([
|
|
121
|
+
(0, typeorm_1.Column)({ nullable: true, length: 128 }),
|
|
122
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Id or name of the second object.' }),
|
|
123
|
+
tslib_1.__metadata("design:type", String)
|
|
124
|
+
], AuditAction.prototype, "objectId", void 0);
|
|
125
|
+
tslib_1.__decorate([
|
|
126
|
+
(0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
|
|
127
|
+
(0, type_graphql_1.Field)(type => shell_1.ScalarObject, { nullable: true, description: 'Further facts, only the keys the kind registered.' }),
|
|
128
|
+
tslib_1.__metadata("design:type", Object)
|
|
129
|
+
], AuditAction.prototype, "detail", void 0);
|
|
130
|
+
exports.AuditAction = AuditAction = tslib_1.__decorate([
|
|
131
|
+
(0, typeorm_1.Entity)(),
|
|
132
|
+
(0, typeorm_1.Index)('ix_audit_action_0', (action) => [action.domain, action.createdAt]),
|
|
133
|
+
(0, typeorm_1.Index)('ix_audit_action_1', (action) => [action.actor, action.createdAt]),
|
|
134
|
+
(0, typeorm_1.Index)('ix_audit_action_2', (action) => [action.requestId]),
|
|
135
|
+
(0, type_graphql_1.ObjectType)({ description: 'One thing done, as the audit keeps it.' })
|
|
136
|
+
], AuditAction);
|
|
137
|
+
//# sourceMappingURL=audit-action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-action.js","sourceRoot":"","sources":["../../../server/service/audit/audit-action.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, ID, Int, ObjectType } from 'type-graphql'\nimport { Column, CreateDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId } from 'typeorm'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\n\nimport { User } from '../user/user.js'\n\n/**\n * One thing done, as the audit keeps it (ADR-0141 §3.2 · §3.3).\n *\n * `kind` says which: `door.check` (a `@privilege` judgement), `door.result` (what a passed door\n * did), or an event a service or the audit subscriber wrote (`role.grant`, `auth.signin`, ...).\n * Only the columns below exist, and `detail` takes only the keys its kind registered\n * (`audit-kinds.ts`) - no business value, secret or request text has a place to go.\n *\n * Immutable like `AuditRequest`, and for the same reason without foreign keys.\n */\n@Entity()\n@Index('ix_audit_action_0', (action: AuditAction) => [action.domain, action.createdAt])\n@Index('ix_audit_action_1', (action: AuditAction) => [action.actor, action.createdAt])\n@Index('ix_audit_action_2', (action: AuditAction) => [action.requestId])\n@ObjectType({ description: 'One thing done, as the audit keeps it.' })\nexport class AuditAction {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier.' })\n readonly id: string\n\n @Column({ nullable: true, length: 36 })\n @Field({ nullable: true, description: 'The request this was done in. Empty for work outside any request.' })\n requestId?: string\n\n @ManyToOne(type => Domain, { nullable: true, createForeignKeyConstraints: false })\n @Field(type => Domain, { nullable: true, description: 'The domain it was done in.' })\n domain?: Domain\n\n @RelationId((action: AuditAction) => action.domain)\n domainId?: string\n\n @ManyToOne(type => User, { nullable: true, createForeignKeyConstraints: false })\n @Field(type => User, { nullable: true, description: 'Who did it. Empty when nobody is known - a sign-in to an account that does not exist.' })\n actor?: User\n\n @RelationId((action: AuditAction) => action.actor)\n actorId?: string\n\n @CreateDateColumn()\n @Field({ description: 'When it was written.' })\n createdAt: Date\n\n @Column({ length: 64 })\n @Field({ description: 'What kind of action: door.check, door.result, role.grant, auth.signin, ...' })\n kind: string\n\n @Column({ nullable: true, length: 128 })\n @Field({ nullable: true, description: 'The door, as category:privilege, or owner / super for a door that asks ownership only.' })\n door?: string\n\n @Column({ nullable: true, length: 16 })\n @Field({ nullable: true, description: 'granted or denied - for a door check.' })\n decision?: string\n\n @Column({ nullable: true, length: 32 })\n @Field({ nullable: true, description: 'Why: role, domain-owner, superuser when granted; role, address, license, ownership, unknown when denied.' })\n basis?: string\n\n @Column({ nullable: true, length: 16 })\n @Field({ nullable: true, description: 'ok or error - what was done after the door, or how an event ended.' })\n outcome?: string\n\n @Column({ nullable: true, length: 64 })\n @Field({ nullable: true, description: 'A fixed code for an error or a refusal. Never a message.' })\n code?: string\n\n @Column({ type: 'int', default: 1 })\n @Field(type => Int, { description: 'How many times this happened in the request - equal checks are kept as one row.' })\n count: number\n\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'How many items a list returned, summed over the request.' })\n returned?: number\n\n @Column({ nullable: true, length: 64 })\n @Field({ nullable: true, description: 'Kind of the target - User, Role, ...' })\n targetKind?: string\n\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => [String], { nullable: true, description: 'Target ids, up to 100.' })\n targetIds?: string[]\n\n @Column({ default: false })\n @Field({ description: 'Whether more targets than were kept.' })\n truncated: boolean\n\n @Column({ nullable: true, length: 16 })\n @Field({ nullable: true, description: 'requested - named by the caller; handled - confirmed by the service.' })\n targetRole?: string\n\n @Column({ nullable: true, length: 64 })\n @Field({ nullable: true, description: 'Kind of the second object - the Role granted, the Privilege added.' })\n objectKind?: string\n\n @Column({ nullable: true, length: 128 })\n @Field({ nullable: true, description: 'Id or name of the second object.' })\n objectId?: string\n\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Further facts, only the keys the kind registered.' })\n detail?: Record<string, string | number | boolean | null>\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kinds of audit action, and the `detail` keys each may carry (ADR-0141 §3.4).
|
|
3
|
+
*
|
|
4
|
+
* The audit stores only fields it allows. The fixed columns of `AuditAction` hold no business
|
|
5
|
+
* value; `detail` is the one open place, so it takes only the keys its kind registered here, and
|
|
6
|
+
* a kind nobody registered is refused - a module that wants a new event says so here, at import,
|
|
7
|
+
* and a typo does not become a new kind quietly.
|
|
8
|
+
*
|
|
9
|
+
* A key holds a security state or a count, never a business value: a user's status (`activated` →
|
|
10
|
+
* `locked`) is the account's security state; a salary is not, and has no key to go in.
|
|
11
|
+
*/
|
|
12
|
+
export type AuditDetailValue = string | number | boolean | null;
|
|
13
|
+
export declare function registerAuditKind(kind: string, detail?: readonly string[]): void;
|
|
14
|
+
export declare function isAuditKind(kind: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* The detail as it may be stored: unknown kinds throw, keys the kind did not register throw, and
|
|
17
|
+
* values are cut to 64 characters. Throwing is right - this is a program writing the wrong thing.
|
|
18
|
+
*/
|
|
19
|
+
export declare function allowedDetail(kind: string, detail?: Record<string, AuditDetailValue>): Record<string, AuditDetailValue> | undefined;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The kinds of audit action, and the `detail` keys each may carry (ADR-0141 §3.4).
|
|
4
|
+
*
|
|
5
|
+
* The audit stores only fields it allows. The fixed columns of `AuditAction` hold no business
|
|
6
|
+
* value; `detail` is the one open place, so it takes only the keys its kind registered here, and
|
|
7
|
+
* a kind nobody registered is refused - a module that wants a new event says so here, at import,
|
|
8
|
+
* and a typo does not become a new kind quietly.
|
|
9
|
+
*
|
|
10
|
+
* A key holds a security state or a count, never a business value: a user's status (`activated` →
|
|
11
|
+
* `locked`) is the account's security state; a salary is not, and has no key to go in.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.registerAuditKind = registerAuditKind;
|
|
15
|
+
exports.isAuditKind = isAuditKind;
|
|
16
|
+
exports.allowedDetail = allowedDetail;
|
|
17
|
+
const KINDS = new Map();
|
|
18
|
+
function registerAuditKind(kind, detail = []) {
|
|
19
|
+
const known = KINDS.get(kind);
|
|
20
|
+
if (known && known.join() !== detail.join()) {
|
|
21
|
+
throw new Error(`Audit kind "${kind}" is registered twice with different detail keys.`);
|
|
22
|
+
}
|
|
23
|
+
KINDS.set(kind, detail);
|
|
24
|
+
}
|
|
25
|
+
function isAuditKind(kind) {
|
|
26
|
+
return KINDS.has(kind);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The detail as it may be stored: unknown kinds throw, keys the kind did not register throw, and
|
|
30
|
+
* values are cut to 64 characters. Throwing is right - this is a program writing the wrong thing.
|
|
31
|
+
*/
|
|
32
|
+
function allowedDetail(kind, detail) {
|
|
33
|
+
const keys = KINDS.get(kind);
|
|
34
|
+
if (!keys) {
|
|
35
|
+
throw new Error(`Audit kind "${kind}" is not registered. Register it with registerAuditKind() first.`);
|
|
36
|
+
}
|
|
37
|
+
if (!detail)
|
|
38
|
+
return undefined;
|
|
39
|
+
const kept = {};
|
|
40
|
+
for (const [key, value] of Object.entries(detail)) {
|
|
41
|
+
if (!keys.includes(key)) {
|
|
42
|
+
throw new Error(`Audit kind "${kind}" does not allow detail "${key}". Allowed: ${keys.join(', ') || 'none'}.`);
|
|
43
|
+
}
|
|
44
|
+
kept[key] = typeof value === 'string' ? value.slice(0, 64) : value;
|
|
45
|
+
}
|
|
46
|
+
return Object.keys(kept).length ? kept : undefined;
|
|
47
|
+
}
|
|
48
|
+
/* Doors */
|
|
49
|
+
registerAuditKind('door.check');
|
|
50
|
+
registerAuditKind('door.result');
|
|
51
|
+
/* Signing in and out (ADR-0141 §3.3). The reason of a failure is the error code column. */
|
|
52
|
+
registerAuditKind('auth.signin');
|
|
53
|
+
registerAuditKind('auth.checkin');
|
|
54
|
+
registerAuditKind('auth.signout');
|
|
55
|
+
registerAuditKind('password.change');
|
|
56
|
+
registerAuditKind('password.reset-by-admin');
|
|
57
|
+
registerAuditKind('credential.issue');
|
|
58
|
+
registerAuditKind('webauthn.register');
|
|
59
|
+
/* Who may do what */
|
|
60
|
+
registerAuditKind('role.grant');
|
|
61
|
+
registerAuditKind('role.revoke');
|
|
62
|
+
registerAuditKind('role.privilege.add');
|
|
63
|
+
registerAuditKind('role.privilege.remove');
|
|
64
|
+
registerAuditKind('role.delete');
|
|
65
|
+
registerAuditKind('user.status', ['from', 'to']);
|
|
66
|
+
registerAuditKind('domain.owner.add');
|
|
67
|
+
registerAuditKind('domain.owner.remove');
|
|
68
|
+
registerAuditKind('invitation.create');
|
|
69
|
+
registerAuditKind('invitation.status', ['from', 'to']);
|
|
70
|
+
//# sourceMappingURL=audit-kinds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-kinds.js","sourceRoot":"","sources":["../../../server/service/audit/audit-kinds.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The kinds of audit action, and the `detail` keys each may carry (ADR-0141 §3.4).\n *\n * The audit stores only fields it allows. The fixed columns of `AuditAction` hold no business\n * value; `detail` is the one open place, so it takes only the keys its kind registered here, and\n * a kind nobody registered is refused - a module that wants a new event says so here, at import,\n * and a typo does not become a new kind quietly.\n *\n * A key holds a security state or a count, never a business value: a user's status (`activated` →\n * `locked`) is the account's security state; a salary is not, and has no key to go in.\n */\n\nexport type AuditDetailValue = string | number | boolean | null\n\nconst KINDS = new Map<string, readonly string[]>()\n\nexport function registerAuditKind(kind: string, detail: readonly string[] = []) {\n const known = KINDS.get(kind)\n if (known && known.join() !== detail.join()) {\n throw new Error(`Audit kind \"${kind}\" is registered twice with different detail keys.`)\n }\n KINDS.set(kind, detail)\n}\n\nexport function isAuditKind(kind: string): boolean {\n return KINDS.has(kind)\n}\n\n/**\n * The detail as it may be stored: unknown kinds throw, keys the kind did not register throw, and\n * values are cut to 64 characters. Throwing is right - this is a program writing the wrong thing.\n */\nexport function allowedDetail(\n kind: string,\n detail?: Record<string, AuditDetailValue>\n): Record<string, AuditDetailValue> | undefined {\n const keys = KINDS.get(kind)\n if (!keys) {\n throw new Error(`Audit kind \"${kind}\" is not registered. Register it with registerAuditKind() first.`)\n }\n if (!detail) return undefined\n\n const kept: Record<string, AuditDetailValue> = {}\n for (const [key, value] of Object.entries(detail)) {\n if (!keys.includes(key)) {\n throw new Error(`Audit kind \"${kind}\" does not allow detail \"${key}\". Allowed: ${keys.join(', ') || 'none'}.`)\n }\n kept[key] = typeof value === 'string' ? value.slice(0, 64) : value\n }\n return Object.keys(kept).length ? kept : undefined\n}\n\n/* Doors */\nregisterAuditKind('door.check')\nregisterAuditKind('door.result')\n\n/* Signing in and out (ADR-0141 §3.3). The reason of a failure is the error code column. */\nregisterAuditKind('auth.signin')\nregisterAuditKind('auth.checkin')\nregisterAuditKind('auth.signout')\nregisterAuditKind('password.change')\nregisterAuditKind('password.reset-by-admin')\nregisterAuditKind('credential.issue')\nregisterAuditKind('webauthn.register')\n\n/* Who may do what */\nregisterAuditKind('role.grant')\nregisterAuditKind('role.revoke')\nregisterAuditKind('role.privilege.add')\nregisterAuditKind('role.privilege.remove')\nregisterAuditKind('role.delete')\nregisterAuditKind('user.status', ['from', 'to'])\nregisterAuditKind('domain.owner.add')\nregisterAuditKind('domain.owner.remove')\nregisterAuditKind('invitation.create')\nregisterAuditKind('invitation.status', ['from', 'to'])\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AuditAction } from './audit-action.js';
|
|
2
|
+
import { AuditRequest } from './audit-request.js';
|
|
3
|
+
import { AuditDoor, AuditHealth, AuditPerson, AuditRecordFilter, AuditRecordList } from './audit-types.js';
|
|
4
|
+
export declare class AuditQuery {
|
|
5
|
+
auditRecords(filter: AuditRecordFilter, context: ResolverContext): Promise<AuditRecordList>;
|
|
6
|
+
auditPeople(from: Date, to: Date, context: ResolverContext): Promise<AuditPerson[]>;
|
|
7
|
+
auditDoors(from: Date, to: Date, context: ResolverContext): Promise<AuditDoor[]>;
|
|
8
|
+
auditHealth(): Promise<AuditHealth>;
|
|
9
|
+
myAuditActions(kind: string, limit: number, context: ResolverContext): Promise<AuditAction[]>;
|
|
10
|
+
request(action: AuditAction): Promise<AuditRequest | null>;
|
|
11
|
+
}
|