@proteinjs/user-server 1.18.0 → 1.20.0
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/CHANGELOG.md +22 -0
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/generated/index.js +3 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/src/authorization/UserActivityStamp.d.ts +17 -15
- package/dist/src/authorization/UserActivityStamp.d.ts.map +1 -1
- package/dist/src/authorization/UserActivityStamp.js +21 -19
- package/dist/src/authorization/UserActivityStamp.js.map +1 -1
- package/dist/src/authorization/userCache.d.ts +9 -0
- package/dist/src/authorization/userCache.d.ts.map +1 -1
- package/dist/src/authorization/userCache.js +9 -9
- package/dist/src/authorization/userCache.js.map +1 -1
- package/dist/src/routes/DevBootstrapRoles.d.ts +21 -0
- package/dist/src/routes/DevBootstrapRoles.d.ts.map +1 -0
- package/dist/src/routes/DevBootstrapRoles.js +64 -0
- package/dist/src/routes/DevBootstrapRoles.js.map +1 -0
- package/dist/src/routes/devLogin.d.ts +11 -0
- package/dist/src/routes/devLogin.d.ts.map +1 -1
- package/dist/src/routes/devLogin.js +23 -3
- package/dist/src/routes/devLogin.js.map +1 -1
- package/dist/src/services/Roles.d.ts +29 -1
- package/dist/src/services/Roles.d.ts.map +1 -1
- package/dist/src/services/Roles.js +88 -14
- package/dist/src/services/Roles.js.map +1 -1
- package/dist/src/services/UserPresence.d.ts +16 -0
- package/dist/src/services/UserPresence.d.ts.map +1 -0
- package/dist/src/services/UserPresence.js +75 -0
- package/dist/src/services/UserPresence.js.map +1 -0
- package/dist/test/DevLogin.test.js +5 -2
- package/dist/test/DevLogin.test.js.map +1 -1
- package/dist/test/DevLoginBootstrapAdmin.test.js +2 -0
- package/dist/test/DevLoginBootstrapAdmin.test.js.map +1 -1
- package/dist/test/DevLoginBootstrapRoles.test.d.ts +2 -0
- package/dist/test/DevLoginBootstrapRoles.test.d.ts.map +1 -0
- package/dist/test/DevLoginBootstrapRoles.test.js +491 -0
- package/dist/test/DevLoginBootstrapRoles.test.js.map +1 -0
- package/dist/test/UserActivityStamp.integration.test.js +57 -90
- package/dist/test/UserActivityStamp.integration.test.js.map +1 -1
- package/dist/test/UserCacheDoesNotStampPresence.integration.test.d.ts +2 -0
- package/dist/test/UserCacheDoesNotStampPresence.integration.test.d.ts.map +1 -0
- package/dist/test/UserCacheDoesNotStampPresence.integration.test.js +117 -0
- package/dist/test/UserCacheDoesNotStampPresence.integration.test.js.map +1 -0
- package/generated/index.ts +3 -1
- package/package.json +3 -3
- package/src/authorization/UserActivityStamp.ts +22 -20
- package/src/authorization/userCache.ts +9 -9
- package/src/routes/DevBootstrapRoles.ts +50 -0
- package/src/routes/devLogin.ts +18 -0
- package/src/services/Roles.ts +61 -1
- package/src/services/UserPresence.ts +26 -0
- package/test/DevLogin.test.ts +5 -2
- package/test/DevLoginBootstrapAdmin.test.ts +2 -0
- package/test/DevLoginBootstrapRoles.test.ts +248 -0
- package/test/UserActivityStamp.integration.test.ts +38 -50
- package/test/UserCacheDoesNotStampPresence.integration.test.ts +44 -0
|
@@ -46,33 +46,35 @@ var logger_1 = require("@proteinjs/logger");
|
|
|
46
46
|
var user_1 = require("@proteinjs/user");
|
|
47
47
|
/**
|
|
48
48
|
* Writes the LAST-ACTIVITY presence stamp (`user_activity`, one row per user — see
|
|
49
|
-
* UserActivityTable's contract): "this
|
|
49
|
+
* UserActivityTable's contract): "this person gave a page input just now".
|
|
50
50
|
*
|
|
51
|
-
* The
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
51
|
+
* The INPUT keying lives at the call site — the UserPresence service is the only caller, and
|
|
52
|
+
* the page reports through it only from a real pointer / key / touch / wheel event. The
|
|
53
|
+
* per-request session build (userCache.create) does NOT call this: it runs for every request
|
|
54
|
+
* over a session cookie, and that transport is what an idle tab produces all day (polls, socket
|
|
55
|
+
* re-joins on reconnect, the reload a deploy pushes) — presence keyed on it read every user as
|
|
56
|
+
* "active today" (founder finding 2026-09-12). What this class owns is the ACCOUNT predicate:
|
|
57
|
+
* the guest identity and machine accounts (`machine` — the one owner of "is this a machine",
|
|
58
|
+
* founder ruling 2026-09-02) are refused, whatever door they arrive through.
|
|
57
59
|
*
|
|
58
60
|
* Write behavior mirrors DbSessionStore's touch: throttled per user (a presence fact consumed at
|
|
59
|
-
* day grain needs no finer cadence, and
|
|
60
|
-
* fail-open (a lost stamp is a few minutes of staleness, never a failed
|
|
61
|
-
* promise NEVER rejects), and race-tolerant (concurrent first stamps contend on the
|
|
62
|
-
* index; the loser's error is swallowed as debug).
|
|
61
|
+
* day grain needs no finer cadence, and the page already throttles its reports — this is the
|
|
62
|
+
* belt), fail-open (a lost stamp is a few minutes of staleness, never a failed call — the
|
|
63
|
+
* returned promise NEVER rejects), and race-tolerant (concurrent first stamps contend on the
|
|
64
|
+
* scope-unique index; the loser's error is swallowed as debug).
|
|
63
65
|
*/
|
|
64
66
|
var UserActivityStamp = /** @class */ (function () {
|
|
65
67
|
function UserActivityStamp() {
|
|
66
68
|
this.logger = new logger_1.Logger({ name: this.constructor.name });
|
|
67
69
|
}
|
|
68
70
|
/**
|
|
69
|
-
* Record that `user`
|
|
70
|
-
*
|
|
71
|
+
* Record that `user` gave a page human input. Fire-and-forget safe: errors are handled (and
|
|
72
|
+
* logged) here, so callers may `void` the returned promise.
|
|
71
73
|
*/
|
|
72
|
-
UserActivityStamp.prototype.
|
|
74
|
+
UserActivityStamp.prototype.recordHumanInput = function (user) {
|
|
73
75
|
var _this = this;
|
|
74
76
|
var _a;
|
|
75
|
-
if (!user.id || user.
|
|
77
|
+
if (!user.id || user.id === user_1.guestUser.id || user.machine === true) {
|
|
76
78
|
return Promise.resolve();
|
|
77
79
|
}
|
|
78
80
|
var last = (_a = UserActivityStamp.lastStampMs.get(user.id)) !== null && _a !== void 0 ? _a : 0;
|
|
@@ -85,9 +87,9 @@ var UserActivityStamp = /** @class */ (function () {
|
|
|
85
87
|
UserActivityStamp.lastStampMs.clear(); // bounded memory; worst case is one extra stamp per user
|
|
86
88
|
}
|
|
87
89
|
return this.upsert(user.id).catch(function (error) {
|
|
88
|
-
// Contention (a concurrent
|
|
89
|
-
// failures land here alike; both are harmless to the
|
|
90
|
-
//
|
|
90
|
+
// Contention (a concurrent report stamped first, racing the scope-unique index) and real
|
|
91
|
+
// failures land here alike; both are harmless to the caller. Un-throttle so the next
|
|
92
|
+
// report retries instead of waiting out a full interval on a stamp that never landed.
|
|
91
93
|
UserActivityStamp.lastStampMs.delete(user.id);
|
|
92
94
|
_this.logger.error({ message: 'Failed to write user activity stamp', error: error });
|
|
93
95
|
});
|
|
@@ -118,7 +120,7 @@ var UserActivityStamp = /** @class */ (function () {
|
|
|
118
120
|
};
|
|
119
121
|
/** Stamp at most this often per user — same cadence class as DbSessionStore.TOUCH_INTERVAL. */
|
|
120
122
|
UserActivityStamp.STAMP_INTERVAL_MS = 1000 * 60 * 5;
|
|
121
|
-
/** Process-wide:
|
|
123
|
+
/** Process-wide: the throttle outlives any one service instance. */
|
|
122
124
|
UserActivityStamp.lastStampMs = new Map();
|
|
123
125
|
return UserActivityStamp;
|
|
124
126
|
}());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserActivityStamp.js","sourceRoot":"","sources":["../../../src/authorization/UserActivityStamp.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kDAA4B;AAC5B,oCAA8C;AAC9C,4CAA2C;AAC3C,
|
|
1
|
+
{"version":3,"file":"UserActivityStamp.js","sourceRoot":"","sources":["../../../src/authorization/UserActivityStamp.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kDAA4B;AAC5B,oCAA8C;AAC9C,4CAA2C;AAC3C,wCAAkF;AAElF;;;;;;;;;;;;;;;;;;GAkBG;AACH;IAAA;QAMU,WAAM,GAAG,IAAI,eAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IAqC/D,CAAC;IAnCC;;;OAGG;IACH,4CAAgB,GAAhB,UAAiB,IAAkC;QAAnD,iBAoBC;;QAnBC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,KAAK,gBAAS,CAAC,EAAE,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,EAAE;YACjE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;SAC1B;QACD,IAAM,IAAI,GAAG,MAAA,iBAAiB,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,mCAAI,CAAC,CAAC;QAC7D,IAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,GAAG,GAAG,IAAI,GAAG,iBAAiB,CAAC,iBAAiB,EAAE;YACpD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;SAC1B;QACD,iBAAiB,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAChD,IAAI,iBAAiB,CAAC,WAAW,CAAC,IAAI,GAAG,KAAK,EAAE;YAC9C,iBAAiB,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,yDAAyD;SACjG;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,UAAC,KAAK;YACtC,yFAAyF;YACzF,qFAAqF;YACrF,sFAAsF;YACtF,iBAAiB,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC9C,KAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,qCAAqC,EAAE,KAAK,OAAA,EAAE,CAAC,CAAC;QAC/E,CAAC,CAAC,CAAC;IACL,CAAC;IAEa,kCAAM,GAApB,UAAqB,MAAc;;;;;;wBAC3B,EAAE,GAAG,IAAA,kBAAa,GAAE,CAAC;wBACV,qBAAM,EAAE,CAAC,GAAG,CAAC,aAAM,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAA;;wBAA/D,QAAQ,GAAG,SAAoD;6BACjE,QAAQ,EAAR,wBAAQ;wBACV,qBAAM,EAAE,CAAC,MAAM,CAAC,aAAM,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,YAAY,EAAE,IAAA,gBAAM,GAAE,EAAkB,CAAC,EAAA;;wBAAjG,SAAiG,CAAC;;4BAElG,qBAAM,EAAE,CAAC,MAAM,CAAC,aAAM,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,IAAA,gBAAM,GAAE,EAAkB,CAAC,EAAA;;wBAA/F,SAA+F,CAAC;;;;;;KAEnG;IAzCD,+FAA+F;IACvE,mCAAiB,GAAG,IAAI,GAAG,EAAE,GAAG,CAAC,AAAhB,CAAiB;IAC1D,oEAAoE;IACrD,6BAAW,GAAG,IAAI,GAAG,EAAkB,AAA5B,CAA6B;IAuCzD,wBAAC;CAAA,AA3CD,IA2CC;AA3CY,8CAAiB"}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { SessionDataCache } from '@proteinjs/server-api';
|
|
2
2
|
import { User } from '@proteinjs/user';
|
|
3
|
+
/**
|
|
4
|
+
* The per-request session-cache build: resolves the session's email to the account row every
|
|
5
|
+
* request that rides a session cookie (wrapRoute), and every socket event (util-server's
|
|
6
|
+
* SocketSessionContext). It resolves IDENTITY only — it does not write the presence stamp
|
|
7
|
+
* (`user_activity`, UserActivityTable's contract): this build runs for an idle tab's polls, a
|
|
8
|
+
* socket's room re-joins on every reconnect, the reload a deploy pushes onto every open tab —
|
|
9
|
+
* transport, not a person. Presence is written from the page's human-input report alone
|
|
10
|
+
* (UserPresence.recordPresence).
|
|
11
|
+
*/
|
|
3
12
|
export declare const userCache: SessionDataCache<User>;
|
|
4
13
|
//# sourceMappingURL=userCache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userCache.d.ts","sourceRoot":"","sources":["../../../src/authorization/userCache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,EAAE,IAAI,EAA6C,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"userCache.d.ts","sourceRoot":"","sources":["../../../src/authorization/userCache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,EAAE,IAAI,EAA6C,MAAM,iBAAiB,CAAC;AAKlF;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,EAAE,gBAAgB,CAAC,IAAI,CA8C5C,CAAC"}
|
|
@@ -45,9 +45,16 @@ var db_1 = require("@proteinjs/db");
|
|
|
45
45
|
var logger_1 = require("@proteinjs/logger");
|
|
46
46
|
var user_1 = require("@proteinjs/user");
|
|
47
47
|
var DefaultAdminCredentials_1 = require("../authentication/DefaultAdminCredentials");
|
|
48
|
-
var UserActivityStamp_1 = require("./UserActivityStamp");
|
|
49
48
|
var logger = new logger_1.Logger({ name: 'userCache' });
|
|
50
|
-
|
|
49
|
+
/**
|
|
50
|
+
* The per-request session-cache build: resolves the session's email to the account row every
|
|
51
|
+
* request that rides a session cookie (wrapRoute), and every socket event (util-server's
|
|
52
|
+
* SocketSessionContext). It resolves IDENTITY only — it does not write the presence stamp
|
|
53
|
+
* (`user_activity`, UserActivityTable's contract): this build runs for an idle tab's polls, a
|
|
54
|
+
* socket's room re-joins on every reconnect, the reload a deploy pushes onto every open tab —
|
|
55
|
+
* transport, not a person. Presence is written from the page's human-input report alone
|
|
56
|
+
* (UserPresence.recordPresence).
|
|
57
|
+
*/
|
|
51
58
|
exports.userCache = {
|
|
52
59
|
key: user_1.USER_SESSION_CACHE_KEY,
|
|
53
60
|
create: function (sessionId, userEmail) { return __awaiter(void 0, void 0, void 0, function () {
|
|
@@ -86,13 +93,6 @@ exports.userCache = {
|
|
|
86
93
|
else if (accountUser) {
|
|
87
94
|
delete accountUser['password'];
|
|
88
95
|
user = accountUser;
|
|
89
|
-
// LAST ACTIVITY (human presence — UserActivityTable's contract): stamped HERE because
|
|
90
|
-
// this cache build runs exactly once per session-cookie request (wrapRoute), i.e. only
|
|
91
|
-
// for interactive transport. Background/seeded contexts (runInUserScope) set session
|
|
92
|
-
// data directly and never pass through, so machinery acting as the user structurally
|
|
93
|
-
// cannot stamp; the stamp itself refuses machine accounts. Fire-and-forget: the
|
|
94
|
-
// returned promise never rejects, and a request never waits on its own stamp.
|
|
95
|
-
void userActivityStamp.recordInteractiveRequest(user);
|
|
96
96
|
}
|
|
97
97
|
else {
|
|
98
98
|
// A session can outlive its account (row deleted, or a dev auto-login for a never-created
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userCache.js","sourceRoot":"","sources":["../../../src/authorization/userCache.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kDAA4B;AAE5B,oCAA8C;AAC9C,4CAA2C;AAC3C,wCAAkF;AAClF,qFAAoF;
|
|
1
|
+
{"version":3,"file":"userCache.js","sourceRoot":"","sources":["../../../src/authorization/userCache.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kDAA4B;AAE5B,oCAA8C;AAC9C,4CAA2C;AAC3C,wCAAkF;AAClF,qFAAoF;AAEpF,IAAM,MAAM,GAAG,IAAI,eAAM,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;AAEjD;;;;;;;;GAQG;AACU,QAAA,SAAS,GAA2B;IAC/C,GAAG,EAAE,6BAAsB;IAC3B,MAAM,EAAE,UAAO,SAAiB,EAAE,SAAiB;;;;;oBAC7C,IAAI,GAAG,gBAAS,CAAC;yBACjB,SAAS,EAAT,wBAAS;oBACL,gBAAgB,GAAG,iDAAuB,CAAC,cAAc,EAAE,CAAC;yBAC9D,CAAA,gBAAgB,IAAI,SAAS,IAAI,gBAAgB,CAAC,QAAQ,CAAA,EAA1D,wBAA0D;oBACtD,SAAS,GAAS;wBACtB,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE,gBAAgB,CAAC,QAAQ;wBAChC,QAAQ,EAAE,gBAAgB,CAAC,QAAQ;wBACnC,aAAa,EAAE,IAAI;wBACnB,KAAK,EAAE,CAAC,OAAO,CAAC;wBAChB,OAAO,EAAE,IAAA,gBAAM,GAAE;wBACjB,OAAO,EAAE,IAAA,gBAAM,GAAE;wBACjB,EAAE,EAAE,OAAO;qBACZ,CAAC;oBACF,IAAI,GAAG,SAAS,CAAC;;wBAEG,qBAAM,IAAA,kBAAa,GAAE,CAAC,GAAG,CAAC,aAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC,EAAA;;oBAAxF,WAAW,GAAG,SAA0E;oBAC9F,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,KAAK,aAAa,EAAE;wBACvD,sFAAsF;wBACtF,iFAAiF;wBACjF,4EAA4E;wBAC5E,MAAM,CAAC,IAAI,CAAC;4BACV,OAAO,EAAE,wEAAwE;4BACjF,GAAG,EAAE,EAAE,SAAS,WAAA,EAAE,SAAS,WAAA,EAAE;yBAC9B,CAAC,CAAC;qBACJ;yBAAM,IAAI,WAAW,EAAE;wBACtB,OAAQ,WAAmB,CAAC,UAAU,CAAC,CAAC;wBACxC,IAAI,GAAG,WAAW,CAAC;qBACpB;yBAAM;wBACL,0FAA0F;wBAC1F,+EAA+E;wBAC/E,sFAAsF;wBACtF,yDAAyD;wBACzD,MAAM,CAAC,IAAI,CAAC;4BACV,OAAO,EAAE,iFAAiF;4BAC1F,GAAG,EAAE,EAAE,SAAS,WAAA,EAAE,SAAS,WAAA,EAAE;yBAC9B,CAAC,CAAC;qBACJ;;wBAIL,sBAAO,IAAI,EAAC;;;SACb;CACF,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BootstrapRolesOutcome } from '../services/Roles';
|
|
2
|
+
/**
|
|
3
|
+
* The `DEV_BOOTSTRAP_ROLES` contract of the dev role-bootstrap door (routes/devLogin.ts):
|
|
4
|
+
*
|
|
5
|
+
* DEV_BOOTSTRAP_ROLES='email:role[,role];email:role…'
|
|
6
|
+
*
|
|
7
|
+
* Addresses and roles are trimmed; addresses are lowercased (the normalization every account
|
|
8
|
+
* email gets); a repeated address unions its roles; an entry with no `:`, no address or no role
|
|
9
|
+
* is ignored (malformed input is skipped, never fatal). The door grants the listed roles the
|
|
10
|
+
* account does not hold and logs ONE marker line per hit for a listed address —
|
|
11
|
+
* `[dev-bootstrap] <email>: granted a, b; held c; refused d (why)` — which tooling that provisions
|
|
12
|
+
* a development database can read back from the server log as proof of the grant.
|
|
13
|
+
*/
|
|
14
|
+
export declare class DevBootstrapRoles {
|
|
15
|
+
/** The roles listed for `email` in the process env (empty when the variable is unset or the address is not listed). */
|
|
16
|
+
static rolesFor(email: string): string[];
|
|
17
|
+
static parse(value: string | undefined): Map<string, string[]>;
|
|
18
|
+
/** The one marker line per hit — a log contract its readers parse; change the shape only together with them. */
|
|
19
|
+
static markerLine(email: string, outcome: BootstrapRolesOutcome): string;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=DevBootstrapRoles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevBootstrapRoles.d.ts","sourceRoot":"","sources":["../../../src/routes/DevBootstrapRoles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE/D;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAiB;IAC5B,uHAAuH;IACvH,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAIxC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAqB9D,gHAAgH;IAChH,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,GAAG,MAAM;CAOzE"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
3
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
4
|
+
if (ar || !(i in from)) {
|
|
5
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
6
|
+
ar[i] = from[i];
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.DevBootstrapRoles = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* The `DEV_BOOTSTRAP_ROLES` contract of the dev role-bootstrap door (routes/devLogin.ts):
|
|
15
|
+
*
|
|
16
|
+
* DEV_BOOTSTRAP_ROLES='email:role[,role];email:role…'
|
|
17
|
+
*
|
|
18
|
+
* Addresses and roles are trimmed; addresses are lowercased (the normalization every account
|
|
19
|
+
* email gets); a repeated address unions its roles; an entry with no `:`, no address or no role
|
|
20
|
+
* is ignored (malformed input is skipped, never fatal). The door grants the listed roles the
|
|
21
|
+
* account does not hold and logs ONE marker line per hit for a listed address —
|
|
22
|
+
* `[dev-bootstrap] <email>: granted a, b; held c; refused d (why)` — which tooling that provisions
|
|
23
|
+
* a development database can read back from the server log as proof of the grant.
|
|
24
|
+
*/
|
|
25
|
+
var DevBootstrapRoles = /** @class */ (function () {
|
|
26
|
+
function DevBootstrapRoles() {
|
|
27
|
+
}
|
|
28
|
+
/** The roles listed for `email` in the process env (empty when the variable is unset or the address is not listed). */
|
|
29
|
+
DevBootstrapRoles.rolesFor = function (email) {
|
|
30
|
+
var _a;
|
|
31
|
+
return (_a = DevBootstrapRoles.parse(process.env.DEV_BOOTSTRAP_ROLES).get(email.trim().toLowerCase())) !== null && _a !== void 0 ? _a : [];
|
|
32
|
+
};
|
|
33
|
+
DevBootstrapRoles.parse = function (value) {
|
|
34
|
+
var _a;
|
|
35
|
+
var entries = new Map();
|
|
36
|
+
for (var _i = 0, _b = (value !== null && value !== void 0 ? value : '').split(';'); _i < _b.length; _i++) {
|
|
37
|
+
var entry = _b[_i];
|
|
38
|
+
var colon = entry.indexOf(':');
|
|
39
|
+
if (colon < 0) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
var email = entry.slice(0, colon).trim().toLowerCase();
|
|
43
|
+
var roles = entry
|
|
44
|
+
.slice(colon + 1)
|
|
45
|
+
.split(',')
|
|
46
|
+
.map(function (role) { return role.trim(); })
|
|
47
|
+
.filter(Boolean);
|
|
48
|
+
if (!email || roles.length === 0) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
entries.set(email, Array.from(new Set(__spreadArray(__spreadArray([], ((_a = entries.get(email)) !== null && _a !== void 0 ? _a : []), true), roles, true))));
|
|
52
|
+
}
|
|
53
|
+
return entries;
|
|
54
|
+
};
|
|
55
|
+
/** The one marker line per hit — a log contract its readers parse; change the shape only together with them. */
|
|
56
|
+
DevBootstrapRoles.markerLine = function (email, outcome) {
|
|
57
|
+
var list = function (items) { return (items.length ? items.join(', ') : '(none)'); };
|
|
58
|
+
return ("[dev-bootstrap] ".concat(email, ": granted ").concat(list(outcome.granted), "; held ").concat(list(outcome.held), "; ") +
|
|
59
|
+
"refused ".concat(list(outcome.refused.map(function (refusal) { return "".concat(refusal.role, " (").concat(refusal.why, ")"); }))));
|
|
60
|
+
};
|
|
61
|
+
return DevBootstrapRoles;
|
|
62
|
+
}());
|
|
63
|
+
exports.DevBootstrapRoles = DevBootstrapRoles;
|
|
64
|
+
//# sourceMappingURL=DevBootstrapRoles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevBootstrapRoles.js","sourceRoot":"","sources":["../../../src/routes/DevBootstrapRoles.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA;;;;;;;;;;;GAWG;AACH;IAAA;IAmCA,CAAC;IAlCC,uHAAuH;IAChH,0BAAQ,GAAf,UAAgB,KAAa;;QAC3B,OAAO,MAAA,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,mCAAI,EAAE,CAAC;IACxG,CAAC;IAEM,uBAAK,GAAZ,UAAa,KAAyB;;QACpC,IAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;QAC5C,KAAoB,UAAwB,EAAxB,KAAA,CAAC,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAxB,cAAwB,EAAxB,IAAwB,EAAE;YAAzC,IAAM,KAAK,SAAA;YACd,IAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACjC,IAAI,KAAK,GAAG,CAAC,EAAE;gBACb,SAAS;aACV;YACD,IAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACzD,IAAM,KAAK,GAAG,KAAK;iBAChB,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;iBAChB,KAAK,CAAC,GAAG,CAAC;iBACV,GAAG,CAAC,UAAC,IAAI,IAAK,OAAA,IAAI,CAAC,IAAI,EAAE,EAAX,CAAW,CAAC;iBAC1B,MAAM,CAAC,OAAO,CAAC,CAAC;YACnB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;gBAChC,SAAS;aACV;YACD,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,iCAAK,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,mCAAI,EAAE,CAAC,SAAK,KAAK,QAAE,CAAC,CAAC,CAAC;SACpF;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,gHAAgH;IACzG,4BAAU,GAAjB,UAAkB,KAAa,EAAE,OAA8B;QAC7D,IAAM,IAAI,GAAG,UAAC,KAAe,IAAK,OAAA,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAA5C,CAA4C,CAAC;QAC/E,OAAO,CACL,0BAAmB,KAAK,uBAAa,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAU,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAI;YAC1F,kBAAW,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,UAAC,OAAO,IAAK,OAAA,UAAG,OAAO,CAAC,IAAI,eAAK,OAAO,CAAC,GAAG,MAAG,EAAlC,CAAkC,CAAC,CAAC,CAAE,CACxF,CAAC;IACJ,CAAC;IACH,wBAAC;AAAD,CAAC,AAnCD,IAmCC;AAnCY,8CAAiB"}
|
|
@@ -33,6 +33,17 @@ import { Route } from '@proteinjs/server-api';
|
|
|
33
33
|
* Test and prod never set it — the omission is the safety, the same idiom as the gates. The
|
|
34
34
|
* outcome is logged as ONE marker line, `Dev bootstrap admin door: <granted|admin-exists>`,
|
|
35
35
|
* which the n3xa compose-estate boot proof reads from the server log to PROVE the grant landed.
|
|
36
|
+
*
|
|
37
|
+
* Role-bootstrap door (`DEV_BOOTSTRAP_ROLES='email:role[,role];email:role…'`, the grammar in
|
|
38
|
+
* DevBootstrapRoles.ts): the first-admin door leaves every OTHER account role-less, and a
|
|
39
|
+
* consumer's admin-grant-only roles then need an admin's act on every fresh development database.
|
|
40
|
+
* Behind the same two gates, when the resolved address is listed, the listed roles the account
|
|
41
|
+
* does not hold are granted through `Roles.bootstrapRoles` — once each, audited, never revoking,
|
|
42
|
+
* never break-glass (refused and named, like a role the catalog does not know); the grant precedes
|
|
43
|
+
* the session so the first page load carries the roles. ONE marker line per hit for a listed
|
|
44
|
+
* address, `[dev-bootstrap] <email>: granted …; held …; refused …`, is what provisioning tooling
|
|
45
|
+
* reads back as proof. The variable absent = nothing changes; the gates closed = 404 regardless; a
|
|
46
|
+
* deployment outside development never sets it.
|
|
36
47
|
*/
|
|
37
48
|
export declare const devLogin: Route;
|
|
38
49
|
//# sourceMappingURL=devLogin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devLogin.d.ts","sourceRoot":"","sources":["../../../src/routes/devLogin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"devLogin.d.ts","sourceRoot":"","sources":["../../../src/routes/devLogin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAc9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,eAAO,MAAM,QAAQ,EAAE,KA4DtB,CAAC"}
|
|
@@ -42,6 +42,7 @@ var util_1 = require("@proteinjs/util");
|
|
|
42
42
|
var establishSession_1 = require("../authentication/establishSession");
|
|
43
43
|
var Roles_1 = require("../services/Roles");
|
|
44
44
|
var Signup_1 = require("../services/Signup");
|
|
45
|
+
var DevBootstrapRoles_1 = require("./DevBootstrapRoles");
|
|
45
46
|
var logger = new logger_1.Logger({ name: 'devLogin' });
|
|
46
47
|
/** Lowercased domain of an email address (the whole string when there is no `@`, which can never
|
|
47
48
|
* equal a real env domain — so malformed params fall out at the domain rail). */
|
|
@@ -80,12 +81,23 @@ var emailDomain = function (address) { return address.slice(address.lastIndexOf(
|
|
|
80
81
|
* Test and prod never set it — the omission is the safety, the same idiom as the gates. The
|
|
81
82
|
* outcome is logged as ONE marker line, `Dev bootstrap admin door: <granted|admin-exists>`,
|
|
82
83
|
* which the n3xa compose-estate boot proof reads from the server log to PROVE the grant landed.
|
|
84
|
+
*
|
|
85
|
+
* Role-bootstrap door (`DEV_BOOTSTRAP_ROLES='email:role[,role];email:role…'`, the grammar in
|
|
86
|
+
* DevBootstrapRoles.ts): the first-admin door leaves every OTHER account role-less, and a
|
|
87
|
+
* consumer's admin-grant-only roles then need an admin's act on every fresh development database.
|
|
88
|
+
* Behind the same two gates, when the resolved address is listed, the listed roles the account
|
|
89
|
+
* does not hold are granted through `Roles.bootstrapRoles` — once each, audited, never revoking,
|
|
90
|
+
* never break-glass (refused and named, like a role the catalog does not know); the grant precedes
|
|
91
|
+
* the session so the first page load carries the roles. ONE marker line per hit for a listed
|
|
92
|
+
* address, `[dev-bootstrap] <email>: granted …; held …; refused …`, is what provisioning tooling
|
|
93
|
+
* reads back as proof. The variable absent = nothing changes; the gates closed = 404 regardless; a
|
|
94
|
+
* deployment outside development never sets it.
|
|
83
95
|
*/
|
|
84
96
|
exports.devLogin = {
|
|
85
97
|
path: '/dev/login',
|
|
86
98
|
method: 'get',
|
|
87
99
|
onRequest: function (request, response) { return __awaiter(void 0, void 0, void 0, function () {
|
|
88
|
-
var envEmail, email, emailParam, requested, creation, bootstrapEmail, outcome;
|
|
100
|
+
var envEmail, email, emailParam, requested, creation, bootstrapEmail, outcome, bootstrapRoles, outcome;
|
|
89
101
|
var _a, _b, _c;
|
|
90
102
|
return __generator(this, function (_d) {
|
|
91
103
|
switch (_d.label) {
|
|
@@ -132,11 +144,19 @@ exports.devLogin = {
|
|
|
132
144
|
outcome = _d.sent();
|
|
133
145
|
logger.info({ message: "Dev bootstrap admin door: ".concat(outcome), obj: { email: email } });
|
|
134
146
|
_d.label = 3;
|
|
135
|
-
case 3:
|
|
147
|
+
case 3:
|
|
148
|
+
bootstrapRoles = DevBootstrapRoles_1.DevBootstrapRoles.rolesFor(email);
|
|
149
|
+
if (!(bootstrapRoles.length > 0)) return [3 /*break*/, 5];
|
|
150
|
+
return [4 /*yield*/, new Roles_1.Roles().bootstrapRoles(email, bootstrapRoles)];
|
|
151
|
+
case 4:
|
|
152
|
+
outcome = _d.sent();
|
|
153
|
+
logger.info({ message: DevBootstrapRoles_1.DevBootstrapRoles.markerLine(email, outcome) });
|
|
154
|
+
_d.label = 5;
|
|
155
|
+
case 5:
|
|
136
156
|
// establishSession commits the session row before the redirect — the redirected GET / must
|
|
137
157
|
// never read the store ahead of the write (observed: first /dev/login load landed on /login).
|
|
138
158
|
return [4 /*yield*/, (0, establishSession_1.establishSession)(request, email)];
|
|
139
|
-
case
|
|
159
|
+
case 6:
|
|
140
160
|
// establishSession commits the session row before the redirect — the redirected GET / must
|
|
141
161
|
// never read the store ahead of the write (observed: first /dev/login load landed on /login).
|
|
142
162
|
_d.sent();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devLogin.js","sourceRoot":"","sources":["../../../src/routes/devLogin.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,4CAA2C;AAC3C,wCAA6C;AAC7C,uEAAsE;AACtE,2CAA0C;AAC1C,6CAA4C;
|
|
1
|
+
{"version":3,"file":"devLogin.js","sourceRoot":"","sources":["../../../src/routes/devLogin.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,12 +8,22 @@ import { Service } from '@proteinjs/service';
|
|
|
8
8
|
*
|
|
9
9
|
* Break-glass roles are never granted through the service door — see `changeRole`; revoking one
|
|
10
10
|
* stays allowed. The ONE break-glass grant in code is `bootstrapAdmin`, the dev first-admin door
|
|
11
|
-
* (server-internal; reachable only through `/dev/login`'s gates, never over RPC).
|
|
11
|
+
* (server-internal; reachable only through `/dev/login`'s gates, never over RPC). Its sibling
|
|
12
|
+
* `bootstrapRoles` is the dev role-bootstrap door's grant — never break-glass, catalog-checked.
|
|
12
13
|
*
|
|
13
14
|
* Nobody edits their OWN roles: separating 'roles' from 'users' means nothing if the holder can
|
|
14
15
|
* simply grant themselves more, and a self-revoke is the mirror hazard (the last holder locking
|
|
15
16
|
* themselves out). Both directions are refused — see `changeRole`; ask another user manager.
|
|
16
17
|
*/
|
|
18
|
+
/** What one `bootstrapRoles` call did, per role: written now, already on the row, or refused with the reason. */
|
|
19
|
+
export type BootstrapRolesOutcome = {
|
|
20
|
+
granted: string[];
|
|
21
|
+
held: string[];
|
|
22
|
+
refused: {
|
|
23
|
+
role: string;
|
|
24
|
+
why: string;
|
|
25
|
+
}[];
|
|
26
|
+
};
|
|
17
27
|
export declare class Roles implements RolesService {
|
|
18
28
|
serviceMetadata: Service['serviceMetadata'];
|
|
19
29
|
grantRole(userId: string, role: string): Promise<void>;
|
|
@@ -33,6 +43,24 @@ export declare class Roles implements RolesService {
|
|
|
33
43
|
* in, and test/prod never set the variable.
|
|
34
44
|
*/
|
|
35
45
|
bootstrapAdmin(email: string): Promise<'granted' | 'admin-exists' | 'no-account'>;
|
|
46
|
+
/**
|
|
47
|
+
* The dev role-bootstrap door's grant — `/dev/login` honoring `DEV_BOOTSTRAP_ROLES`
|
|
48
|
+
* (routes/devLogin.ts, the grammar in routes/DevBootstrapRoles.ts). A fresh development
|
|
49
|
+
* database has one admin at most (the first-admin door) and every other account role-less, and
|
|
50
|
+
* a consumer's `adminGrantOnly` roles can be handed out by an admin only — a manual act on
|
|
51
|
+
* every fresh database. So this grants `roles` to `email`'s account, each
|
|
52
|
+
* once: a role the account holds is `held` (no write, no audit row — idempotent); nothing is
|
|
53
|
+
* ever revoked; a role the catalog does not know or a break-glass role is `refused` and named
|
|
54
|
+
* (`bootstrapAdmin` is the ONE break-glass path, with its own rail). Admin-grant-only roles ARE
|
|
55
|
+
* granted here — that is the door's point. Every grant is audited like any grant, actor = the
|
|
56
|
+
* account itself (the door acts for nobody else), the role update and its rows in one
|
|
57
|
+
* transaction.
|
|
58
|
+
*
|
|
59
|
+
* Server-internal, like `bootstrapAdmin`: absent from `RolesService`, never RPC-reachable; the
|
|
60
|
+
* caller's two gates (DEVELOPMENT + DEV_AUTO_LOGIN_EMAIL) are the only way in, and a deployment
|
|
61
|
+
* outside development never sets the variable.
|
|
62
|
+
*/
|
|
63
|
+
bootstrapRoles(email: string, roles: string[]): Promise<BootstrapRolesOutcome>;
|
|
36
64
|
private changeRole;
|
|
37
65
|
/** Whether any account carries the break-glass role — `UserAuth.hasRole`'s membership test, over every row. */
|
|
38
66
|
private adminExists;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Roles.d.ts","sourceRoot":"","sources":["../../../src/services/Roles.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAA8D,MAAM,iBAAiB,CAAC;AAE3G,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C
|
|
1
|
+
{"version":3,"file":"Roles.d.ts","sourceRoot":"","sources":["../../../src/services/Roles.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAA8D,MAAM,iBAAiB,CAAC;AAE3G,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C;;;;;;;;;;;;;;GAcG;AACH,iHAAiH;AACjH,MAAM,MAAM,qBAAqB,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAAC;AAEpH,qBAAa,KAAM,YAAW,YAAY;IACjC,eAAe,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAIhD;IAEI,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7D;;;;;;;;;;;;;OAaG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,cAAc,GAAG,YAAY,CAAC;IAqBvF;;;;;;;;;;;;;;;;OAgBG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC;YAuCtE,UAAU;IA8ExB,+GAA+G;YACjG,WAAW;CAK1B"}
|
|
@@ -49,20 +49,6 @@ exports.Roles = void 0;
|
|
|
49
49
|
var db_1 = require("@proteinjs/db");
|
|
50
50
|
var user_1 = require("@proteinjs/user");
|
|
51
51
|
var logger_1 = require("@proteinjs/logger");
|
|
52
|
-
/**
|
|
53
|
-
* The ONE write path for user roles (the `user.roles` column is service-protected — generic
|
|
54
|
-
* record writes cannot touch it). Grants and revokes are validated against the roles catalog and
|
|
55
|
-
* audited: the role update and its `role_grant_event` row (actor, target, role, action; `created`
|
|
56
|
-
* is the timestamp) commit in one transaction, so the trail cannot diverge from the grants.
|
|
57
|
-
*
|
|
58
|
-
* Break-glass roles are never granted through the service door — see `changeRole`; revoking one
|
|
59
|
-
* stays allowed. The ONE break-glass grant in code is `bootstrapAdmin`, the dev first-admin door
|
|
60
|
-
* (server-internal; reachable only through `/dev/login`'s gates, never over RPC).
|
|
61
|
-
*
|
|
62
|
-
* Nobody edits their OWN roles: separating 'roles' from 'users' means nothing if the holder can
|
|
63
|
-
* simply grant themselves more, and a self-revoke is the mirror hazard (the last holder locking
|
|
64
|
-
* themselves out). Both directions are refused — see `changeRole`; ask another user manager.
|
|
65
|
-
*/
|
|
66
52
|
var Roles = /** @class */ (function () {
|
|
67
53
|
function Roles() {
|
|
68
54
|
this.serviceMetadata = {
|
|
@@ -152,6 +138,94 @@ var Roles = /** @class */ (function () {
|
|
|
152
138
|
});
|
|
153
139
|
});
|
|
154
140
|
};
|
|
141
|
+
/**
|
|
142
|
+
* The dev role-bootstrap door's grant — `/dev/login` honoring `DEV_BOOTSTRAP_ROLES`
|
|
143
|
+
* (routes/devLogin.ts, the grammar in routes/DevBootstrapRoles.ts). A fresh development
|
|
144
|
+
* database has one admin at most (the first-admin door) and every other account role-less, and
|
|
145
|
+
* a consumer's `adminGrantOnly` roles can be handed out by an admin only — a manual act on
|
|
146
|
+
* every fresh database. So this grants `roles` to `email`'s account, each
|
|
147
|
+
* once: a role the account holds is `held` (no write, no audit row — idempotent); nothing is
|
|
148
|
+
* ever revoked; a role the catalog does not know or a break-glass role is `refused` and named
|
|
149
|
+
* (`bootstrapAdmin` is the ONE break-glass path, with its own rail). Admin-grant-only roles ARE
|
|
150
|
+
* granted here — that is the door's point. Every grant is audited like any grant, actor = the
|
|
151
|
+
* account itself (the door acts for nobody else), the role update and its rows in one
|
|
152
|
+
* transaction.
|
|
153
|
+
*
|
|
154
|
+
* Server-internal, like `bootstrapAdmin`: absent from `RolesService`, never RPC-reachable; the
|
|
155
|
+
* caller's two gates (DEVELOPMENT + DEV_AUTO_LOGIN_EMAIL) are the only way in, and a deployment
|
|
156
|
+
* outside development never sets the variable.
|
|
157
|
+
*/
|
|
158
|
+
Roles.prototype.bootstrapRoles = function (email, roles) {
|
|
159
|
+
var _a;
|
|
160
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
161
|
+
var logger, db, user, held, outcome, _i, _b, role, entry;
|
|
162
|
+
var _this = this;
|
|
163
|
+
return __generator(this, function (_c) {
|
|
164
|
+
switch (_c.label) {
|
|
165
|
+
case 0:
|
|
166
|
+
logger = new logger_1.Logger({ name: 'Roles.bootstrapRoles' });
|
|
167
|
+
db = (0, db_1.getDbAsSystem)();
|
|
168
|
+
return [4 /*yield*/, db.get(user_1.tables.User, { email: email.toLowerCase() })];
|
|
169
|
+
case 1:
|
|
170
|
+
user = _c.sent();
|
|
171
|
+
if (!user) {
|
|
172
|
+
throw new Error("bootstrapRoles: no account for ".concat(email, " \u2014 the door creates the account before it grants"));
|
|
173
|
+
}
|
|
174
|
+
held = (_a = user.roles) !== null && _a !== void 0 ? _a : [];
|
|
175
|
+
outcome = { granted: [], held: [], refused: [] };
|
|
176
|
+
for (_i = 0, _b = Array.from(new Set(roles)); _i < _b.length; _i++) {
|
|
177
|
+
role = _b[_i];
|
|
178
|
+
entry = user_1.RolesCatalog.getEntry(role);
|
|
179
|
+
if (!entry) {
|
|
180
|
+
outcome.refused.push({ role: role, why: 'unknown role' });
|
|
181
|
+
}
|
|
182
|
+
else if (entry.breakGlass) {
|
|
183
|
+
outcome.refused.push({ role: role, why: 'break-glass' });
|
|
184
|
+
}
|
|
185
|
+
else if (held.includes(role)) {
|
|
186
|
+
outcome.held.push(role);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
outcome.granted.push(role);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (outcome.granted.length === 0) {
|
|
193
|
+
return [2 /*return*/, outcome];
|
|
194
|
+
}
|
|
195
|
+
return [4 /*yield*/, db.runTransaction(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
196
|
+
var _i, _a, role;
|
|
197
|
+
return __generator(this, function (_b) {
|
|
198
|
+
switch (_b.label) {
|
|
199
|
+
case 0: return [4 /*yield*/, db.update(user_1.tables.User, { id: user.id, roles: __spreadArray(__spreadArray([], held, true), outcome.granted, true) })];
|
|
200
|
+
case 1:
|
|
201
|
+
_b.sent();
|
|
202
|
+
_i = 0, _a = outcome.granted;
|
|
203
|
+
_b.label = 2;
|
|
204
|
+
case 2:
|
|
205
|
+
if (!(_i < _a.length)) return [3 /*break*/, 5];
|
|
206
|
+
role = _a[_i];
|
|
207
|
+
return [4 /*yield*/, db.insert(user_1.tables.RoleGrantEvent, { actor: user.id, target: user.id, role: role, action: 'grant' })];
|
|
208
|
+
case 3:
|
|
209
|
+
_b.sent();
|
|
210
|
+
_b.label = 4;
|
|
211
|
+
case 4:
|
|
212
|
+
_i++;
|
|
213
|
+
return [3 /*break*/, 2];
|
|
214
|
+
case 5: return [2 /*return*/];
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}); })];
|
|
218
|
+
case 2:
|
|
219
|
+
_c.sent();
|
|
220
|
+
logger.info({
|
|
221
|
+
message: 'Roles granted by the dev role-bootstrap door',
|
|
222
|
+
obj: { target: user.id, email: email, granted: outcome.granted },
|
|
223
|
+
});
|
|
224
|
+
return [2 /*return*/, outcome];
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
};
|
|
155
229
|
Roles.prototype.changeRole = function (userId, role, action) {
|
|
156
230
|
var _a;
|
|
157
231
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Roles.js","sourceRoot":"","sources":["../../../src/services/Roles.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oCAAmE;AACnE,wCAA2G;AAC3G,4CAA2C;
|
|
1
|
+
{"version":3,"file":"Roles.js","sourceRoot":"","sources":["../../../src/services/Roles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Service } from '@proteinjs/service';
|
|
2
|
+
import { UserPresenceService } from '@proteinjs/user';
|
|
3
|
+
/**
|
|
4
|
+
* The server end of the human-input presence door (UserActivityTable's contract): the page
|
|
5
|
+
* reports "a person interacted just now" and this writes the `user_activity` stamp for the
|
|
6
|
+
* CALLING user — the one write path onto presence. Any signed-in user may report their own
|
|
7
|
+
* presence and nobody else's (the user is the session's, never an argument); the guest identity
|
|
8
|
+
* and machine accounts are refused inside the stamp. `doNotAwait`: the page never waits on its
|
|
9
|
+
* own stamp, and a lost stamp is minutes of staleness at day grain, never a failed call.
|
|
10
|
+
*/
|
|
11
|
+
export declare class UserPresence implements UserPresenceService {
|
|
12
|
+
serviceMetadata: Service['serviceMetadata'];
|
|
13
|
+
private stamp;
|
|
14
|
+
recordPresence(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=UserPresence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserPresence.d.ts","sourceRoot":"","sources":["../../../src/services/UserPresence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAuB,MAAM,iBAAiB,CAAC;AAG3E;;;;;;;GAOG;AACH,qBAAa,YAAa,YAAW,mBAAmB;IAC/C,eAAe,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAKhD;IAEF,OAAO,CAAC,KAAK,CAA2B;IAElC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;CAGtC"}
|