@spinajs/rbac 2.0.481 → 2.0.482
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/lib/cjs/actions.d.ts.map +1 -1
- package/lib/cjs/actions.js +32 -6
- package/lib/cjs/actions.js.map +1 -1
- package/lib/cjs/cli/ActivateUser.js +1 -1
- package/lib/cjs/cli/ActivateUser.js.map +1 -1
- package/lib/cjs/cli/BanUser.js +1 -1
- package/lib/cjs/cli/BanUser.js.map +1 -1
- package/lib/cjs/config/rbac.d.ts +11 -1
- package/lib/cjs/config/rbac.d.ts.map +1 -1
- package/lib/cjs/config/rbac.js +12 -3
- package/lib/cjs/config/rbac.js.map +1 -1
- package/lib/cjs/cron/DeactivatePasswords.d.ts.map +1 -1
- package/lib/cjs/cron/DeactivatePasswords.js +6 -2
- package/lib/cjs/cron/DeactivatePasswords.js.map +1 -1
- package/lib/cjs/index.d.ts +6 -0
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +6 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/interfaces.d.ts +63 -37
- package/lib/cjs/interfaces.d.ts.map +1 -1
- package/lib/cjs/interfaces.js +43 -1
- package/lib/cjs/interfaces.js.map +1 -1
- package/lib/cjs/middleware.d.ts +23 -1
- package/lib/cjs/middleware.d.ts.map +1 -1
- package/lib/cjs/middleware.js +158 -104
- package/lib/cjs/middleware.js.map +1 -1
- package/lib/cjs/models/User.d.ts.map +1 -1
- package/lib/cjs/models/User.js +15 -1
- package/lib/cjs/models/User.js.map +1 -1
- package/lib/cjs/ownership.d.ts +2 -0
- package/lib/cjs/ownership.d.ts.map +1 -0
- package/lib/cjs/ownership.js +46 -0
- package/lib/cjs/ownership.js.map +1 -0
- package/lib/cjs/session-codec.d.ts +15 -0
- package/lib/cjs/session-codec.d.ts.map +1 -0
- package/lib/cjs/session-codec.js +59 -0
- package/lib/cjs/session-codec.js.map +1 -0
- package/lib/cjs/session-expiration.d.ts +40 -0
- package/lib/cjs/session-expiration.d.ts.map +1 -0
- package/lib/cjs/session-expiration.js +93 -0
- package/lib/cjs/session-expiration.js.map +1 -0
- package/lib/cjs/session.d.ts +38 -18
- package/lib/cjs/session.d.ts.map +1 -1
- package/lib/cjs/session.js +91 -50
- package/lib/cjs/session.js.map +1 -1
- package/lib/mjs/actions.d.ts.map +1 -1
- package/lib/mjs/actions.js +32 -6
- package/lib/mjs/actions.js.map +1 -1
- package/lib/mjs/cli/ActivateUser.js +1 -1
- package/lib/mjs/cli/ActivateUser.js.map +1 -1
- package/lib/mjs/cli/BanUser.js +1 -1
- package/lib/mjs/cli/BanUser.js.map +1 -1
- package/lib/mjs/config/rbac.d.ts +11 -1
- package/lib/mjs/config/rbac.d.ts.map +1 -1
- package/lib/mjs/config/rbac.js +12 -3
- package/lib/mjs/config/rbac.js.map +1 -1
- package/lib/mjs/cron/DeactivatePasswords.d.ts.map +1 -1
- package/lib/mjs/cron/DeactivatePasswords.js +6 -2
- package/lib/mjs/cron/DeactivatePasswords.js.map +1 -1
- package/lib/mjs/index.d.ts +6 -0
- package/lib/mjs/index.d.ts.map +1 -1
- package/lib/mjs/index.js +6 -0
- package/lib/mjs/index.js.map +1 -1
- package/lib/mjs/interfaces.d.ts +63 -37
- package/lib/mjs/interfaces.d.ts.map +1 -1
- package/lib/mjs/interfaces.js +41 -0
- package/lib/mjs/interfaces.js.map +1 -1
- package/lib/mjs/middleware.d.ts +23 -1
- package/lib/mjs/middleware.d.ts.map +1 -1
- package/lib/mjs/middleware.js +158 -104
- package/lib/mjs/middleware.js.map +1 -1
- package/lib/mjs/models/User.d.ts.map +1 -1
- package/lib/mjs/models/User.js +15 -1
- package/lib/mjs/models/User.js.map +1 -1
- package/lib/mjs/ownership.d.ts +2 -0
- package/lib/mjs/ownership.d.ts.map +1 -0
- package/lib/mjs/ownership.js +44 -0
- package/lib/mjs/ownership.js.map +1 -0
- package/lib/mjs/session-codec.d.ts +15 -0
- package/lib/mjs/session-codec.d.ts.map +1 -0
- package/lib/mjs/session-codec.js +55 -0
- package/lib/mjs/session-codec.js.map +1 -0
- package/lib/mjs/session-expiration.d.ts +40 -0
- package/lib/mjs/session-expiration.d.ts.map +1 -0
- package/lib/mjs/session-expiration.js +90 -0
- package/lib/mjs/session-expiration.js.map +1 -0
- package/lib/mjs/session.d.ts +38 -18
- package/lib/mjs/session.d.ts.map +1 -1
- package/lib/mjs/session.js +89 -47
- package/lib/mjs/session.js.map +1 -1
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +15 -12
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.encodeSessionData = encodeSessionData;
|
|
4
|
+
exports.decodeSessionData = decodeSessionData;
|
|
5
|
+
const luxon_1 = require("luxon");
|
|
6
|
+
const util_1 = require("@spinajs/util");
|
|
7
|
+
/**
|
|
8
|
+
* Shared session-data serialization codec (fixes B5 — symmetric write/read).
|
|
9
|
+
*
|
|
10
|
+
* Persistent session stores (db / dynamodb / redis) use this pair so the
|
|
11
|
+
* `replacer` used on write and the `reviver` used on read are symmetric and
|
|
12
|
+
* structural types survive the round-trip:
|
|
13
|
+
* - `Map` / `Set` via the shared @spinajs/util replacer/reviver,
|
|
14
|
+
* - luxon `DateTime` via a tagged wrapper.
|
|
15
|
+
*
|
|
16
|
+
* The in-memory store keeps live objects and does not use this codec.
|
|
17
|
+
*/
|
|
18
|
+
const DATETIME_TAG = 'DateTime';
|
|
19
|
+
/**
|
|
20
|
+
* `JSON.stringify` replacer. Must read the RAW value from the holder (`this`)
|
|
21
|
+
* because luxon's `DateTime.prototype.toJSON` would otherwise stringify the
|
|
22
|
+
* DateTime to an ISO string before we can tag it.
|
|
23
|
+
*/
|
|
24
|
+
function sessionReplacer(key, value) {
|
|
25
|
+
const raw = this[key];
|
|
26
|
+
if (raw instanceof luxon_1.DateTime) {
|
|
27
|
+
return { dataType: DATETIME_TAG, value: raw.toISO() };
|
|
28
|
+
}
|
|
29
|
+
return (0, util_1.replacer)(key, value);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* `JSON.parse` reviver paired with {@link sessionReplacer}.
|
|
33
|
+
*/
|
|
34
|
+
function sessionReviver(key, value) {
|
|
35
|
+
if (value && typeof value === 'object' && value.dataType === DATETIME_TAG) {
|
|
36
|
+
return luxon_1.DateTime.fromISO(value.value);
|
|
37
|
+
}
|
|
38
|
+
return (0, util_1.reviver)(key, value);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Serializes a session `Data` map to a JSON string, preserving `Map` / `Set` /
|
|
42
|
+
* `DateTime` values.
|
|
43
|
+
*
|
|
44
|
+
* @param data - the session data map
|
|
45
|
+
*/
|
|
46
|
+
function encodeSessionData(data) {
|
|
47
|
+
return JSON.stringify({ dataType: 'Map', value: Array.from(data.entries()) }, sessionReplacer);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Restores a session `Data` map from a JSON string produced by
|
|
51
|
+
* {@link encodeSessionData}.
|
|
52
|
+
*
|
|
53
|
+
* @param json - the serialized session data
|
|
54
|
+
*/
|
|
55
|
+
function decodeSessionData(json) {
|
|
56
|
+
const parsed = JSON.parse(json, sessionReviver);
|
|
57
|
+
return parsed instanceof Map ? parsed : new Map();
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=session-codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-codec.js","sourceRoot":"","sources":["../../src/session-codec.ts"],"names":[],"mappings":";;AA8CA,8CAEC;AAQD,8CAGC;AA3DD,iCAAiC;AACjC,wCAAkG;AAElG;;;;;;;;;;GAUG;AAEH,MAAM,YAAY,GAAG,UAAU,CAAC;AAEhC;;;;GAIG;AACH,SAAS,eAAe,CAAgC,GAAW,EAAE,KAAc;IACjF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,GAAG,YAAY,gBAAQ,EAAE,CAAC;QAC5B,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC;IACxD,CAAC;IACD,OAAO,IAAA,eAAY,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,GAAW,EAAE,KAAgC;IACnE,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAK,KAAyB,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;QAC/F,OAAO,gBAAQ,CAAC,OAAO,CAAE,KAAyB,CAAC,KAAe,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,IAAA,cAAW,EAAC,GAAG,EAAE,KAAwB,CAAC,CAAC;AACpD,CAAC;AAED;;;;;GAKG;AACH,SAAgB,iBAAiB,CAAC,IAA0B;IAC1D,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,eAAe,CAAC,CAAC;AACjG,CAAC;AAED;;;;;GAKG;AACH,SAAgB,iBAAiB,CAAC,IAAY;IAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAChD,OAAO,MAAM,YAAY,GAAG,CAAC,CAAC,CAAE,MAA+B,CAAC,CAAC,CAAC,IAAI,GAAG,EAAmB,CAAC;AAC/F,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DateTime } from 'luxon';
|
|
2
|
+
import { ISession, SessionExpirationProvider } from './interfaces.js';
|
|
3
|
+
/**
|
|
4
|
+
* Absolute expiration: the session dies at a fixed instant from creation and is
|
|
5
|
+
* never extended. `touch` is therefore a no-op (renew returns the current
|
|
6
|
+
* Expiration unchanged).
|
|
7
|
+
*
|
|
8
|
+
* Settings: `rbac.session.expiration.ttl` (minutes).
|
|
9
|
+
*/
|
|
10
|
+
export declare class AbsoluteExpiration extends SessionExpirationProvider {
|
|
11
|
+
protected Ttl: number;
|
|
12
|
+
initial(session: ISession): DateTime | undefined;
|
|
13
|
+
renew(session: ISession): DateTime | undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Sliding expiration: every renewal pushes the expiration to `now + ttl`, with
|
|
17
|
+
* no upper bound. Activity keeps the session alive indefinitely.
|
|
18
|
+
*
|
|
19
|
+
* Settings: `rbac.session.expiration.ttl` (minutes).
|
|
20
|
+
*/
|
|
21
|
+
export declare class SlidingExpiration extends SessionExpirationProvider {
|
|
22
|
+
protected Ttl: number;
|
|
23
|
+
initial(_session: ISession): DateTime | undefined;
|
|
24
|
+
renew(_session: ISession): DateTime | undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Sliding expiration with an absolute cap: renewal pushes the expiration to
|
|
28
|
+
* `now + ttl` but never past `Creation + maxLifetime`. Combines idle timeout
|
|
29
|
+
* with a hard session lifetime ceiling.
|
|
30
|
+
*
|
|
31
|
+
* Settings: `rbac.session.expiration.ttl` and
|
|
32
|
+
* `rbac.session.expiration.maxLifetime` (both minutes).
|
|
33
|
+
*/
|
|
34
|
+
export declare class SlidingCappedExpiration extends SessionExpirationProvider {
|
|
35
|
+
protected Ttl: number;
|
|
36
|
+
protected MaxLifetime: number;
|
|
37
|
+
initial(_session: ISession): DateTime | undefined;
|
|
38
|
+
renew(session: ISession): DateTime | undefined;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=session-expiration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-expiration.d.ts","sourceRoot":"","sources":["../../src/session-expiration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGjC,OAAO,EAAE,QAAQ,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAEtE;;;;;;GAMG;AACH,qBACa,kBAAmB,SAAQ,yBAAyB;IAE/D,SAAS,CAAC,GAAG,EAAG,MAAM,CAAC;IAEhB,OAAO,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS;IAIhD,KAAK,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS;CAItD;AAED;;;;;GAKG;AACH,qBACa,iBAAkB,SAAQ,yBAAyB;IAE9D,SAAS,CAAC,GAAG,EAAG,MAAM,CAAC;IAEhB,OAAO,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS;IAIjD,KAAK,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS;CAGvD;AAED;;;;;;;GAOG;AACH,qBACa,uBAAwB,SAAQ,yBAAyB;IAEpE,SAAS,CAAC,GAAG,EAAG,MAAM,CAAC;IAGvB,SAAS,CAAC,WAAW,EAAG,MAAM,CAAC;IAExB,OAAO,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS;IAIjD,KAAK,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS;CAKtD"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SlidingCappedExpiration = exports.SlidingExpiration = exports.AbsoluteExpiration = void 0;
|
|
13
|
+
const luxon_1 = require("luxon");
|
|
14
|
+
const di_1 = require("@spinajs/di");
|
|
15
|
+
const configuration_1 = require("@spinajs/configuration");
|
|
16
|
+
const interfaces_js_1 = require("./interfaces.js");
|
|
17
|
+
/**
|
|
18
|
+
* Absolute expiration: the session dies at a fixed instant from creation and is
|
|
19
|
+
* never extended. `touch` is therefore a no-op (renew returns the current
|
|
20
|
+
* Expiration unchanged).
|
|
21
|
+
*
|
|
22
|
+
* Settings: `rbac.session.expiration.ttl` (minutes).
|
|
23
|
+
*/
|
|
24
|
+
let AbsoluteExpiration = class AbsoluteExpiration extends interfaces_js_1.SessionExpirationProvider {
|
|
25
|
+
initial(session) {
|
|
26
|
+
return session.Creation.plus({ minutes: this.Ttl });
|
|
27
|
+
}
|
|
28
|
+
renew(session) {
|
|
29
|
+
// no slide — keep whatever absolute instant was set at creation
|
|
30
|
+
return session.Expiration;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
exports.AbsoluteExpiration = AbsoluteExpiration;
|
|
34
|
+
__decorate([
|
|
35
|
+
(0, configuration_1.Config)('rbac.session.expiration.ttl', { defaultValue: 120 }),
|
|
36
|
+
__metadata("design:type", Number)
|
|
37
|
+
], AbsoluteExpiration.prototype, "Ttl", void 0);
|
|
38
|
+
exports.AbsoluteExpiration = AbsoluteExpiration = __decorate([
|
|
39
|
+
(0, di_1.Injectable)(interfaces_js_1.SessionExpirationProvider)
|
|
40
|
+
], AbsoluteExpiration);
|
|
41
|
+
/**
|
|
42
|
+
* Sliding expiration: every renewal pushes the expiration to `now + ttl`, with
|
|
43
|
+
* no upper bound. Activity keeps the session alive indefinitely.
|
|
44
|
+
*
|
|
45
|
+
* Settings: `rbac.session.expiration.ttl` (minutes).
|
|
46
|
+
*/
|
|
47
|
+
let SlidingExpiration = class SlidingExpiration extends interfaces_js_1.SessionExpirationProvider {
|
|
48
|
+
initial(_session) {
|
|
49
|
+
return luxon_1.DateTime.now().plus({ minutes: this.Ttl });
|
|
50
|
+
}
|
|
51
|
+
renew(_session) {
|
|
52
|
+
return luxon_1.DateTime.now().plus({ minutes: this.Ttl });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
exports.SlidingExpiration = SlidingExpiration;
|
|
56
|
+
__decorate([
|
|
57
|
+
(0, configuration_1.Config)('rbac.session.expiration.ttl', { defaultValue: 120 }),
|
|
58
|
+
__metadata("design:type", Number)
|
|
59
|
+
], SlidingExpiration.prototype, "Ttl", void 0);
|
|
60
|
+
exports.SlidingExpiration = SlidingExpiration = __decorate([
|
|
61
|
+
(0, di_1.Injectable)(interfaces_js_1.SessionExpirationProvider)
|
|
62
|
+
], SlidingExpiration);
|
|
63
|
+
/**
|
|
64
|
+
* Sliding expiration with an absolute cap: renewal pushes the expiration to
|
|
65
|
+
* `now + ttl` but never past `Creation + maxLifetime`. Combines idle timeout
|
|
66
|
+
* with a hard session lifetime ceiling.
|
|
67
|
+
*
|
|
68
|
+
* Settings: `rbac.session.expiration.ttl` and
|
|
69
|
+
* `rbac.session.expiration.maxLifetime` (both minutes).
|
|
70
|
+
*/
|
|
71
|
+
let SlidingCappedExpiration = class SlidingCappedExpiration extends interfaces_js_1.SessionExpirationProvider {
|
|
72
|
+
initial(_session) {
|
|
73
|
+
return luxon_1.DateTime.now().plus({ minutes: this.Ttl });
|
|
74
|
+
}
|
|
75
|
+
renew(session) {
|
|
76
|
+
const slide = luxon_1.DateTime.now().plus({ minutes: this.Ttl });
|
|
77
|
+
const cap = session.Creation.plus({ minutes: this.MaxLifetime });
|
|
78
|
+
return slide < cap ? slide : cap;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
exports.SlidingCappedExpiration = SlidingCappedExpiration;
|
|
82
|
+
__decorate([
|
|
83
|
+
(0, configuration_1.Config)('rbac.session.expiration.ttl', { defaultValue: 120 }),
|
|
84
|
+
__metadata("design:type", Number)
|
|
85
|
+
], SlidingCappedExpiration.prototype, "Ttl", void 0);
|
|
86
|
+
__decorate([
|
|
87
|
+
(0, configuration_1.Config)('rbac.session.expiration.maxLifetime', { defaultValue: 1440 }),
|
|
88
|
+
__metadata("design:type", Number)
|
|
89
|
+
], SlidingCappedExpiration.prototype, "MaxLifetime", void 0);
|
|
90
|
+
exports.SlidingCappedExpiration = SlidingCappedExpiration = __decorate([
|
|
91
|
+
(0, di_1.Injectable)(interfaces_js_1.SessionExpirationProvider)
|
|
92
|
+
], SlidingCappedExpiration);
|
|
93
|
+
//# sourceMappingURL=session-expiration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-expiration.js","sourceRoot":"","sources":["../../src/session-expiration.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,iCAAiC;AACjC,oCAAyC;AACzC,0DAAgD;AAChD,mDAAsE;AAEtE;;;;;;GAMG;AAEI,IAAM,kBAAkB,GAAxB,MAAM,kBAAmB,SAAQ,yCAAyB;IAIxD,OAAO,CAAC,OAAiB;QAC9B,OAAO,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAEM,KAAK,CAAC,OAAiB;QAC5B,gEAAgE;QAChE,OAAO,OAAO,CAAC,UAAU,CAAC;IAC5B,CAAC;CACF,CAAA;AAZY,gDAAkB;AAEnB;IADT,IAAA,sBAAM,EAAC,6BAA6B,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;;+CACtC;6BAFZ,kBAAkB;IAD9B,IAAA,eAAU,EAAC,yCAAyB,CAAC;GACzB,kBAAkB,CAY9B;AAED;;;;;GAKG;AAEI,IAAM,iBAAiB,GAAvB,MAAM,iBAAkB,SAAQ,yCAAyB;IAIvD,OAAO,CAAC,QAAkB;QAC/B,OAAO,gBAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACpD,CAAC;IAEM,KAAK,CAAC,QAAkB;QAC7B,OAAO,gBAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACpD,CAAC;CACF,CAAA;AAXY,8CAAiB;AAElB;IADT,IAAA,sBAAM,EAAC,6BAA6B,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;;8CACtC;4BAFZ,iBAAiB;IAD7B,IAAA,eAAU,EAAC,yCAAyB,CAAC;GACzB,iBAAiB,CAW7B;AAED;;;;;;;GAOG;AAEI,IAAM,uBAAuB,GAA7B,MAAM,uBAAwB,SAAQ,yCAAyB;IAO7D,OAAO,CAAC,QAAkB;QAC/B,OAAO,gBAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACpD,CAAC;IAEM,KAAK,CAAC,OAAiB;QAC5B,MAAM,KAAK,GAAG,gBAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACzD,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QACjE,OAAO,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;IACnC,CAAC;CACF,CAAA;AAhBY,0DAAuB;AAExB;IADT,IAAA,sBAAM,EAAC,6BAA6B,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;;oDACtC;AAGb;IADT,IAAA,sBAAM,EAAC,qCAAqC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;;4DACvC;kCALpB,uBAAuB;IADnC,IAAA,eAAU,EAAC,yCAAyB,CAAC;GACzB,uBAAuB,CAgBnC"}
|
package/lib/cjs/session.d.ts
CHANGED
|
@@ -1,40 +1,60 @@
|
|
|
1
1
|
import { DateTime } from 'luxon';
|
|
2
2
|
import { SessionProvider, ISession } from './interfaces.js';
|
|
3
|
-
import { User } from './models/User.js';
|
|
4
3
|
/**
|
|
5
4
|
* Session base class
|
|
6
5
|
*/
|
|
7
6
|
export declare class UserSession implements ISession {
|
|
8
|
-
protected SessionExpirationTime: number;
|
|
9
7
|
SessionId: string;
|
|
10
8
|
/**
|
|
11
|
-
*
|
|
9
|
+
* User id that owns this session. Single source of truth for ownership.
|
|
12
10
|
*/
|
|
13
|
-
|
|
14
|
-
Data: Map<string, unknown>;
|
|
11
|
+
UserId: number;
|
|
15
12
|
Creation: DateTime;
|
|
16
13
|
/**
|
|
17
|
-
*
|
|
14
|
+
* Expiration time for session, if undefined it does not expire.
|
|
15
|
+
* Set by the SessionProvider through the configured expiration strategy.
|
|
18
16
|
*/
|
|
19
|
-
|
|
17
|
+
Expiration: DateTime | undefined;
|
|
18
|
+
Data: Map<string, unknown>;
|
|
20
19
|
constructor(session?: Partial<ISession>);
|
|
21
|
-
/**
|
|
22
|
-
* Extends lifetime of session
|
|
23
|
-
*
|
|
24
|
-
* @param seconds - hom mutch to extend
|
|
25
|
-
*/
|
|
26
|
-
extend(seconds?: number): void;
|
|
27
20
|
}
|
|
28
21
|
/**
|
|
29
|
-
*
|
|
22
|
+
* Derives the `ssid` cookie `maxAge` (milliseconds) from a session's real
|
|
23
|
+
* `Expiration` (fixes B1 — the cookie previously always died at
|
|
24
|
+
* `expiration * 1000` ms i.e. ~2 minutes). Returns `undefined` for a
|
|
25
|
+
* never-expiring session (a session cookie), and never a negative value.
|
|
26
|
+
*
|
|
27
|
+
* @param session - the session whose expiry drives the cookie lifetime
|
|
28
|
+
*/
|
|
29
|
+
export declare function sessionCookieMaxAge(session: ISession): number | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Session-fixation protection helper. Mints a NEW session id, copies the
|
|
32
|
+
* ownership identity and data across, deletes the old id and persists the new
|
|
33
|
+
* one. Apply on privilege elevation (login already mints a fresh session, but
|
|
34
|
+
* 2FA-authorize and role-switch elevate an existing one).
|
|
35
|
+
*
|
|
36
|
+
* The returned session carries a new `SessionId` so the caller can reset the
|
|
37
|
+
* `ssid` cookie. `Expiration` is left unset so `save` schedules a fresh one via
|
|
38
|
+
* the configured strategy (`Creation` is preserved, so a capped lifetime window
|
|
39
|
+
* is not extended).
|
|
40
|
+
*
|
|
41
|
+
* @param provider - the active session store
|
|
42
|
+
* @param session - the session to regenerate
|
|
43
|
+
*/
|
|
44
|
+
export declare function regenerateSession(provider: SessionProvider, session: ISession): Promise<ISession>;
|
|
45
|
+
/**
|
|
46
|
+
* Simple session storage in memory, for testing or rapid prototyping.
|
|
47
|
+
* Keeps live session objects (no serialization).
|
|
30
48
|
*/
|
|
31
49
|
export declare class MemorySessionStore extends SessionProvider<ISession> {
|
|
32
50
|
protected Sessions: Map<string, ISession>;
|
|
33
|
-
truncate(): Promise<void>;
|
|
34
|
-
touch(session: ISession): Promise<void>;
|
|
35
51
|
restore(sessionId: string): Promise<ISession | null>;
|
|
52
|
+
save(session: ISession): Promise<void>;
|
|
53
|
+
touch(session: ISession): Promise<boolean>;
|
|
36
54
|
delete(sessionId: string): Promise<void>;
|
|
37
|
-
|
|
38
|
-
|
|
55
|
+
deleteByUser(userId: number): Promise<void>;
|
|
56
|
+
listByUser(userId: number): Promise<ISession[]>;
|
|
57
|
+
truncate(): Promise<void>;
|
|
58
|
+
private expirationEquals;
|
|
39
59
|
}
|
|
40
60
|
//# sourceMappingURL=session.d.ts.map
|
package/lib/cjs/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAI5D;;GAEG;AACH,qBACa,WAAY,YAAW,QAAQ;IACnC,SAAS,EAAE,MAAM,CAAY;IAEpC;;OAEG;IACI,MAAM,EAAE,MAAM,CAAC;IAEf,QAAQ,EAAE,QAAQ,CAAkB;IAE3C;;;OAGG;IACI,UAAU,EAAE,QAAQ,GAAG,SAAS,CAAa;IAE7C,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAa;gBAElC,OAAO,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC;CAKxC;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAKzE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAWvG;AAED;;;GAGG;AACH,qBACa,kBAAmB,SAAQ,eAAe,CAAC,QAAQ,CAAC;IAC/D,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAA+B;IAE3D,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAepD,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAWtC,KAAK,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAc1C,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAU/C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,OAAO,CAAC,gBAAgB;CAMzB"}
|
package/lib/cjs/session.js
CHANGED
|
@@ -8,102 +8,143 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
12
|
exports.MemorySessionStore = exports.UserSession = void 0;
|
|
13
|
+
exports.sessionCookieMaxAge = sessionCookieMaxAge;
|
|
14
|
+
exports.regenerateSession = regenerateSession;
|
|
16
15
|
const luxon_1 = require("luxon");
|
|
17
16
|
const interfaces_js_1 = require("./interfaces.js");
|
|
18
17
|
const di_1 = require("@spinajs/di");
|
|
19
|
-
const configuration_1 = require("@spinajs/configuration");
|
|
20
18
|
const uuid_1 = require("uuid");
|
|
21
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
22
19
|
/**
|
|
23
20
|
* Session base class
|
|
24
21
|
*/
|
|
25
22
|
let UserSession = class UserSession {
|
|
26
23
|
constructor(session) {
|
|
27
24
|
this.SessionId = (0, uuid_1.v4)();
|
|
25
|
+
this.Creation = luxon_1.DateTime.now();
|
|
28
26
|
/**
|
|
29
|
-
* Expiration time for session, if
|
|
27
|
+
* Expiration time for session, if undefined it does not expire.
|
|
28
|
+
* Set by the SessionProvider through the configured expiration strategy.
|
|
30
29
|
*/
|
|
31
30
|
this.Expiration = undefined;
|
|
32
31
|
this.Data = new Map();
|
|
33
|
-
this.Creation = luxon_1.DateTime.now();
|
|
34
32
|
if (session) {
|
|
35
33
|
Object.assign(this, session);
|
|
36
34
|
}
|
|
37
35
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Extends lifetime of session
|
|
40
|
-
*
|
|
41
|
-
* @param seconds - hom mutch to extend
|
|
42
|
-
*/
|
|
43
|
-
extend(seconds) {
|
|
44
|
-
this.Expiration = luxon_1.DateTime.now().plus({ seconds: seconds ? seconds : this.SessionExpirationTime });
|
|
45
|
-
}
|
|
46
36
|
};
|
|
47
37
|
exports.UserSession = UserSession;
|
|
48
|
-
__decorate([
|
|
49
|
-
(0, configuration_1.Config)('rbac.session.expiration'),
|
|
50
|
-
__metadata("design:type", Number)
|
|
51
|
-
], UserSession.prototype, "SessionExpirationTime", void 0);
|
|
52
38
|
exports.UserSession = UserSession = __decorate([
|
|
53
39
|
(0, di_1.NewInstance)(),
|
|
54
40
|
__metadata("design:paramtypes", [Object])
|
|
55
41
|
], UserSession);
|
|
56
42
|
/**
|
|
57
|
-
*
|
|
43
|
+
* Derives the `ssid` cookie `maxAge` (milliseconds) from a session's real
|
|
44
|
+
* `Expiration` (fixes B1 — the cookie previously always died at
|
|
45
|
+
* `expiration * 1000` ms i.e. ~2 minutes). Returns `undefined` for a
|
|
46
|
+
* never-expiring session (a session cookie), and never a negative value.
|
|
47
|
+
*
|
|
48
|
+
* @param session - the session whose expiry drives the cookie lifetime
|
|
49
|
+
*/
|
|
50
|
+
function sessionCookieMaxAge(session) {
|
|
51
|
+
if (!session.Expiration) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
return Math.max(0, Math.floor(session.Expiration.diff(luxon_1.DateTime.now()).milliseconds));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Session-fixation protection helper. Mints a NEW session id, copies the
|
|
58
|
+
* ownership identity and data across, deletes the old id and persists the new
|
|
59
|
+
* one. Apply on privilege elevation (login already mints a fresh session, but
|
|
60
|
+
* 2FA-authorize and role-switch elevate an existing one).
|
|
61
|
+
*
|
|
62
|
+
* The returned session carries a new `SessionId` so the caller can reset the
|
|
63
|
+
* `ssid` cookie. `Expiration` is left unset so `save` schedules a fresh one via
|
|
64
|
+
* the configured strategy (`Creation` is preserved, so a capped lifetime window
|
|
65
|
+
* is not extended).
|
|
66
|
+
*
|
|
67
|
+
* @param provider - the active session store
|
|
68
|
+
* @param session - the session to regenerate
|
|
69
|
+
*/
|
|
70
|
+
async function regenerateSession(provider, session) {
|
|
71
|
+
const regenerated = new UserSession({
|
|
72
|
+
UserId: session.UserId,
|
|
73
|
+
Creation: session.Creation,
|
|
74
|
+
Data: new Map(session.Data),
|
|
75
|
+
});
|
|
76
|
+
await provider.delete(session.SessionId);
|
|
77
|
+
await provider.save(regenerated);
|
|
78
|
+
return regenerated;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Simple session storage in memory, for testing or rapid prototyping.
|
|
82
|
+
* Keeps live session objects (no serialization).
|
|
58
83
|
*/
|
|
59
84
|
let MemorySessionStore = class MemorySessionStore extends interfaces_js_1.SessionProvider {
|
|
60
85
|
constructor() {
|
|
61
86
|
super(...arguments);
|
|
62
87
|
this.Sessions = new Map();
|
|
63
88
|
}
|
|
64
|
-
async truncate() {
|
|
65
|
-
this.Sessions = new Map();
|
|
66
|
-
}
|
|
67
|
-
async touch(session) {
|
|
68
|
-
return this.save(session);
|
|
69
|
-
}
|
|
70
89
|
async restore(sessionId) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (!session.Expiration || session.Expiration > luxon_1.DateTime.now()) {
|
|
74
|
-
return session;
|
|
75
|
-
}
|
|
90
|
+
const session = this.Sessions.get(sessionId);
|
|
91
|
+
if (!session) {
|
|
76
92
|
return null;
|
|
77
93
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
async delete(sessionId) {
|
|
81
|
-
if (this.Sessions.has(sessionId)) {
|
|
94
|
+
// an expired row is treated as absent (and evicted)
|
|
95
|
+
if (this.isExpired(session)) {
|
|
82
96
|
this.Sessions.delete(sessionId);
|
|
97
|
+
return null;
|
|
83
98
|
}
|
|
99
|
+
return session;
|
|
84
100
|
}
|
|
85
|
-
async save(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
async save(session) {
|
|
102
|
+
// A brand-new session without a scheduled expiration gets its initial
|
|
103
|
+
// expiration from the strategy. An already-scheduled expiration is
|
|
104
|
+
// persisted verbatim (fixes B3 — never recompute on every save).
|
|
105
|
+
if (session.Expiration === undefined) {
|
|
106
|
+
this.applyInitialExpiration(session);
|
|
91
107
|
}
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
this.Sessions.set(session.SessionId, session);
|
|
109
|
+
}
|
|
110
|
+
async touch(session) {
|
|
111
|
+
const current = session.Expiration;
|
|
112
|
+
const renewed = this.Expiration.renew(session);
|
|
113
|
+
// no change (e.g. AbsoluteExpiration) — skip the write, report false
|
|
114
|
+
if (this.expirationEquals(current, renewed)) {
|
|
115
|
+
return false;
|
|
94
116
|
}
|
|
117
|
+
session.Expiration = renewed;
|
|
118
|
+
this.Sessions.set(session.SessionId, session);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
async delete(sessionId) {
|
|
122
|
+
this.Sessions.delete(sessionId);
|
|
95
123
|
}
|
|
96
|
-
|
|
97
|
-
const sessionsToDelete = [];
|
|
124
|
+
async deleteByUser(userId) {
|
|
98
125
|
for (const [key, session] of this.Sessions.entries()) {
|
|
99
|
-
if (session.UserId ===
|
|
100
|
-
|
|
126
|
+
if (session.UserId === userId) {
|
|
127
|
+
this.Sessions.delete(key);
|
|
101
128
|
}
|
|
102
129
|
}
|
|
103
|
-
|
|
104
|
-
|
|
130
|
+
}
|
|
131
|
+
async listByUser(userId) {
|
|
132
|
+
const result = [];
|
|
133
|
+
for (const session of this.Sessions.values()) {
|
|
134
|
+
if (session.UserId === userId && !this.isExpired(session)) {
|
|
135
|
+
result.push(session);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
async truncate() {
|
|
141
|
+
this.Sessions = new Map();
|
|
142
|
+
}
|
|
143
|
+
expirationEquals(a, b) {
|
|
144
|
+
if (a === undefined || b === undefined) {
|
|
145
|
+
return a === b;
|
|
105
146
|
}
|
|
106
|
-
return
|
|
147
|
+
return a.toMillis() === b.toMillis();
|
|
107
148
|
}
|
|
108
149
|
};
|
|
109
150
|
exports.MemorySessionStore = MemorySessionStore;
|
package/lib/cjs/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/session.ts"],"names":[],"mappings":"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"}
|
package/lib/mjs/actions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/actions.ts"],"names":[],"mappings":"AAKA,OAAO,EAAwB,IAAI,EAAY,MAAM,kBAAkB,CAAC;AAGxE,OAAO,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/actions.ts"],"names":[],"mappings":"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"}
|
package/lib/mjs/actions.js
CHANGED
|
@@ -6,6 +6,8 @@ import { _ev } from '@spinajs/queue';
|
|
|
6
6
|
import { USER_COMMON_METADATA, User, UserBase } from './models/User.js';
|
|
7
7
|
import { _cfg, _service } from '@spinajs/configuration';
|
|
8
8
|
import { UserActivated, UserBanned, UserChanged, UserCreated, UserDeactivated, UserDeleted, UserLogged, UserPasswordChangeRequest, UserPasswordChanged, UserRoleGranted, UserRoleRevoked, UserUnbanned } from './events/index.js';
|
|
9
|
+
import { DI } from '@spinajs/di';
|
|
10
|
+
import { Configuration } from '@spinajs/configuration';
|
|
9
11
|
import { AuthProvider, PasswordProvider, PasswordValidationProvider } from './interfaces.js';
|
|
10
12
|
import { DateTime } from 'luxon';
|
|
11
13
|
import { ErrorCode } from '@spinajs/exceptions';
|
|
@@ -213,6 +215,20 @@ export async function activate(identifier) {
|
|
|
213
215
|
export async function deactivate(identifier) {
|
|
214
216
|
return _chain(_user(identifier), _user_update({ IsActive: false }), _user_ev(UserDeactivated), _user_email('deactivated'));
|
|
215
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Reads a create-middleware list from configuration.
|
|
220
|
+
*
|
|
221
|
+
* NOTE: we deliberately do NOT use `_cfg(path, [])` here. `_cfg` wraps its
|
|
222
|
+
* result in `_non_nil()`, which rejects empty arrays — so an unset or
|
|
223
|
+
* empty `beforeCreate` / `afterCreate` (the default in the shipped config)
|
|
224
|
+
* would throw and break user creation. Reading the value directly keeps an
|
|
225
|
+
* empty list as a valid "no middleware" result.
|
|
226
|
+
*/
|
|
227
|
+
function _create_middleware(path) {
|
|
228
|
+
const cfg = DI.get(Configuration);
|
|
229
|
+
const mw = cfg?.get(path, []);
|
|
230
|
+
return Array.isArray(mw) ? mw : [];
|
|
231
|
+
}
|
|
216
232
|
/**
|
|
217
233
|
* Creates a new user account.
|
|
218
234
|
*
|
|
@@ -248,11 +264,11 @@ export async function create(email, login, password, roles, id, metadata) {
|
|
|
248
264
|
Uuid: uuidv4(),
|
|
249
265
|
})),
|
|
250
266
|
// run before create middleware
|
|
251
|
-
(u) => _chain(
|
|
267
|
+
(u) => _chain(u, ..._create_middleware('rbac.actions.create.beforeCreate')),
|
|
252
268
|
// insert to db
|
|
253
269
|
_insert(), _either(() => metadata !== undefined, _set_user_meta(metadata ? Object.entries(metadata).map(([key, value]) => ({ key, value })) : []), async (u) => u),
|
|
254
270
|
// run after create middleware
|
|
255
|
-
(u) => _chain(
|
|
271
|
+
(u) => _chain(u, ..._create_middleware('rbac.actions.create.afterCreate')),
|
|
256
272
|
// send event
|
|
257
273
|
_user_ev(UserCreated, (u) => u.toJSON()),
|
|
258
274
|
// send email
|
|
@@ -327,11 +343,21 @@ export async function ban(identifier, reason, duration) {
|
|
|
327
343
|
* @returns
|
|
328
344
|
*/
|
|
329
345
|
export async function unban(identifier) {
|
|
330
|
-
return _chain(_user(identifier),
|
|
346
|
+
return _chain(_user(identifier),
|
|
347
|
+
// guard must return the user so the chain can keep flowing it downstream
|
|
348
|
+
_tap(async (u) => {
|
|
331
349
|
if (!u.Metadata[USER_COMMON_METADATA.USER_BAN_IS_BANNED]) {
|
|
332
350
|
throw new ErrorCode(E_CODES.E_USER_BANNED, `User is already unbanned`, { user: u });
|
|
333
351
|
}
|
|
334
|
-
}
|
|
352
|
+
}),
|
|
353
|
+
// actually remove the ban metadata from the DB. Assigning a regex-like
|
|
354
|
+
// string key never cleared anything; delete() removes each key from store.
|
|
355
|
+
_tap(async (u) => {
|
|
356
|
+
await u.Metadata.delete(USER_COMMON_METADATA.USER_BAN_IS_BANNED);
|
|
357
|
+
await u.Metadata.delete(USER_COMMON_METADATA.USER_BAN_START_DATE);
|
|
358
|
+
await u.Metadata.delete(USER_COMMON_METADATA.USER_BAN_DURATION);
|
|
359
|
+
await u.Metadata.delete(USER_COMMON_METADATA.USER_BAN_REASON);
|
|
360
|
+
}), _user_ev(UserUnbanned));
|
|
335
361
|
}
|
|
336
362
|
/**
|
|
337
363
|
* Initiates a password-change request for a user.
|
|
@@ -341,7 +367,7 @@ export async function unban(identifier) {
|
|
|
341
367
|
* @param identifier - numeric id, uuid / email / login string, or an existing {@link User} instance
|
|
342
368
|
*/
|
|
343
369
|
export async function passwordChangeRequest(identifier) {
|
|
344
|
-
const pwdWaitTime = await _cfg('rbac.password.
|
|
370
|
+
const pwdWaitTime = await _cfg('rbac.password.passwordResetWaitTime')();
|
|
345
371
|
return _chain(_user(identifier), _set_user_meta([
|
|
346
372
|
{ key: USER_COMMON_METADATA.USER_PWD_RESET_START_DATE, value: DateTime.now() },
|
|
347
373
|
{ key: USER_COMMON_METADATA.USER_PWD_RESET_TOKEN, value: uuidv4() },
|
|
@@ -358,7 +384,7 @@ export async function passwordChangeRequest(identifier) {
|
|
|
358
384
|
*/
|
|
359
385
|
export async function confirmPasswordReset(identifier, newPassword, token) {
|
|
360
386
|
return _chain(_user(identifier), _tap((u) => _chain(u, _zip(_get_user_meta(USER_COMMON_METADATA.USER_PWD_RESET_START_DATE), _get_user_meta(USER_COMMON_METADATA.USER_PWD_RESET_WAIT_TIME)), ([dueDate, waitTime]) => {
|
|
361
|
-
if (dueDate.plus(waitTime) < DateTime.now()) {
|
|
387
|
+
if (dueDate.plus({ seconds: waitTime }) < DateTime.now()) {
|
|
362
388
|
throw new ErrorCode(E_CODES.E_TOKEN_EXPIRED, `Password change token expired, token expiration date is: ${dueDate.toISO()}`, {
|
|
363
389
|
dueDate,
|
|
364
390
|
waitTime,
|