@furystack/rest-service 11.0.6 → 12.0.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 +74 -0
- package/README.md +3 -2
- package/esm/authenticate.d.ts.map +1 -1
- package/esm/authenticate.js +4 -1
- package/esm/authenticate.js.map +1 -1
- package/esm/authenticate.spec.js +6 -4
- package/esm/authenticate.spec.js.map +1 -1
- package/esm/authentication-providers/authentication-provider.d.ts +25 -0
- package/esm/authentication-providers/authentication-provider.d.ts.map +1 -0
- package/esm/authentication-providers/authentication-provider.js +2 -0
- package/esm/authentication-providers/authentication-provider.js.map +1 -0
- package/esm/authentication-providers/basic-auth-provider.d.ts +8 -0
- package/esm/authentication-providers/basic-auth-provider.d.ts.map +1 -0
- package/esm/authentication-providers/basic-auth-provider.js +18 -0
- package/esm/authentication-providers/basic-auth-provider.js.map +1 -0
- package/esm/authentication-providers/cookie-auth-provider.d.ts +11 -0
- package/esm/authentication-providers/cookie-auth-provider.d.ts.map +1 -0
- package/esm/authentication-providers/cookie-auth-provider.js +21 -0
- package/esm/authentication-providers/cookie-auth-provider.js.map +1 -0
- package/esm/authentication-providers/helpers.d.ts +11 -0
- package/esm/authentication-providers/helpers.d.ts.map +1 -0
- package/esm/authentication-providers/helpers.js +47 -0
- package/esm/authentication-providers/helpers.js.map +1 -0
- package/esm/authentication-providers/index.d.ts +5 -0
- package/esm/authentication-providers/index.d.ts.map +1 -0
- package/esm/authentication-providers/index.js +5 -0
- package/esm/authentication-providers/index.js.map +1 -0
- package/esm/endpoint-generators/utils.d.ts.map +1 -1
- package/esm/endpoint-generators/utils.js +4 -1
- package/esm/endpoint-generators/utils.js.map +1 -1
- package/esm/helpers.d.ts +5 -2
- package/esm/helpers.d.ts.map +1 -1
- package/esm/helpers.js +27 -3
- package/esm/helpers.js.map +1 -1
- package/esm/helpers.spec.js +37 -0
- package/esm/helpers.spec.js.map +1 -1
- package/esm/http-authentication-settings.d.ts +11 -4
- package/esm/http-authentication-settings.d.ts.map +1 -1
- package/esm/http-authentication-settings.js +9 -2
- package/esm/http-authentication-settings.js.map +1 -1
- package/esm/http-user-context.d.ts +9 -4
- package/esm/http-user-context.d.ts.map +1 -1
- package/esm/http-user-context.js +28 -55
- package/esm/http-user-context.js.map +1 -1
- package/esm/http-user-context.spec.d.ts +3 -1
- package/esm/http-user-context.spec.d.ts.map +1 -1
- package/esm/http-user-context.spec.js +103 -45
- package/esm/http-user-context.spec.js.map +1 -1
- package/esm/index.d.ts +1 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/rest-service.integration.spec.d.ts.map +1 -1
- package/esm/rest-service.integration.spec.js +5 -4
- package/esm/rest-service.integration.spec.js.map +1 -1
- package/esm/validate.integration.spec.js +5 -0
- package/esm/validate.integration.spec.js.map +1 -1
- package/package.json +6 -6
- package/src/authenticate.spec.ts +6 -4
- package/src/authenticate.ts +4 -1
- package/src/authentication-providers/authentication-provider.ts +25 -0
- package/src/authentication-providers/basic-auth-provider.ts +21 -0
- package/src/authentication-providers/cookie-auth-provider.ts +26 -0
- package/src/authentication-providers/helpers.ts +73 -0
- package/src/authentication-providers/index.ts +4 -0
- package/src/endpoint-generators/utils.ts +4 -1
- package/src/helpers.spec.ts +40 -0
- package/src/helpers.ts +48 -3
- package/src/http-authentication-settings.ts +14 -5
- package/src/http-user-context.spec.ts +112 -44
- package/src/http-user-context.ts +27 -57
- package/src/index.ts +1 -0
- package/src/rest-service.integration.spec.ts +5 -4
- package/src/validate.integration.spec.ts +5 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-authentication-settings.d.ts","sourceRoot":"","sources":["../src/http-authentication-settings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"http-authentication-settings.d.ts","sourceRoot":"","sources":["../src/http-authentication-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAEhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAEpD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uDAAuD,CAAA;AACnG,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAE5D;;GAEG;AACH,qBACa,0BAA0B,CAAC,KAAK,SAAS,IAAI,EAAE,QAAQ,SAAS,cAAc;IAClF,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAA+B;IAE1D,cAAc,GAAI,UAAU,QAAQ,2FAA8C;IAElF,iBAAiB,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,QAAQ,EAAE,MAAM,QAAQ,CAAC,CACmB;IAE/F,UAAU,SAAQ;IAClB,eAAe,UAAO;IAE7B;;;;OAIG;IACI,uBAAuB,EAAE,sBAAsB,EAAE,CAAK;CAC9D"}
|
|
@@ -6,16 +6,23 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
};
|
|
7
7
|
import { User } from '@furystack/core';
|
|
8
8
|
import { Injectable } from '@furystack/inject';
|
|
9
|
+
import { getDataSetFor } from '@furystack/repository';
|
|
9
10
|
import { DefaultSession } from './models/default-session.js';
|
|
10
11
|
/**
|
|
11
12
|
* Authentication settings object for FuryStack HTTP Api
|
|
12
13
|
*/
|
|
13
14
|
let HttpAuthenticationSettings = class HttpAuthenticationSettings {
|
|
14
15
|
model = User;
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
getUserDataSet = (injector) => getDataSetFor(injector, User, 'username');
|
|
17
|
+
getSessionDataSet = (injector) => getDataSetFor(injector, DefaultSession, 'sessionId');
|
|
17
18
|
cookieName = 'fss';
|
|
18
19
|
enableBasicAuth = true;
|
|
20
|
+
/**
|
|
21
|
+
* Ordered list of authentication providers. Populated by {@link useHttpAuthentication}
|
|
22
|
+
* and extended by `useJwtAuthentication()` or other auth plugins.
|
|
23
|
+
* Safe to mutate only during setup, before the first request is served.
|
|
24
|
+
*/
|
|
25
|
+
authenticationProviders = [];
|
|
19
26
|
};
|
|
20
27
|
HttpAuthenticationSettings = __decorate([
|
|
21
28
|
Injectable({ lifetime: 'singleton' })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-authentication-settings.js","sourceRoot":"","sources":["../src/http-authentication-settings.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"http-authentication-settings.js","sourceRoot":"","sources":["../src/http-authentication-settings.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAEtC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAE5D;;GAEG;AAEI,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;IAC9B,KAAK,GAAyB,IAA4B,CAAA;IAE1D,cAAc,GAAG,CAAC,QAAkB,EAAE,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,CAAA;IAElF,iBAAiB,GAA8D,CAAC,QAAQ,EAAE,EAAE,CACjG,aAAa,CAAC,QAAQ,EAAE,cAAc,EAAE,WAAW,CAAiD,CAAA;IAE/F,UAAU,GAAG,KAAK,CAAA;IAClB,eAAe,GAAG,IAAI,CAAA;IAE7B;;;;OAIG;IACI,uBAAuB,GAA6B,EAAE,CAAA;CAC9D,CAAA;AAjBY,0BAA0B;IADtC,UAAU,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;GACzB,0BAA0B,CAiBtC"}
|
|
@@ -6,10 +6,9 @@ import type { DefaultSession } from './models/default-session.js';
|
|
|
6
6
|
* Injectable UserContext for FuryStack HTTP Api
|
|
7
7
|
*/
|
|
8
8
|
export declare class HttpUserContext {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
getUserDataSet: () => import("@furystack/repository").DataSet<User, "username", import("@furystack/core").WithOptionalId<User, "username">>;
|
|
10
|
+
getSessionDataSet: () => import("@furystack/repository").DataSet<DefaultSession, keyof DefaultSession, import("@furystack/core").WithOptionalId<DefaultSession, keyof DefaultSession>>;
|
|
11
11
|
private getUserByName;
|
|
12
|
-
private getSessionById;
|
|
13
12
|
private user?;
|
|
14
13
|
/**
|
|
15
14
|
* @param request The request to be authenticated
|
|
@@ -32,6 +31,12 @@ export declare class HttpUserContext {
|
|
|
32
31
|
authenticateUser(userName: string, password: string): Promise<import("@furystack/core").PartialResult<User, (keyof User)[]>>;
|
|
33
32
|
getCurrentUser(request: Pick<IncomingMessage, 'headers'>): Promise<User>;
|
|
34
33
|
getSessionIdFromRequest(request: Pick<IncomingMessage, 'headers'>): string | null;
|
|
34
|
+
/**
|
|
35
|
+
* Iterates registered authentication providers in order.
|
|
36
|
+
* - A provider returning `User` means authentication succeeded.
|
|
37
|
+
* - A provider returning `null` means it does not apply; try the next one.
|
|
38
|
+
* - A provider throwing means it owns the request but auth failed; propagate the error.
|
|
39
|
+
*/
|
|
35
40
|
authenticateRequest(request: Pick<IncomingMessage, 'headers'>): Promise<User>;
|
|
36
41
|
/**
|
|
37
42
|
* Creates and sets up a cookie-based session for the provided user
|
|
@@ -46,7 +51,7 @@ export declare class HttpUserContext {
|
|
|
46
51
|
setHeader: (header: string, value: string) => void;
|
|
47
52
|
}): Promise<void>;
|
|
48
53
|
readonly authentication: HttpAuthenticationSettings<User, DefaultSession>;
|
|
49
|
-
private readonly
|
|
54
|
+
private readonly systemInjector;
|
|
50
55
|
private readonly authenticator;
|
|
51
56
|
init(): void;
|
|
52
57
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-user-context.d.ts","sourceRoot":"","sources":["../src/http-user-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"http-user-context.d.ts","sourceRoot":"","sources":["../src/http-user-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAM3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAE3C,OAAO,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAA;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAEjE;;GAEG;AACH,qBACa,eAAe;IACnB,cAAc,8HAAgE;IAE9E,iBAAiB,sKAAmE;IAE3F,OAAO,CAAC,aAAa,CAOpB;IAED,OAAO,CAAC,IAAI,CAAC,CAAM;IAEnB;;;OAGG;IACU,eAAe,CAAC,OAAO,EAAE,eAAe;IASrD;;;;;OAKG;IACU,YAAY,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAUzF;;;;;OAKG;IACU,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAanD,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC;IAQ9D,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,GAAG,MAAM,GAAG,IAAI;IAIxF;;;;;OAKG;IACU,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ1F;;;;;OAKG;IACU,WAAW,CACtB,IAAI,EAAE,IAAI,EACV,cAAc,EAAE;QAAE,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,GACrE,OAAO,CAAC,IAAI,CAAC;IAQH,YAAY,CACvB,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,EACzC,QAAQ,EAAE;QAAE,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE;IAalE,SACwB,cAAc,EAAE,0BAA0B,CAAC,IAAI,EAAE,cAAc,CAAC,CAAA;IAExF,iBACyB,cAAc,CAAU;IAEjD,iBACyB,aAAa,CAAuB;IAEtD,IAAI;CAiBZ"}
|
package/esm/http-user-context.js
CHANGED
|
@@ -7,33 +7,26 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
-
import {
|
|
10
|
+
import { useSystemIdentityContext } from '@furystack/core';
|
|
11
11
|
import { Injectable, Injected } from '@furystack/inject';
|
|
12
12
|
import { PasswordAuthenticator, UnauthenticatedError } from '@furystack/security';
|
|
13
13
|
import { randomBytes } from 'crypto';
|
|
14
|
+
import { extractSessionIdFromCookies } from './authentication-providers/helpers.js';
|
|
14
15
|
import { HttpAuthenticationSettings } from './http-authentication-settings.js';
|
|
15
16
|
/**
|
|
16
17
|
* Injectable UserContext for FuryStack HTTP Api
|
|
17
18
|
*/
|
|
18
19
|
let HttpUserContext = class HttpUserContext {
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
getUserDataSet = () => this.authentication.getUserDataSet(this.systemInjector);
|
|
21
|
+
getSessionDataSet = () => this.authentication.getSessionDataSet(this.systemInjector);
|
|
21
22
|
getUserByName = async (userName) => {
|
|
22
|
-
const
|
|
23
|
-
const users = await
|
|
23
|
+
const userDataSet = this.getUserDataSet();
|
|
24
|
+
const users = await userDataSet.find(this.systemInjector, { filter: { username: { $eq: userName } }, top: 2 });
|
|
24
25
|
if (users.length !== 1) {
|
|
25
26
|
throw new UnauthenticatedError();
|
|
26
27
|
}
|
|
27
28
|
return users[0];
|
|
28
29
|
};
|
|
29
|
-
getSessionById = async (sessionId) => {
|
|
30
|
-
const sessionStore = this.getSessionStore();
|
|
31
|
-
const sessions = await sessionStore.find({ filter: { sessionId: { $eq: sessionId } }, top: 2 });
|
|
32
|
-
if (sessions.length !== 1) {
|
|
33
|
-
throw new UnauthenticatedError();
|
|
34
|
-
}
|
|
35
|
-
return sessions[0];
|
|
36
|
-
};
|
|
37
30
|
user;
|
|
38
31
|
/**
|
|
39
32
|
* @param request The request to be authenticated
|
|
@@ -88,39 +81,19 @@ let HttpUserContext = class HttpUserContext {
|
|
|
88
81
|
return this.user;
|
|
89
82
|
}
|
|
90
83
|
getSessionIdFromRequest(request) {
|
|
91
|
-
|
|
92
|
-
const cookies = request.headers.cookie
|
|
93
|
-
.toString()
|
|
94
|
-
.split(';')
|
|
95
|
-
.filter((val) => val.length > 0)
|
|
96
|
-
.map((val) => {
|
|
97
|
-
const [name, value] = val.split('=');
|
|
98
|
-
return { name: name?.trim(), value: value?.trim() };
|
|
99
|
-
});
|
|
100
|
-
const sessionCookie = cookies.find((c) => c.name === this.authentication.cookieName);
|
|
101
|
-
if (sessionCookie) {
|
|
102
|
-
return sessionCookie.value;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return null;
|
|
84
|
+
return extractSessionIdFromCookies(request, this.authentication.cookieName);
|
|
106
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Iterates registered authentication providers in order.
|
|
88
|
+
* - A provider returning `User` means authentication succeeded.
|
|
89
|
+
* - A provider returning `null` means it does not apply; try the next one.
|
|
90
|
+
* - A provider throwing means it owns the request but auth failed; propagate the error.
|
|
91
|
+
*/
|
|
107
92
|
async authenticateRequest(request) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return await this.authenticateUser(userName, password);
|
|
113
|
-
}
|
|
114
|
-
// Cookie auth
|
|
115
|
-
const sessionId = this.getSessionIdFromRequest(request);
|
|
116
|
-
if (sessionId) {
|
|
117
|
-
const session = await this.getSessionById(sessionId);
|
|
118
|
-
if (session) {
|
|
119
|
-
const user = await this.getUserByName(session.username);
|
|
120
|
-
if (user) {
|
|
121
|
-
return user;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
93
|
+
for (const provider of this.authentication.authenticationProviders) {
|
|
94
|
+
const user = await provider.authenticate(request);
|
|
95
|
+
if (user)
|
|
96
|
+
return user;
|
|
124
97
|
}
|
|
125
98
|
throw new UnauthenticatedError();
|
|
126
99
|
}
|
|
@@ -132,7 +105,7 @@ let HttpUserContext = class HttpUserContext {
|
|
|
132
105
|
*/
|
|
133
106
|
async cookieLogin(user, serverResponse) {
|
|
134
107
|
const sessionId = randomBytes(32).toString('hex');
|
|
135
|
-
await this.
|
|
108
|
+
await this.getSessionDataSet().add(this.systemInjector, { sessionId, username: user.username });
|
|
136
109
|
serverResponse.setHeader('Set-Cookie', `${this.authentication.cookieName}=${sessionId}; Path=/; HttpOnly`);
|
|
137
110
|
this.user = user;
|
|
138
111
|
return user;
|
|
@@ -142,24 +115,24 @@ let HttpUserContext = class HttpUserContext {
|
|
|
142
115
|
const sessionId = this.getSessionIdFromRequest(request);
|
|
143
116
|
response.setHeader('Set-Cookie', `${this.authentication.cookieName}=; Path=/; HttpOnly`);
|
|
144
117
|
if (sessionId) {
|
|
145
|
-
const
|
|
146
|
-
const sessions = await
|
|
147
|
-
await
|
|
118
|
+
const sessionDataSet = this.getSessionDataSet();
|
|
119
|
+
const sessions = await sessionDataSet.find(this.systemInjector, { filter: { sessionId: { $eq: sessionId } } });
|
|
120
|
+
await sessionDataSet.remove(this.systemInjector, ...sessions.map((s) => s[sessionDataSet.primaryKey]));
|
|
148
121
|
}
|
|
149
122
|
}
|
|
150
123
|
init() {
|
|
151
|
-
this.
|
|
124
|
+
this.getUserDataSet().addListener('onEntityUpdated', ({ id, change }) => {
|
|
152
125
|
if (this.user?.username === id) {
|
|
153
126
|
this.user = { ...this.user, ...change };
|
|
154
127
|
}
|
|
155
128
|
});
|
|
156
|
-
this.
|
|
129
|
+
this.getUserDataSet().addListener('onEntityRemoved', ({ key }) => {
|
|
157
130
|
if (this.user?.username === key) {
|
|
158
131
|
this.user = undefined;
|
|
159
132
|
}
|
|
160
133
|
});
|
|
161
|
-
this.
|
|
162
|
-
this.user = undefined;
|
|
134
|
+
this.getSessionDataSet().addListener('onEntityRemoved', () => {
|
|
135
|
+
this.user = undefined;
|
|
163
136
|
});
|
|
164
137
|
}
|
|
165
138
|
};
|
|
@@ -168,9 +141,9 @@ __decorate([
|
|
|
168
141
|
__metadata("design:type", HttpAuthenticationSettings)
|
|
169
142
|
], HttpUserContext.prototype, "authentication", void 0);
|
|
170
143
|
__decorate([
|
|
171
|
-
Injected(
|
|
172
|
-
__metadata("design:type",
|
|
173
|
-
], HttpUserContext.prototype, "
|
|
144
|
+
Injected((injector) => useSystemIdentityContext({ injector, username: 'HttpUserContext' })),
|
|
145
|
+
__metadata("design:type", Function)
|
|
146
|
+
], HttpUserContext.prototype, "systemInjector", void 0);
|
|
174
147
|
__decorate([
|
|
175
148
|
Injected(PasswordAuthenticator),
|
|
176
149
|
__metadata("design:type", PasswordAuthenticator)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-user-context.js","sourceRoot":"","sources":["../src/http-user-context.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"http-user-context.js","sourceRoot":"","sources":["../src/http-user-context.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAE1D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAEpC,OAAO,EAAE,2BAA2B,EAAE,MAAM,uCAAuC,CAAA;AACnF,OAAO,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAA;AAG9E;;GAEG;AAEI,IAAM,eAAe,GAArB,MAAM,eAAe;IACnB,cAAc,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;IAE9E,iBAAiB,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;IAEnF,aAAa,GAAG,KAAK,EAAE,QAAgB,EAAE,EAAE;QACjD,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,EAAE,CAAA;QACzC,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;QAC9G,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,oBAAoB,EAAE,CAAA;QAClC,CAAC;QACD,OAAO,KAAK,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAA;IAEO,IAAI,CAAO;IAEnB;;;OAGG;IACI,KAAK,CAAC,eAAe,CAAC,OAAwB;QACnD,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;YACtD,OAAO,WAAW,KAAK,IAAI,CAAA;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,YAAY,CAAC,OAAwB,EAAE,GAAG,KAAe;QACpE,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;QACtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,WAAW,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;gBAC/D,OAAO,KAAK,CAAA;YACd,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,gBAAgB,CAAC,QAAgB,EAAE,QAAgB;QAC9D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QAEhF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,oBAAoB,EAAE,CAAA;QAClC,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QAC/C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,oBAAoB,EAAE,CAAA;QAClC,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAEM,KAAK,CAAC,cAAc,CAAC,OAAyC;QACnE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAA;YACnD,OAAO,IAAI,CAAC,IAAI,CAAA;QAClB,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAA;IAClB,CAAC;IAEM,uBAAuB,CAAC,OAAyC;QACtE,OAAO,2BAA2B,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,CAAA;IAC7E,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,mBAAmB,CAAC,OAAyC;QACxE,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,cAAc,CAAC,uBAAuB,EAAE,CAAC;YACnE,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;YACjD,IAAI,IAAI;gBAAE,OAAO,IAAI,CAAA;QACvB,CAAC;QACD,MAAM,IAAI,oBAAoB,EAAE,CAAA;IAClC,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,WAAW,CACtB,IAAU,EACV,cAAsE;QAEtE,MAAM,SAAS,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QACjD,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC/F,cAAc,CAAC,SAAS,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,IAAI,SAAS,oBAAoB,CAAC,CAAA;QAC1G,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,OAAO,IAAI,CAAA;IACb,CAAC;IAEM,KAAK,CAAC,YAAY,CACvB,OAAyC,EACzC,QAAgE;QAEhE,IAAI,CAAC,IAAI,GAAG,SAAS,CAAA;QACrB,MAAM,SAAS,GAAG,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAA;QACvD,QAAQ,CAAC,SAAS,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,qBAAqB,CAAC,CAAA;QAExF,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAA;YAC/C,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAA;YAC9G,MAAM,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;QACxG,CAAC;IACH,CAAC;IAWM,IAAI;QACT,IAAI,CAAC,cAAc,EAAE,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;YACtE,IAAI,IAAI,CAAC,IAAI,EAAE,QAAQ,KAAK,EAAE,EAAE,CAAC;gBAC/B,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,MAAM,EAAE,CAAA;YACzC,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,cAAc,EAAE,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;YAC/D,IAAI,IAAI,CAAC,IAAI,EAAE,QAAQ,KAAK,GAAG,EAAE,CAAC;gBAChC,IAAI,CAAC,IAAI,GAAG,SAAS,CAAA;YACvB,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,iBAAiB,EAAE,CAAC,WAAW,CAAC,iBAAiB,EAAE,GAAG,EAAE;YAC3D,IAAI,CAAC,IAAI,GAAG,SAAS,CAAA;QACvB,CAAC,CAAC,CAAA;IACJ,CAAC;CACF,CAAA;AAzByB;IADvB,QAAQ,CAAC,0BAA0B,CAAC;8BACG,0BAA0B;uDAAsB;AAG/D;IADxB,QAAQ,CAAC,CAAC,QAAkB,EAAE,EAAE,CAAC,wBAAwB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC,CAAC;;uDACrD;AAGxB;IADxB,QAAQ,CAAC,qBAAqB,CAAC;8BACQ,qBAAqB;sDAAA;AAjIlD,eAAe;IAD3B,UAAU,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;GACtB,eAAe,CAoJ3B"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { Injector } from '@furystack/inject';
|
|
2
|
-
export declare const prepareInjector: (i: Injector
|
|
2
|
+
export declare const prepareInjector: (i: Injector, options?: {
|
|
3
|
+
enableBasicAuth?: boolean;
|
|
4
|
+
}) => Promise<void>;
|
|
3
5
|
//# sourceMappingURL=http-user-context.spec.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-user-context.spec.d.ts","sourceRoot":"","sources":["../src/http-user-context.spec.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"http-user-context.spec.d.ts","sourceRoot":"","sources":["../src/http-user-context.spec.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAgB5C,eAAO,MAAM,eAAe,GAAU,GAAG,QAAQ,EAAE,UAAU;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,kBAczF,CAAA"}
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
-
import { InMemoryStore, StoreManager, User, addStore } from '@furystack/core';
|
|
2
|
+
import { InMemoryStore, StoreManager, User, addStore, useSystemIdentityContext } from '@furystack/core';
|
|
3
3
|
import { Injector } from '@furystack/inject';
|
|
4
|
-
import {
|
|
4
|
+
import { getDataSetFor, getRepository } from '@furystack/repository';
|
|
5
|
+
import { PasswordAuthenticator, PasswordCredential, PasswordResetToken, UnauthenticatedError, usePasswordPolicy, } from '@furystack/security';
|
|
5
6
|
import { usingAsync } from '@furystack/utils';
|
|
6
7
|
import { describe, expect, it, vi } from 'vitest';
|
|
7
8
|
import { useHttpAuthentication } from './helpers.js';
|
|
8
9
|
import { HttpUserContext } from './http-user-context.js';
|
|
9
10
|
import { DefaultSession } from './models/default-session.js';
|
|
10
|
-
export const prepareInjector = async (i) => {
|
|
11
|
+
export const prepareInjector = async (i, options) => {
|
|
11
12
|
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' }))
|
|
12
13
|
.addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }))
|
|
13
|
-
.addStore(new InMemoryStore({ model: PasswordCredential, primaryKey: 'userName' }))
|
|
14
|
-
|
|
14
|
+
.addStore(new InMemoryStore({ model: PasswordCredential, primaryKey: 'userName' }))
|
|
15
|
+
.addStore(new InMemoryStore({ model: PasswordResetToken, primaryKey: 'token' }));
|
|
16
|
+
const repo = getRepository(i);
|
|
17
|
+
repo.createDataSet(User, 'username');
|
|
18
|
+
repo.createDataSet(DefaultSession, 'sessionId');
|
|
19
|
+
repo.createDataSet(PasswordCredential, 'userName');
|
|
20
|
+
repo.createDataSet(PasswordResetToken, 'token');
|
|
21
|
+
usePasswordPolicy(i);
|
|
22
|
+
useHttpAuthentication(i, { enableBasicAuth: options?.enableBasicAuth ?? true });
|
|
15
23
|
};
|
|
16
24
|
const setupUser = async (i, userName, password) => {
|
|
17
25
|
const sm = i.getInstance(StoreManager);
|
|
@@ -155,28 +163,25 @@ describe('HttpUserContext', () => {
|
|
|
155
163
|
});
|
|
156
164
|
});
|
|
157
165
|
describe('authenticateRequest', () => {
|
|
158
|
-
it('Should
|
|
166
|
+
it('Should authenticate with Basic Auth when enabled and valid credentials provided', async () => {
|
|
159
167
|
await usingAsync(new Injector(), async (i) => {
|
|
160
168
|
await prepareInjector(i);
|
|
169
|
+
await setupUser(i, 'testuser', 'password');
|
|
161
170
|
const ctx = i.getInstance(HttpUserContext);
|
|
162
|
-
ctx.authenticateUser = vi.fn(async () => testUser);
|
|
163
171
|
const result = await ctx.authenticateRequest({
|
|
164
172
|
headers: { authorization: `Basic dGVzdHVzZXI6cGFzc3dvcmQ=` },
|
|
165
173
|
});
|
|
166
|
-
expect(
|
|
167
|
-
expect(result).toBe(testUser);
|
|
174
|
+
expect(result.username).toBe('testuser');
|
|
168
175
|
});
|
|
169
176
|
});
|
|
170
|
-
it('Should NOT try to authenticate with Basic
|
|
177
|
+
it('Should NOT try to authenticate with Basic when disabled', async () => {
|
|
171
178
|
await usingAsync(new Injector(), async (i) => {
|
|
172
|
-
await prepareInjector(i);
|
|
179
|
+
await prepareInjector(i, { enableBasicAuth: false });
|
|
180
|
+
await setupUser(i, 'testuser', 'password');
|
|
173
181
|
const ctx = i.getInstance(HttpUserContext);
|
|
174
|
-
ctx.authentication.enableBasicAuth = false;
|
|
175
|
-
ctx.authenticateUser = vi.fn(async () => testUser);
|
|
176
182
|
await expect(ctx.authenticateRequest({
|
|
177
183
|
headers: { authorization: `Basic dGVzdHVzZXI6cGFzc3dvcmQ=` },
|
|
178
184
|
})).rejects.toThrowError(UnauthenticatedError);
|
|
179
|
-
expect(ctx.authenticateUser).not.toBeCalled();
|
|
180
185
|
});
|
|
181
186
|
});
|
|
182
187
|
it('Should fail with no session in the store', async () => {
|
|
@@ -192,9 +197,10 @@ describe('HttpUserContext', () => {
|
|
|
192
197
|
await usingAsync(new Injector(), async (i) => {
|
|
193
198
|
await prepareInjector(i);
|
|
194
199
|
const ctx = i.getInstance(HttpUserContext);
|
|
195
|
-
await
|
|
196
|
-
|
|
197
|
-
|
|
200
|
+
await i.getInstance(StoreManager).getStoreFor(DefaultSession, 'sessionId').add({
|
|
201
|
+
sessionId: '666',
|
|
202
|
+
username: testUser.username,
|
|
203
|
+
});
|
|
198
204
|
await expect(ctx.authenticateRequest({
|
|
199
205
|
headers: { cookie: `${ctx.authentication.cookieName}=666;a=3` },
|
|
200
206
|
})).rejects.toThrowError(UnauthenticatedError);
|
|
@@ -204,16 +210,60 @@ describe('HttpUserContext', () => {
|
|
|
204
210
|
await usingAsync(new Injector(), async (i) => {
|
|
205
211
|
await prepareInjector(i);
|
|
206
212
|
const ctx = i.getInstance(HttpUserContext);
|
|
207
|
-
await
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
213
|
+
await i.getInstance(StoreManager).getStoreFor(DefaultSession, 'sessionId').add({
|
|
214
|
+
sessionId: '666',
|
|
215
|
+
username: testUser.username,
|
|
216
|
+
});
|
|
217
|
+
await i
|
|
218
|
+
.getInstance(StoreManager)
|
|
219
|
+
.getStoreFor(User, 'username')
|
|
220
|
+
.add({ ...testUser });
|
|
211
221
|
const result = await ctx.authenticateRequest({
|
|
212
222
|
headers: { cookie: `${ctx.authentication.cookieName}=666;a=3` },
|
|
213
223
|
});
|
|
214
224
|
expect(result).toEqual(testUser);
|
|
215
225
|
});
|
|
216
226
|
});
|
|
227
|
+
it('Should iterate providers and return null results pass to next', async () => {
|
|
228
|
+
await usingAsync(new Injector(), async (i) => {
|
|
229
|
+
await prepareInjector(i);
|
|
230
|
+
const ctx = i.getInstance(HttpUserContext);
|
|
231
|
+
const provider1 = vi.fn(async () => null);
|
|
232
|
+
const provider2 = vi.fn(async () => testUser);
|
|
233
|
+
ctx.authentication.authenticationProviders = [
|
|
234
|
+
{ name: 'test-1', authenticate: provider1 },
|
|
235
|
+
{ name: 'test-2', authenticate: provider2 },
|
|
236
|
+
];
|
|
237
|
+
const result = await ctx.authenticateRequest(request);
|
|
238
|
+
expect(provider1).toHaveBeenCalledOnce();
|
|
239
|
+
expect(provider2).toHaveBeenCalledOnce();
|
|
240
|
+
expect(result).toBe(testUser);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
it('Should throw if provider throws (skipping remaining providers)', async () => {
|
|
244
|
+
await usingAsync(new Injector(), async (i) => {
|
|
245
|
+
await prepareInjector(i);
|
|
246
|
+
const ctx = i.getInstance(HttpUserContext);
|
|
247
|
+
const provider1 = vi.fn(async () => {
|
|
248
|
+
throw new UnauthenticatedError();
|
|
249
|
+
});
|
|
250
|
+
const provider2 = vi.fn(async () => testUser);
|
|
251
|
+
ctx.authentication.authenticationProviders = [
|
|
252
|
+
{ name: 'test-1', authenticate: provider1 },
|
|
253
|
+
{ name: 'test-2', authenticate: provider2 },
|
|
254
|
+
];
|
|
255
|
+
await expect(ctx.authenticateRequest(request)).rejects.toThrowError(UnauthenticatedError);
|
|
256
|
+
expect(provider2).not.toHaveBeenCalled();
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
it('Should throw UnauthenticatedError if no provider returns a user', async () => {
|
|
260
|
+
await usingAsync(new Injector(), async (i) => {
|
|
261
|
+
await prepareInjector(i);
|
|
262
|
+
const ctx = i.getInstance(HttpUserContext);
|
|
263
|
+
ctx.authentication.authenticationProviders = [{ name: 'test-1', authenticate: async () => null }];
|
|
264
|
+
await expect(ctx.authenticateRequest(request)).rejects.toThrowError(UnauthenticatedError);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
217
267
|
});
|
|
218
268
|
describe('getCurrentUser', () => {
|
|
219
269
|
it('Should return the current user from authenticateRequest() once per request', async () => {
|
|
@@ -235,14 +285,13 @@ describe('HttpUserContext', () => {
|
|
|
235
285
|
await prepareInjector(i);
|
|
236
286
|
const ctx = i.getInstance(HttpUserContext);
|
|
237
287
|
const setHeader = vi.fn();
|
|
288
|
+
const addMock = vi.fn(async () => ({}));
|
|
238
289
|
// @ts-expect-error
|
|
239
|
-
ctx.
|
|
240
|
-
return {};
|
|
241
|
-
});
|
|
290
|
+
ctx.getSessionDataSet = vi.fn(() => ({ add: addMock }));
|
|
242
291
|
const authResult = await ctx.cookieLogin(testUser, { setHeader });
|
|
243
292
|
expect(authResult).toBe(testUser);
|
|
244
293
|
expect(setHeader).toBeCalled();
|
|
245
|
-
expect(
|
|
294
|
+
expect(addMock).toBeCalled();
|
|
246
295
|
});
|
|
247
296
|
});
|
|
248
297
|
});
|
|
@@ -252,18 +301,22 @@ describe('HttpUserContext', () => {
|
|
|
252
301
|
await prepareInjector(i);
|
|
253
302
|
const ctx = i.getInstance(HttpUserContext);
|
|
254
303
|
const setHeader = vi.fn();
|
|
304
|
+
const removeMock = vi.fn(async () => undefined);
|
|
305
|
+
const sessionDataSetMock = {
|
|
306
|
+
add: vi.fn(async () => ({})),
|
|
307
|
+
find: vi.fn(async () => [{ sessionId: 'example-session-id' }]),
|
|
308
|
+
remove: removeMock,
|
|
309
|
+
primaryKey: 'sessionId',
|
|
310
|
+
};
|
|
255
311
|
// @ts-expect-error
|
|
256
|
-
ctx.
|
|
257
|
-
return {};
|
|
258
|
-
});
|
|
312
|
+
ctx.getSessionDataSet = vi.fn(() => sessionDataSetMock);
|
|
259
313
|
ctx.authenticateRequest = vi.fn(async () => testUser);
|
|
260
|
-
ctx.getSessionStore().remove = vi.fn(async () => undefined);
|
|
261
314
|
ctx.getSessionIdFromRequest = () => 'example-session-id';
|
|
262
315
|
response.setHeader = vi.fn(() => response);
|
|
263
316
|
await ctx.cookieLogin(testUser, { setHeader });
|
|
264
317
|
await ctx.cookieLogout(request, response);
|
|
265
318
|
expect(response.setHeader).toBeCalledWith('Set-Cookie', 'fss=; Path=/; HttpOnly');
|
|
266
|
-
expect(
|
|
319
|
+
expect(removeMock).toBeCalled();
|
|
267
320
|
});
|
|
268
321
|
});
|
|
269
322
|
});
|
|
@@ -272,18 +325,20 @@ describe('HttpUserContext', () => {
|
|
|
272
325
|
return usingAsync(new Injector(), async (i) => {
|
|
273
326
|
await prepareInjector(i);
|
|
274
327
|
const ctx = i.getInstance(HttpUserContext);
|
|
275
|
-
const
|
|
276
|
-
await
|
|
328
|
+
const sm = i.getInstance(StoreManager);
|
|
329
|
+
await sm.getStoreFor(User, 'username').add(testUser);
|
|
277
330
|
const pw = await i.getInstance(PasswordAuthenticator).hasher.createCredential(testUser.username, 'test');
|
|
278
|
-
await
|
|
331
|
+
await sm.getStoreFor(PasswordCredential, 'userName').add(pw);
|
|
279
332
|
await ctx.cookieLogin(testUser, { setHeader: vi.fn() });
|
|
280
333
|
const originalUser = await ctx.getCurrentUser(request);
|
|
281
334
|
expect(originalUser).toEqual(testUser);
|
|
335
|
+
const systemInjector = useSystemIdentityContext({ injector: i, username: 'test' });
|
|
336
|
+
const userDataSet = getDataSetFor(systemInjector, User, 'username');
|
|
282
337
|
const updatedUser = { ...testUser, roles: ['newFancyRole'] };
|
|
283
|
-
await
|
|
338
|
+
await userDataSet.update(systemInjector, testUser.username, updatedUser);
|
|
284
339
|
const updatedUserFromContext = await ctx.getCurrentUser(request);
|
|
285
340
|
expect(updatedUserFromContext.roles).toEqual(['newFancyRole']);
|
|
286
|
-
await
|
|
341
|
+
await userDataSet.update(systemInjector, testUser.username, { ...updatedUser, roles: [] });
|
|
287
342
|
const reloadedUserFromContext = await ctx.getCurrentUser(request);
|
|
288
343
|
expect(reloadedUserFromContext.roles).toEqual([]);
|
|
289
344
|
});
|
|
@@ -292,14 +347,16 @@ describe('HttpUserContext', () => {
|
|
|
292
347
|
return usingAsync(new Injector(), async (i) => {
|
|
293
348
|
await prepareInjector(i);
|
|
294
349
|
const ctx = i.getInstance(HttpUserContext);
|
|
295
|
-
const
|
|
296
|
-
await
|
|
350
|
+
const sm = i.getInstance(StoreManager);
|
|
351
|
+
await sm.getStoreFor(User, 'username').add(testUser);
|
|
297
352
|
const pw = await i.getInstance(PasswordAuthenticator).hasher.createCredential(testUser.username, 'test');
|
|
298
|
-
await
|
|
353
|
+
await sm.getStoreFor(PasswordCredential, 'userName').add(pw);
|
|
299
354
|
await ctx.cookieLogin(testUser, { setHeader: vi.fn() });
|
|
300
355
|
const originalUser = await ctx.getCurrentUser(request);
|
|
301
356
|
expect(originalUser).toEqual(testUser);
|
|
302
|
-
|
|
357
|
+
const systemInjector = useSystemIdentityContext({ injector: i, username: 'test' });
|
|
358
|
+
const userDataSet = getDataSetFor(systemInjector, User, 'username');
|
|
359
|
+
await userDataSet.remove(systemInjector, testUser.username);
|
|
303
360
|
await expect(() => ctx.getCurrentUser(request)).rejects.toThrowError(UnauthenticatedError);
|
|
304
361
|
});
|
|
305
362
|
});
|
|
@@ -307,21 +364,22 @@ describe('HttpUserContext', () => {
|
|
|
307
364
|
return usingAsync(new Injector(), async (i) => {
|
|
308
365
|
await prepareInjector(i);
|
|
309
366
|
const ctx = i.getInstance(HttpUserContext);
|
|
310
|
-
const
|
|
311
|
-
await
|
|
367
|
+
const sm = i.getInstance(StoreManager);
|
|
368
|
+
await sm.getStoreFor(User, 'username').add(testUser);
|
|
312
369
|
let sessionId = '';
|
|
313
370
|
const pw = await i.getInstance(PasswordAuthenticator).hasher.createCredential(testUser.username, 'test');
|
|
314
|
-
await
|
|
371
|
+
await sm.getStoreFor(PasswordCredential, 'userName').add(pw);
|
|
315
372
|
await ctx.cookieLogin(testUser, {
|
|
316
373
|
setHeader: (_headerName, headerValue) => {
|
|
317
|
-
sessionId = headerValue;
|
|
374
|
+
sessionId = headerValue.split('=')[1].split(';')[0];
|
|
318
375
|
return {};
|
|
319
376
|
},
|
|
320
377
|
});
|
|
321
378
|
const originalUser = await ctx.getCurrentUser(request);
|
|
322
379
|
expect(originalUser).toEqual(testUser);
|
|
323
|
-
const
|
|
324
|
-
|
|
380
|
+
const systemInjector = useSystemIdentityContext({ injector: i, username: 'test' });
|
|
381
|
+
const sessionDataSet = getDataSetFor(systemInjector, DefaultSession, 'sessionId');
|
|
382
|
+
await sessionDataSet.remove(systemInjector, sessionId);
|
|
325
383
|
await expect(() => ctx.getCurrentUser(request)).rejects.toThrowError(UnauthenticatedError);
|
|
326
384
|
});
|
|
327
385
|
});
|