kuzzle 2.19.12 → 2.20.1

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.
Files changed (33) hide show
  1. package/index.d.ts +1 -1
  2. package/index.js +1 -1
  3. package/lib/api/controllers/authController.d.ts +164 -0
  4. package/lib/api/controllers/authController.js +469 -654
  5. package/lib/api/controllers/baseController.d.ts +74 -0
  6. package/lib/api/controllers/baseController.js +169 -221
  7. package/lib/api/controllers/documentController.js +6 -8
  8. package/lib/api/funnel.js +4 -1
  9. package/lib/api/httpRoutes.js +6 -0
  10. package/lib/api/openapi/openApiGenerator.js +2 -2
  11. package/lib/api/request/kuzzleRequest.d.ts +3 -1
  12. package/lib/api/request/kuzzleRequest.js +32 -0
  13. package/lib/core/backend/backendController.js +2 -2
  14. package/lib/core/backend/backendPlugin.js +2 -2
  15. package/lib/core/network/protocols/httpwsProtocol.js +0 -12
  16. package/lib/core/plugin/pluginRepository.js +1 -1
  17. package/lib/core/plugin/pluginsManager.js +1 -1
  18. package/lib/core/security/index.js +1 -1
  19. package/lib/core/security/profileRepository.d.ts +14 -4
  20. package/lib/core/security/profileRepository.js +2 -2
  21. package/lib/core/security/roleRepository.js +1 -1
  22. package/lib/core/security/tokenRepository.d.ts +73 -0
  23. package/lib/core/security/tokenRepository.js +359 -460
  24. package/lib/core/security/userRepository.js +1 -1
  25. package/lib/core/shared/repository.d.ts +178 -0
  26. package/lib/core/shared/repository.js +365 -450
  27. package/lib/kerror/codes/7-security.json +6 -0
  28. package/lib/model/security/token.d.ts +2 -0
  29. package/lib/model/security/token.js +1 -0
  30. package/lib/service/storage/elasticsearch.js +4 -0
  31. package/lib/util/{inflector.d.ts → Inflector.d.ts} +5 -0
  32. package/lib/util/{inflector.js → Inflector.js} +12 -1
  33. package/package.json +11 -4
@@ -1,3 +1,32 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.AuthController = void 0;
1
30
  /*
2
31
  * Kuzzle, a backend software, self-hostable and ready to use
3
32
  * to power modern apps
@@ -18,683 +47,469 @@
18
47
  * See the License for the specific language governing permissions and
19
48
  * limitations under the License.
20
49
  */
21
-
22
- "use strict";
23
-
24
- const { IncomingMessage } = require("http");
25
- const Cookie = require("cookie");
26
-
27
- const Bluebird = require("bluebird");
28
- const { isEmpty } = require("lodash");
29
-
30
- const { KuzzleError } = require("../../kerror/errors");
31
- const { KuzzleRequest } = require("../request");
32
- const kerror = require("../../kerror");
33
- const { has } = require("../../util/safeObject");
34
- const { NativeController } = require("./baseController");
35
- const formatProcessing = require("../../core/auth/formatProcessing");
36
- const { User } = require("../../model/security/user");
37
- const ApiKey = require("../../model/storage/apiKey");
38
- const SecurityController = require("./securityController");
39
-
40
- /**
41
- * @class AuthController
42
- */
43
- class AuthController extends NativeController {
44
- /**
45
- * @param {Kuzzle} kuzzle
46
- * @constructor
47
- */
48
- constructor() {
49
- super([
50
- "checkRights",
51
- "checkToken",
52
- "createApiKey",
53
- "createMyCredentials",
54
- "credentialsExist",
55
- "deleteApiKey",
56
- "deleteMyCredentials",
57
- "getCurrentUser",
58
- "getMyCredentials",
59
- "getMyRights",
60
- "getStrategies",
61
- "login",
62
- "logout",
63
- "refreshToken",
64
- "searchApiKeys",
65
- "updateMyCredentials",
66
- "updateSelf",
67
- "validateMyCredentials",
68
- ]);
69
-
70
- this.anonymousId = null;
71
- }
72
-
73
- /**
74
- * Controller initialization: we need the anonymous user identifier for the
75
- * "isAuthenticated" assertion
76
- *
77
- * @returns {Promise}
78
- */
79
- async init() {
80
- const anonymous = await global.kuzzle.ask(
81
- "core:security:user:anonymous:get"
82
- );
83
- this.anonymousId = anonymous._id;
84
- }
85
-
86
- /**
87
- * Checks if an API action can be executed by the current user
88
- */
89
- async checkRights(request) {
90
- const requestPayload = request.getBody();
91
-
92
- if (typeof requestPayload.controller !== "string") {
93
- throw kerror.get("api", "assert", "missing_argument", "body.controller");
50
+ const http_1 = require("http");
51
+ const cookie_1 = __importDefault(require("cookie"));
52
+ const bluebird_1 = __importDefault(require("bluebird"));
53
+ const lodash_1 = require("lodash");
54
+ const errors_1 = require("../../kerror/errors");
55
+ const request_1 = require("../request");
56
+ const kerror = __importStar(require("../../kerror"));
57
+ const safeObject_1 = require("../../util/safeObject");
58
+ const baseController_1 = require("./baseController");
59
+ const formatProcessing_1 = __importDefault(require("../../core/auth/formatProcessing"));
60
+ const user_1 = require("../../model/security/user");
61
+ const apiKey_1 = __importDefault(require("../../model/storage/apiKey"));
62
+ const securityController_1 = __importDefault(require("./securityController"));
63
+ class AuthController extends baseController_1.NativeController {
64
+ /**
65
+ * @param {Kuzzle} kuzzle
66
+ * @constructor
67
+ */
68
+ constructor() {
69
+ super([
70
+ "checkRights",
71
+ "checkToken",
72
+ "createApiKey",
73
+ "createMyCredentials",
74
+ "createToken",
75
+ "credentialsExist",
76
+ "deleteApiKey",
77
+ "deleteMyCredentials",
78
+ "getCurrentUser",
79
+ "getMyCredentials",
80
+ "getMyRights",
81
+ "getStrategies",
82
+ "login",
83
+ "logout",
84
+ "refreshToken",
85
+ "searchApiKeys",
86
+ "updateMyCredentials",
87
+ "updateSelf",
88
+ "validateMyCredentials",
89
+ ]);
90
+ this.anonymousId = null;
94
91
  }
95
-
96
- if (typeof requestPayload.action !== "string") {
97
- throw kerror.get("api", "assert", "missing_argument", "body.action");
92
+ /**
93
+ * Controller initialization: we need the anonymous user identifier for the
94
+ * "isAuthenticated" assertion
95
+ *
96
+ * @returns {Promise}
97
+ */
98
+ async init() {
99
+ const anonymous = await global.kuzzle.ask("core:security:user:anonymous:get");
100
+ this.anonymousId = anonymous._id;
98
101
  }
99
-
100
- const user = request.context.user;
101
-
102
- const allowed = await user.isActionAllowed(
103
- new KuzzleRequest(requestPayload)
104
- );
105
-
106
- return {
107
- allowed,
108
- };
109
- }
110
-
111
- /**
112
- * Creates a new API key for the user
113
- * @param {KuzzleRequest} request
114
- */
115
- async createApiKey(request) {
116
- const expiresIn = request.input.args.expiresIn || -1;
117
- const refresh = request.getRefresh("wait_for");
118
- const apiKeyId = request.getId({ ifMissing: "generate" });
119
- const description = request.getBodyString("description");
120
-
121
- const user = request.context.user;
122
-
123
- const apiKey = await ApiKey.create(user, expiresIn, description, {
124
- apiKeyId,
125
- creatorId: user._id,
126
- refresh,
127
- });
128
-
129
- return apiKey.serialize({ includeToken: true });
130
- }
131
-
132
- /**
133
- * Search in the user API keys
134
- */
135
- async searchApiKeys(request) {
136
- let query = request.getBody({});
137
- const { from, size } = request.getSearchParams();
138
- const lang = request.getLangParam();
139
-
140
- const user = request.context.user;
141
-
142
- if (lang === "koncorde") {
143
- query = await this.translateKoncorde(query);
102
+ async createToken(request) {
103
+ const singleUse = request.getBoolean("singleUse");
104
+ if (`${request.input.args.expiresIn}` === "-1") {
105
+ throw kerror.get("security", "token", "invalid_expiration", "expiresIn", "cannot be infinite");
106
+ }
107
+ const token = await this.ask("core:security:token:create", request.getUser(), {
108
+ expiresIn: request.input.args.expiresIn,
109
+ singleUse,
110
+ });
111
+ return {
112
+ expiresAt: token.expiresAt,
113
+ singleUse: token.singleUse,
114
+ token: token.jwt,
115
+ ttl: token.ttl,
116
+ };
144
117
  }
145
-
146
- const searchBody = {
147
- query: {
148
- bool: {
149
- filter: { bool: { must: { term: { userId: user._id } } } },
150
- must: isEmpty(query) ? { match_all: {} } : query,
151
- },
152
- },
153
- };
154
-
155
- const apiKeys = await ApiKey.search(searchBody, { from, size });
156
-
157
- return {
158
- hits: apiKeys.map((apiKey) => apiKey.serialize()),
159
- total: apiKeys.length,
160
- };
161
- }
162
-
163
- /**
164
- * Deletes an API key
165
- */
166
- async deleteApiKey(request) {
167
- const apiKeyId = request.getId();
168
- const refresh = request.getRefresh();
169
-
170
- const apiKey = await ApiKey.load(request.context.user._id, apiKeyId);
171
-
172
- await apiKey.delete({ refresh });
173
-
174
- return { _id: apiKeyId };
175
- }
176
-
177
- /**
178
- * Logs the current user out
179
- *
180
- * @param {KuzzleRequest} request
181
- * @returns {Promise<object>}
182
- */
183
- async logout(request) {
184
- if (
185
- !global.kuzzle.config.http.cookieAuthentication ||
186
- !request.getBoolean("cookieAuth")
187
- ) {
188
- this.assertIsAuthenticated(request);
118
+ /**
119
+ * Checks if an API action can be executed by the current user
120
+ */
121
+ async checkRights(request) {
122
+ const requestPayload = request.getBody();
123
+ if (typeof requestPayload.controller !== "string") {
124
+ throw kerror.get("api", "assert", "missing_argument", "body.controller");
125
+ }
126
+ if (typeof requestPayload.action !== "string") {
127
+ throw kerror.get("api", "assert", "missing_argument", "body.action");
128
+ }
129
+ const user = request.context.user;
130
+ const allowed = await user.isActionAllowed(new request_1.KuzzleRequest(requestPayload));
131
+ return {
132
+ allowed,
133
+ };
189
134
  }
190
-
191
- if (
192
- global.kuzzle.config.internal.notifiableProtocols.includes(
193
- request.context.connection.protocol
194
- )
195
- ) {
196
- // Unlink connection so the connection will not be notified when the token expires.
197
- global.kuzzle.tokenManager.unlink(
198
- request.context.token,
199
- request.context.connection.id
200
- );
135
+ /**
136
+ * Creates a new API key for the user
137
+ * @param {KuzzleRequest} request
138
+ */
139
+ async createApiKey(request) {
140
+ const expiresIn = request.input.args.expiresIn || -1;
141
+ const refresh = request.getRefresh("wait_for");
142
+ const apiKeyId = request.getId({ ifMissing: "generate" });
143
+ const description = request.getBodyString("description");
144
+ const user = request.context.user;
145
+ const apiKey = await apiKey_1.default.create(user, expiresIn, description, {
146
+ apiKeyId,
147
+ creatorId: user._id,
148
+ refresh,
149
+ });
150
+ return apiKey.serialize({ includeToken: true });
201
151
  }
202
-
203
- if (request.context.user._id !== this.anonymousId) {
204
- if (request.getBoolean("global")) {
205
- await global.kuzzle.ask(
206
- "core:security:token:deleteByKuid",
207
- request.getKuid(),
208
- { keepApiKeys: true }
209
- );
210
- } else if (
211
- request.context.token &&
212
- request.context.token.type !== "apiKey"
213
- ) {
214
- await global.kuzzle.ask(
215
- "core:security:token:delete",
216
- request.context.token
217
- );
218
- }
152
+ /**
153
+ * Search in the user API keys
154
+ */
155
+ async searchApiKeys(request) {
156
+ let query = request.getBody({});
157
+ const { from, size } = request.getSearchParams();
158
+ const lang = request.getLangParam();
159
+ const user = request.context.user;
160
+ if (lang === "koncorde") {
161
+ query = await this.translateKoncorde(query);
162
+ }
163
+ const searchBody = {
164
+ query: {
165
+ bool: {
166
+ filter: { bool: { must: { term: { userId: user._id } } } },
167
+ must: (0, lodash_1.isEmpty)(query) ? { match_all: {} } : query,
168
+ },
169
+ },
170
+ };
171
+ const apiKeys = await apiKey_1.default.search(searchBody, { from, size });
172
+ return {
173
+ hits: apiKeys.map((apiKey) => apiKey.serialize()),
174
+ total: apiKeys.length,
175
+ };
176
+ }
177
+ /**
178
+ * Deletes an API key
179
+ */
180
+ async deleteApiKey(request) {
181
+ const apiKeyId = request.getId();
182
+ const refresh = request.getRefresh();
183
+ const apiKey = await apiKey_1.default.load(request.context.user._id, apiKeyId);
184
+ await apiKey.delete({ refresh });
185
+ return { _id: apiKeyId };
219
186
  }
220
-
221
- if (
222
- global.kuzzle.config.http.cookieAuthentication &&
223
- request.getBoolean("cookieAuth")
224
- ) {
225
- request.response.configure({
226
- headers: {
227
- "Set-Cookie": Cookie.serialize("authToken", null, {
228
- httpOnly: true,
229
- path: "/",
230
- sameSite: "strict",
231
- }),
232
- },
233
- });
187
+ /**
188
+ * Logs the current user out
189
+ *
190
+ * @param {KuzzleRequest} request
191
+ * @returns {Promise<object>}
192
+ */
193
+ async logout(request) {
194
+ if (!global.kuzzle.config.http.cookieAuthentication ||
195
+ !request.getBoolean("cookieAuth")) {
196
+ this.assertIsAuthenticated(request);
197
+ }
198
+ if (global.kuzzle.config.internal.notifiableProtocols.includes(request.context.connection.protocol)) {
199
+ // Unlink connection so the connection will not be notified when the token expires.
200
+ global.kuzzle.tokenManager.unlink(request.context.token, request.context.connection.id);
201
+ }
202
+ if (request.context.user._id !== this.anonymousId) {
203
+ if (request.getBoolean("global")) {
204
+ await global.kuzzle.ask("core:security:token:deleteByKuid", request.getKuid(), { keepApiKeys: true });
205
+ }
206
+ else if (request.context.token &&
207
+ request.context.token.type !== "apiKey") {
208
+ await global.kuzzle.ask("core:security:token:delete", request.context.token);
209
+ }
210
+ }
211
+ if (global.kuzzle.config.http.cookieAuthentication &&
212
+ request.getBoolean("cookieAuth")) {
213
+ request.response.configure({
214
+ headers: {
215
+ "Set-Cookie": cookie_1.default.serialize("authToken", null, {
216
+ httpOnly: true,
217
+ path: "/",
218
+ sameSite: "strict",
219
+ }),
220
+ },
221
+ });
222
+ }
223
+ return { acknowledged: true };
224
+ }
225
+ // Used to send the Token using different ways when in cookieAuth mode. (DRY)
226
+ async _sendToken(token, request) {
227
+ // Only if the support of Browser Cookie as Authentication Token is enabled
228
+ // otherwise we should send a normal response because
229
+ // even if the SDK / Browser can handle the cookie,
230
+ // Kuzzle would not be capable of doing anything with it
231
+ if (global.kuzzle.config.http.cookieAuthentication &&
232
+ request.getBoolean("cookieAuth")) {
233
+ // Here we are not sending auth token when cookieAuth is set to true
234
+ // This allow us to detect if kuzzle does support cookie as auth token directly from the SDK
235
+ // or that the version of kuzzle doesn't support the feature Browser Cookie as Authentication Token
236
+ request.response.configure({
237
+ headers: {
238
+ "Set-Cookie": cookie_1.default.serialize("authToken", token.jwt, {
239
+ expires: new Date(token.expiresAt),
240
+ httpOnly: true,
241
+ path: "/",
242
+ sameSite: "strict",
243
+ }),
244
+ },
245
+ });
246
+ return {
247
+ _id: token.userId,
248
+ expiresAt: token.expiresAt,
249
+ ttl: token.ttl,
250
+ };
251
+ }
252
+ return {
253
+ _id: token.userId,
254
+ expiresAt: token.expiresAt,
255
+ jwt: token.jwt,
256
+ ttl: token.ttl,
257
+ };
258
+ }
259
+ /**
260
+ * Attempts a login with request informations against the provided strategy;
261
+ * local is used if strategy is not provided.
262
+ *
263
+ * @param {KuzzleRequest} request
264
+ * @returns {Promise<Token>}
265
+ */
266
+ async login(request) {
267
+ const strategy = request.getString("strategy");
268
+ const passportRequest = new http_1.IncomingMessage(null);
269
+ // Even in http, the url and the method are not pushed back to the request object
270
+ // set some arbitrary values to get a pseudo-valid object.
271
+ passportRequest.url = `/login?strategy=${strategy}`;
272
+ passportRequest.method = "POST";
273
+ passportRequest.httpVersion = "1.1";
274
+ passportRequest.httpVersionMajor = 1;
275
+ passportRequest.httpVersionMinor = 1;
276
+ passportRequest.body = request.input.body;
277
+ passportRequest.query = request.input.args;
278
+ passportRequest.headers = Object.assign({}, request.input.headers);
279
+ for (const h of Object.keys(passportRequest.headers)) {
280
+ passportRequest.rawHeaders.push(h);
281
+ passportRequest.rawHeaders.push(passportRequest.headers[h]);
282
+ }
283
+ passportRequest.original = request;
284
+ if (!(0, safeObject_1.has)(global.kuzzle.pluginsManager.strategies, strategy)) {
285
+ throw kerror.get("security", "credentials", "unknown_strategy", strategy);
286
+ }
287
+ const content = await global.kuzzle.passport.authenticate(passportRequest, strategy);
288
+ // do not trigger the "auth:strategyAutenticated" pipe if the result is
289
+ // not a User object, i.e. if we are a intermediate step of a multi-step
290
+ // authentication strategy
291
+ // (example: first redirection call for oAuth strategies)
292
+ const authResponse = !(content instanceof user_1.User)
293
+ ? { content, strategy }
294
+ : await this.pipe("auth:strategyAuthenticated", { content, strategy });
295
+ if (!(authResponse.content instanceof user_1.User)) {
296
+ request.response.configure({
297
+ headers: authResponse.content.headers,
298
+ status: authResponse.content.statusCode || 200,
299
+ });
300
+ return authResponse.content;
301
+ }
302
+ const options = {};
303
+ if (request.input.args.expiresIn) {
304
+ options.expiresIn = request.input.args.expiresIn;
305
+ }
306
+ const existingToken = global.kuzzle.tokenManager.getConnectedUserToken(authResponse.content._id, request.context.connection.id);
307
+ /**
308
+ * If a previous token from the same User is linked to this connection
309
+ * and the token is either an API Key or an infinite duration token
310
+ * we dont need to create a new token or refresh anything, just send back the exact same token
311
+ * to avoid breaking changes.
312
+ */
313
+ if (existingToken &&
314
+ (existingToken.type === "apiKey" || existingToken.ttl < 0)) {
315
+ return this._sendToken(existingToken, request);
316
+ }
317
+ const token = await this.ask("core:security:token:create", authResponse.content, options);
318
+ if (existingToken) {
319
+ global.kuzzle.tokenManager.refresh(existingToken, token);
320
+ }
321
+ if (global.kuzzle.config.internal.notifiableProtocols.includes(request.context.connection.protocol)) {
322
+ // Link the connection with the token, this way the connection can be notified when the token has expired.
323
+ global.kuzzle.tokenManager.link(token, request.context.connection.id);
324
+ }
325
+ return this._sendToken(token, request);
234
326
  }
235
-
236
- return { acknowledged: true };
237
- }
238
-
239
- // Used to send the Token using different ways when in cookieAuth mode. (DRY)
240
- async _sendToken(token, request) {
241
- // Only if the support of Browser Cookie as Authentication Token is enabled
242
- // otherwise we should send a normal response because
243
- // even if the SDK / Browser can handle the cookie,
244
- // Kuzzle would not be capable of doing anything with it
245
- if (
246
- global.kuzzle.config.http.cookieAuthentication &&
247
- request.getBoolean("cookieAuth")
248
- ) {
249
- // Here we are not sending auth token when cookieAuth is set to true
250
- // This allow us to detect if kuzzle does support cookie as auth token directly from the SDK
251
- // or that the version of kuzzle doesn't support the feature Browser Cookie as Authentication Token
252
-
253
- request.response.configure({
254
- headers: {
255
- "Set-Cookie": Cookie.serialize("authToken", token.jwt, {
256
- expires: new Date(token.expiresAt),
257
- httpOnly: true,
258
- path: "/",
259
- sameSite: "strict",
260
- }),
261
- },
262
- });
263
-
264
- return {
265
- _id: token.userId,
266
- expiresAt: token.expiresAt,
267
- ttl: token.ttl,
268
- };
327
+ /**
328
+ * Returns the user identified by the given jwt token
329
+ *
330
+ * @param {KuzzleRequest} request
331
+ * @returns {Promise<Object>}
332
+ */
333
+ getCurrentUser(request) {
334
+ const userId = request.context.token.userId, formattedUser = formatProcessing_1.default.serializeUser(request.context.user), promises = [];
335
+ if (this.anonymousId === userId) {
336
+ promises.push(bluebird_1.default.resolve([]));
337
+ }
338
+ else {
339
+ for (const strategy of global.kuzzle.pluginsManager.listStrategies()) {
340
+ const existsMethod = global.kuzzle.pluginsManager.getStrategyMethod(strategy, "exists");
341
+ promises.push(existsMethod(request, userId, strategy)
342
+ .then((exists) => (exists ? strategy : null))
343
+ .catch((err) => wrapPluginError(err)));
344
+ }
345
+ }
346
+ return bluebird_1.default.all(promises).then((strategies) => {
347
+ if (strategies.length > 0) {
348
+ formattedUser.strategies = strategies.filter((item) => item !== null);
349
+ }
350
+ return formattedUser;
351
+ });
269
352
  }
270
-
271
- return {
272
- _id: token.userId,
273
- expiresAt: token.expiresAt,
274
- jwt: token.jwt,
275
- ttl: token.ttl,
276
- };
277
- }
278
-
279
- /**
280
- * Attempts a login with request informations against the provided strategy;
281
- * local is used if strategy is not provided.
282
- *
283
- * @param {KuzzleRequest} request
284
- * @returns {Promise<Token>}
285
- */
286
- async login(request) {
287
- const strategy = request.getString("strategy");
288
- const passportRequest = new IncomingMessage();
289
-
290
- // Even in http, the url and the method are not pushed back to the request object
291
- // set some arbitrary values to get a pseudo-valid object.
292
- passportRequest.url = `/login?strategy=${strategy}`;
293
- passportRequest.method = "POST";
294
- passportRequest.httpVersion = "1.1";
295
- passportRequest.httpVersionMajor = 1;
296
- passportRequest.httpVersionMinor = 1;
297
- passportRequest.body = request.input.body;
298
- passportRequest.query = request.input.args;
299
- passportRequest.headers = Object.assign({}, request.input.headers);
300
-
301
- for (const h of Object.keys(passportRequest.headers)) {
302
- passportRequest.rawHeaders.push(h);
303
- passportRequest.rawHeaders.push(passportRequest.headers[h]);
353
+ /**
354
+ * Returns the rights of the user identified by the given jwt token
355
+ *
356
+ * @param {KuzzleRequest} request
357
+ * @returns {Promise<object>}
358
+ */
359
+ getMyRights(request) {
360
+ return request.context.user
361
+ .getRights(global.kuzzle)
362
+ .then((rights) => Object.keys(rights).reduce((array, item) => array.concat(rights[item]), []))
363
+ .then((rights) => ({ hits: rights, total: rights.length }));
304
364
  }
305
- passportRequest.original = request;
306
-
307
- if (!has(global.kuzzle.pluginsManager.strategies, strategy)) {
308
- throw kerror.get("security", "credentials", "unknown_strategy", strategy);
365
+ /**
366
+ * Checks the validity of a token.
367
+ *
368
+ * @param {KuzzleRequest} request
369
+ * @returns {Promise<object>}
370
+ */
371
+ async checkToken(request) {
372
+ let token = "";
373
+ if (global.kuzzle.config.http.cookieAuthentication &&
374
+ request.getBoolean("cookieAuth")) {
375
+ token = request.input.jwt;
376
+ }
377
+ else {
378
+ token = request.getBodyString("token", "") || null;
379
+ }
380
+ try {
381
+ const { expiresAt = -1, userId } = await this.ask("core:security:token:verify", token);
382
+ return { expiresAt, kuid: userId, valid: true };
383
+ }
384
+ catch (error) {
385
+ if (error.status === 401) {
386
+ return { state: error.message, valid: false };
387
+ }
388
+ throw error;
389
+ }
309
390
  }
310
-
311
- const content = await global.kuzzle.passport.authenticate(
312
- passportRequest,
313
- strategy
314
- );
315
-
316
- // do not trigger the "auth:strategyAutenticated" pipe if the result is
317
- // not a User object, i.e. if we are a intermediate step of a multi-step
318
- // authentication strategy
319
- // (example: first redirection call for oAuth strategies)
320
- const authResponse = !(content instanceof User)
321
- ? { content, strategy }
322
- : await this.pipe("auth:strategyAuthenticated", { content, strategy });
323
-
324
- if (!(authResponse.content instanceof User)) {
325
- request.response.configure({
326
- headers: authResponse.content.headers,
327
- status: authResponse.content.statusCode || 200,
328
- });
329
-
330
- return authResponse.content;
391
+ /**
392
+ * Updates the current user if it is not anonymous
393
+ *
394
+ * @param {KuzzleRequest} request
395
+ * @returns {Promise<object>}
396
+ */
397
+ async updateSelf(request) {
398
+ this.assertIsAuthenticated(request);
399
+ this.assertBodyHasNotAttributes(request, "_id", "profileIds");
400
+ const userId = request.getKuid();
401
+ const body = request.getBody();
402
+ const user = await this.ask("core:security:user:update", userId, null, body, {
403
+ refresh: request.getRefresh("wait_for"),
404
+ retryOnConflict: request.getInteger("retryOnConflict", 10),
405
+ userId,
406
+ });
407
+ global.kuzzle.log.info(`[SECURITY] ${securityController_1.default.userOrSdk(userId)} applied action "${request.input.action}" on user "${userId}."`);
408
+ return formatProcessing_1.default.serializeUser(user);
331
409
  }
332
-
333
- const options = {};
334
- if (request.input.args.expiresIn) {
335
- options.expiresIn = request.input.args.expiresIn;
410
+ /**
411
+ * List authentication strategies
412
+ *
413
+ * @returns {Promise.<string[]>}
414
+ */
415
+ getStrategies() {
416
+ return bluebird_1.default.resolve(global.kuzzle.pluginsManager.listStrategies());
336
417
  }
337
-
338
- const existingToken = global.kuzzle.tokenManager.getConnectedUserToken(
339
- authResponse.content._id,
340
- request.context.connection.id
341
- );
342
-
343
418
  /**
344
- * If a previous token from the same User is linked to this connection
345
- * and the token is either an API Key or an infinite duration token
346
- * we dont need to create a new token or refresh anything, just send back the exact same token
347
- * to avoid breaking changes.
419
+ * @param {KuzzleRequest} request
420
+ * @returns {Promise.<Object>}
348
421
  */
349
- if (
350
- existingToken &&
351
- (existingToken.type === "apiKey" || existingToken.ttl < 0)
352
- ) {
353
- return this._sendToken(existingToken, request);
422
+ createMyCredentials(request) {
423
+ this.assertIsAuthenticated(request);
424
+ const userId = request.getKuid(), strategy = request.getString("strategy"), credentials = request.getBody();
425
+ this.assertIsStrategyRegistered(strategy);
426
+ const createMethod = global.kuzzle.pluginsManager.getStrategyMethod(strategy, "create"), validateMethod = global.kuzzle.pluginsManager.getStrategyMethod(strategy, "validate");
427
+ return validateMethod(request, credentials, userId, strategy, false)
428
+ .then(() => createMethod(request, credentials, userId, strategy))
429
+ .catch((err) => wrapPluginError(err));
354
430
  }
355
-
356
- const token = await this.ask(
357
- "core:security:token:create",
358
- authResponse.content,
359
- options
360
- );
361
-
362
- if (existingToken) {
363
- global.kuzzle.tokenManager.refresh(existingToken, token);
431
+ /**
432
+ * @param {KuzzleRequest} request
433
+ * @returns {Promise.<Object>}
434
+ */
435
+ updateMyCredentials(request) {
436
+ this.assertIsAuthenticated(request);
437
+ const userId = request.getKuid(), strategy = request.getString("strategy"), credentials = request.getBody();
438
+ this.assertIsStrategyRegistered(strategy);
439
+ const updateMethod = global.kuzzle.pluginsManager.getStrategyMethod(request.input.args.strategy, "update"), validateMethod = global.kuzzle.pluginsManager.getStrategyMethod(request.input.args.strategy, "validate");
440
+ return validateMethod(request, credentials, userId, strategy, true)
441
+ .then(() => updateMethod(request, credentials, userId, strategy))
442
+ .catch((err) => wrapPluginError(err));
364
443
  }
365
-
366
- if (
367
- global.kuzzle.config.internal.notifiableProtocols.includes(
368
- request.context.connection.protocol
369
- )
370
- ) {
371
- // Link the connection with the token, this way the connection can be notified when the token has expired.
372
- global.kuzzle.tokenManager.link(token, request.context.connection.id);
444
+ /**
445
+ * @param {KuzzleRequest} request
446
+ * @returns {Promise.<Object>}
447
+ */
448
+ credentialsExist(request) {
449
+ this.assertIsAuthenticated(request);
450
+ const userId = request.getKuid(), strategy = request.getString("strategy");
451
+ this.assertIsStrategyRegistered(strategy);
452
+ const existsMethod = global.kuzzle.pluginsManager.getStrategyMethod(strategy, "exists");
453
+ return existsMethod(request, userId, strategy).catch((err) => wrapPluginError(err));
373
454
  }
374
-
375
- return this._sendToken(token, request);
376
- }
377
-
378
- /**
379
- * Returns the user identified by the given jwt token
380
- *
381
- * @param {KuzzleRequest} request
382
- * @returns {Promise<Object>}
383
- */
384
- getCurrentUser(request) {
385
- const userId = request.context.token.userId,
386
- formattedUser = formatProcessing.serializeUser(request.context.user),
387
- promises = [];
388
-
389
- if (this.anonymousId === userId) {
390
- promises.push(Bluebird.resolve([]));
391
- } else {
392
- for (const strategy of global.kuzzle.pluginsManager.listStrategies()) {
393
- const existsMethod = global.kuzzle.pluginsManager.getStrategyMethod(
394
- strategy,
395
- "exists"
396
- );
397
-
398
- promises.push(
399
- existsMethod(request, userId, strategy)
400
- .then((exists) => (exists ? strategy : null))
401
- .catch((err) => wrapPluginError(err))
402
- );
403
- }
455
+ /**
456
+ * @param {KuzzleRequest} request
457
+ * @returns {Promise.<Object>}
458
+ */
459
+ validateMyCredentials(request) {
460
+ this.assertIsAuthenticated(request);
461
+ const userId = request.getKuid(), strategy = request.getString("strategy"), credentials = request.getBody();
462
+ this.assertIsStrategyRegistered(strategy);
463
+ const validateMethod = global.kuzzle.pluginsManager.getStrategyMethod(strategy, "validate");
464
+ return validateMethod(request, credentials, userId, strategy, false).catch((err) => wrapPluginError(err));
404
465
  }
405
-
406
- return Bluebird.all(promises).then((strategies) => {
407
- if (strategies.length > 0) {
408
- formattedUser.strategies = strategies.filter((item) => item !== null);
409
- }
410
-
411
- return formattedUser;
412
- });
413
- }
414
-
415
- /**
416
- * Returns the rights of the user identified by the given jwt token
417
- *
418
- * @param {KuzzleRequest} request
419
- * @returns {Promise<object>}
420
- */
421
- getMyRights(request) {
422
- return request.context.user
423
- .getRights(global.kuzzle)
424
- .then((rights) =>
425
- Object.keys(rights).reduce(
426
- (array, item) => array.concat(rights[item]),
427
- []
428
- )
429
- )
430
- .then((rights) => ({ hits: rights, total: rights.length }));
431
- }
432
-
433
- /**
434
- * Checks the validity of a token.
435
- *
436
- * @param {KuzzleRequest} request
437
- * @returns {Promise<object>}
438
- */
439
- async checkToken(request) {
440
- let token = "";
441
-
442
- if (
443
- global.kuzzle.config.http.cookieAuthentication &&
444
- request.getBoolean("cookieAuth")
445
- ) {
446
- token = request.input.jwt;
447
- } else {
448
- token = request.getBodyString("token", "") || null;
466
+ /**
467
+ * @param {KuzzleRequest} request
468
+ * @returns {Promise.<Object>}
469
+ */
470
+ deleteMyCredentials(request) {
471
+ this.assertIsAuthenticated(request);
472
+ const userId = request.getKuid(), strategy = request.getString("strategy");
473
+ this.assertIsStrategyRegistered(strategy);
474
+ const deleteMethod = global.kuzzle.pluginsManager.getStrategyMethod(strategy, "delete");
475
+ return deleteMethod(request, userId, strategy)
476
+ .then(() => ({ acknowledged: true }))
477
+ .catch((err) => wrapPluginError(err));
449
478
  }
450
-
451
- try {
452
- const { expiresAt = -1, userId } = await this.ask(
453
- "core:security:token:verify",
454
- token
455
- );
456
-
457
- return { expiresAt, kuid: userId, valid: true };
458
- } catch (error) {
459
- if (error.status === 401) {
460
- return { state: error.message, valid: false };
461
- }
462
-
463
- throw error;
479
+ /**
480
+ * @param {KuzzleRequest} request
481
+ * @returns {Promise.<Object>}
482
+ */
483
+ getMyCredentials(request) {
484
+ this.assertIsAuthenticated(request);
485
+ const userId = request.getKuid(), strategy = request.getString("strategy");
486
+ this.assertIsStrategyRegistered(strategy);
487
+ if (!global.kuzzle.pluginsManager.hasStrategyMethod(strategy, "getInfo")) {
488
+ return bluebird_1.default.resolve({});
489
+ }
490
+ const getInfoMethod = global.kuzzle.pluginsManager.getStrategyMethod(strategy, "getInfo");
491
+ return getInfoMethod(request, userId, strategy).catch((err) => wrapPluginError(err));
464
492
  }
465
- }
466
-
467
- /**
468
- * Updates the current user if it is not anonymous
469
- *
470
- * @param {KuzzleRequest} request
471
- * @returns {Promise<object>}
472
- */
473
- async updateSelf(request) {
474
- this.assertIsAuthenticated(request);
475
- this.assertBodyHasNotAttributes(request, "_id", "profileIds");
476
-
477
- const userId = request.getKuid();
478
- const body = request.getBody();
479
-
480
- const user = await this.ask(
481
- "core:security:user:update",
482
- userId,
483
- null,
484
- body,
485
- {
486
- refresh: request.getRefresh("wait_for"),
487
- retryOnConflict: request.getInteger("retryOnConflict", 10),
488
- userId,
489
- }
490
- );
491
-
492
- global.kuzzle.log.info(
493
- `[SECURITY] ${SecurityController.userOrSdk(userId)} applied action "${
494
- request.input.action
495
- }" on user "${userId}."`
496
- );
497
-
498
- return formatProcessing.serializeUser(user);
499
- }
500
-
501
- /**
502
- * List authentication strategies
503
- *
504
- * @returns {Promise.<string[]>}
505
- */
506
- getStrategies() {
507
- return Bluebird.resolve(global.kuzzle.pluginsManager.listStrategies());
508
- }
509
-
510
- /**
511
- * @param {KuzzleRequest} request
512
- * @returns {Promise.<Object>}
513
- */
514
- createMyCredentials(request) {
515
- this.assertIsAuthenticated(request);
516
-
517
- const userId = request.getKuid(),
518
- strategy = request.getString("strategy"),
519
- credentials = request.getBody();
520
-
521
- this.assertIsStrategyRegistered(strategy);
522
-
523
- const createMethod = global.kuzzle.pluginsManager.getStrategyMethod(
524
- strategy,
525
- "create"
526
- ),
527
- validateMethod = global.kuzzle.pluginsManager.getStrategyMethod(
528
- strategy,
529
- "validate"
530
- );
531
-
532
- return validateMethod(request, credentials, userId, strategy, false)
533
- .then(() => createMethod(request, credentials, userId, strategy))
534
- .catch((err) => wrapPluginError(err));
535
- }
536
-
537
- /**
538
- * @param {KuzzleRequest} request
539
- * @returns {Promise.<Object>}
540
- */
541
- updateMyCredentials(request) {
542
- this.assertIsAuthenticated(request);
543
-
544
- const userId = request.getKuid(),
545
- strategy = request.getString("strategy"),
546
- credentials = request.getBody();
547
-
548
- this.assertIsStrategyRegistered(strategy);
549
-
550
- const updateMethod = global.kuzzle.pluginsManager.getStrategyMethod(
551
- request.input.args.strategy,
552
- "update"
553
- ),
554
- validateMethod = global.kuzzle.pluginsManager.getStrategyMethod(
555
- request.input.args.strategy,
556
- "validate"
557
- );
558
-
559
- return validateMethod(request, credentials, userId, strategy, true)
560
- .then(() => updateMethod(request, credentials, userId, strategy))
561
- .catch((err) => wrapPluginError(err));
562
- }
563
-
564
- /**
565
- * @param {KuzzleRequest} request
566
- * @returns {Promise.<Object>}
567
- */
568
- credentialsExist(request) {
569
- this.assertIsAuthenticated(request);
570
-
571
- const userId = request.getKuid(),
572
- strategy = request.getString("strategy");
573
-
574
- this.assertIsStrategyRegistered(strategy);
575
-
576
- const existsMethod = global.kuzzle.pluginsManager.getStrategyMethod(
577
- strategy,
578
- "exists"
579
- );
580
-
581
- return existsMethod(request, userId, strategy).catch((err) =>
582
- wrapPluginError(err)
583
- );
584
- }
585
-
586
- /**
587
- * @param {KuzzleRequest} request
588
- * @returns {Promise.<Object>}
589
- */
590
- validateMyCredentials(request) {
591
- this.assertIsAuthenticated(request);
592
-
593
- const userId = request.getKuid(),
594
- strategy = request.getString("strategy"),
595
- credentials = request.getBody();
596
-
597
- this.assertIsStrategyRegistered(strategy);
598
-
599
- const validateMethod = global.kuzzle.pluginsManager.getStrategyMethod(
600
- strategy,
601
- "validate"
602
- );
603
-
604
- return validateMethod(request, credentials, userId, strategy, false).catch(
605
- (err) => wrapPluginError(err)
606
- );
607
- }
608
-
609
- /**
610
- * @param {KuzzleRequest} request
611
- * @returns {Promise.<Object>}
612
- */
613
- deleteMyCredentials(request) {
614
- this.assertIsAuthenticated(request);
615
-
616
- const userId = request.getKuid(),
617
- strategy = request.getString("strategy");
618
-
619
- this.assertIsStrategyRegistered(strategy);
620
-
621
- const deleteMethod = global.kuzzle.pluginsManager.getStrategyMethod(
622
- strategy,
623
- "delete"
624
- );
625
-
626
- return deleteMethod(request, userId, strategy)
627
- .then(() => ({ acknowledged: true }))
628
- .catch((err) => wrapPluginError(err));
629
- }
630
-
631
- /**
632
- * @param {KuzzleRequest} request
633
- * @returns {Promise.<Object>}
634
- */
635
- getMyCredentials(request) {
636
- this.assertIsAuthenticated(request);
637
-
638
- const userId = request.getKuid(),
639
- strategy = request.getString("strategy");
640
-
641
- this.assertIsStrategyRegistered(strategy);
642
-
643
- if (!global.kuzzle.pluginsManager.hasStrategyMethod(strategy, "getInfo")) {
644
- return Bluebird.resolve({});
493
+ /**
494
+ * @param {KuzzleRequest} request
495
+ */
496
+ async refreshToken(request) {
497
+ this.assertIsAuthenticated(request);
498
+ const token = await this.ask("core:security:token:refresh", request.context.user, request.context.token, request.input.args.expiresIn);
499
+ return this._sendToken(token, request);
645
500
  }
646
-
647
- const getInfoMethod = global.kuzzle.pluginsManager.getStrategyMethod(
648
- strategy,
649
- "getInfo"
650
- );
651
-
652
- return getInfoMethod(request, userId, strategy).catch((err) =>
653
- wrapPluginError(err)
654
- );
655
- }
656
-
657
- /**
658
- * @param {KuzzleRequest} request
659
- */
660
- async refreshToken(request) {
661
- this.assertIsAuthenticated(request);
662
-
663
- const token = await this.ask(
664
- "core:security:token:refresh",
665
- request.context.user,
666
- request.context.token,
667
- request.input.args.expiresIn
668
- );
669
-
670
- return this._sendToken(token, request);
671
- }
672
-
673
- assertIsAuthenticated(request) {
674
- if (request.context.user._id === this.anonymousId) {
675
- throw kerror.get(
676
- "security",
677
- "rights",
678
- "unauthorized",
679
- request.input.controller,
680
- request.input.action
681
- );
501
+ assertIsAuthenticated(request) {
502
+ if (request.context.user._id === this.anonymousId) {
503
+ throw kerror.get("security", "rights", "unauthorized", request.input.controller, request.input.action);
504
+ }
682
505
  }
683
- }
684
506
  }
685
-
507
+ exports.AuthController = AuthController;
686
508
  function wrapPluginError(error) {
687
- if (!(error instanceof KuzzleError)) {
688
- throw kerror.getFrom(
689
- error,
690
- "plugin",
691
- "runtime",
692
- "unexpected_error",
693
- error.message
694
- );
695
- }
696
-
697
- throw error;
509
+ if (!(error instanceof errors_1.KuzzleError)) {
510
+ throw kerror.getFrom(error, "plugin", "runtime", "unexpected_error", error.message);
511
+ }
512
+ throw error;
698
513
  }
699
-
700
514
  module.exports = AuthController;
515
+ //# sourceMappingURL=authController.js.map