kuzzle 2.14.0 → 2.14.5

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 (36) hide show
  1. package/lib/api/controllers/securityController.js +49 -13
  2. package/lib/api/funnel.js +13 -24
  3. package/lib/api/openApiGenerator.js +6 -0
  4. package/lib/api/request/kuzzleRequest.js +9 -9
  5. package/lib/config/default.config.js +10 -1
  6. package/lib/core/auth/tokenManager.d.ts +65 -0
  7. package/lib/core/auth/tokenManager.js +222 -256
  8. package/lib/core/backend/backend.js +3 -4
  9. package/lib/core/backend/backendController.d.ts +27 -0
  10. package/lib/core/backend/backendController.js +27 -0
  11. package/lib/core/backend/backendImport.js +6 -6
  12. package/lib/core/backend/backendPlugin.js +1 -1
  13. package/lib/core/plugin/plugin.js +42 -2
  14. package/lib/core/plugin/pluginContext.js +2 -2
  15. package/lib/core/plugin/pluginsManager.js +1 -0
  16. package/lib/core/realtime/hotelClerk.js +30 -16
  17. package/lib/core/security/profileRepository.js +5 -22
  18. package/lib/core/security/roleRepository.js +11 -5
  19. package/lib/core/security/tokenRepository.js +1 -1
  20. package/lib/core/security/userRepository.js +2 -2
  21. package/lib/core/shared/KoncordeWrapper.d.ts +1 -2
  22. package/lib/core/shared/KoncordeWrapper.js +10 -9
  23. package/lib/core/shared/repository.js +3 -3
  24. package/lib/core/shared/sdk/embeddedSdk.d.ts +2 -2
  25. package/lib/core/shared/sdk/embeddedSdk.js +2 -2
  26. package/lib/kerror/codes/4-plugin.json +6 -0
  27. package/lib/kuzzle/kuzzle.js +1 -1
  28. package/lib/model/security/token.d.ts +29 -0
  29. package/lib/model/security/token.js +25 -24
  30. package/lib/service/storage/elasticsearch.js +23 -5
  31. package/lib/types/ControllerDefinition.d.ts +51 -1
  32. package/lib/types/Plugin.js +1 -1
  33. package/lib/util/koncordeCompat.js +1 -1
  34. package/lib/util/mutex.js +2 -2
  35. package/package-lock.json +559 -1889
  36. package/package.json +27 -34
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /*
2
3
  * Kuzzle, a backend software, self-hostable and ready to use
3
4
  * to power modern apps
@@ -18,13 +19,29 @@
18
19
  * See the License for the specific language governing permissions and
19
20
  * limitations under the License.
20
21
  */
21
-
22
- 'use strict';
23
-
24
- const SortedArray = require('sorted-array');
25
-
26
- const Token = require('../../model/security/token');
27
-
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.TokenManager = void 0;
27
+ const sorted_array_1 = __importDefault(require("sorted-array"));
28
+ require("../../types/Global");
29
+ const token_1 = require("../../model/security/token");
30
+ /**
31
+ * Extends the Token model with a set of linked connection IDs.
32
+ */
33
+ class ManagedToken extends token_1.Token {
34
+ constructor(token, connectionIds) {
35
+ super(token);
36
+ this.connectionIds = connectionIds;
37
+ }
38
+ /**
39
+ * Returns an unique string that identify a token and allows to sort them by expiration date.
40
+ */
41
+ static indexFor(token) {
42
+ return `${token.expiresAt};${token._id}`;
43
+ }
44
+ }
28
45
  /*
29
46
  Maximum delay of a setTimeout call. If larger than this value,
30
47
  it's replaced by 1 (see setTimeout documentation)
@@ -34,270 +51,219 @@ const Token = require('../../model/security/token');
34
51
  Until this constant is exposed in NodeJS' API, we have to manually set it.
35
52
  */
36
53
  const TIMEOUT_MAX = Math.pow(2, 31) - 1;
37
-
38
54
  /**
39
- * Maintains a list of valid tokens used by real-time subscriptions
40
- * When a token expires, this module cleans up the corresponding connection's
41
- * subscriptions, and notify the user
55
+ * Maintains a list of valid tokens used by connected protocols.
42
56
  *
43
- * @class TokenManager
57
+ * When a token expires, this module cleans up the corresponding connection's
58
+ * subscriptions if any, and notify the user
44
59
  */
45
60
  class TokenManager {
46
- constructor () {
47
- this.anonymousUserId = null;
48
-
49
- /*
50
- * Tokens are sorted by their expiration date
51
- *
52
- * The token id is added to the key to handle
53
- * equality between different tokens sharing
54
- * the exact same expiration date
55
- *
56
- * We should always put infinite duration token at the end of the array
57
- * because the loop that checks if a token is expired is always verifiying the first element of the array.
58
- * Since an infinite token cannot be expired, if there is an infinite duration token at the first element
59
- * the loop will verify the same token over and over again because the token cannot be removed from the queue
60
- * and the other tokens will never be verifier.
61
- */
62
- this.tokens = new SortedArray([], (a, b) => {
63
- if (a.idx === b.idx) {
64
- return 0;
65
- }
66
-
67
- if (a.idx && a.idx[0] === '-') {
68
- return 1;
69
- }
70
-
71
- if (b.idx && b.idx[0] === '-') {
72
- return -1;
73
- }
74
-
75
- return a.idx < b.idx ? -1 : 1;
76
- });
77
- this.tokensByConnection = new Map();
78
-
79
- this.timer = null;
80
- }
81
-
82
- async init () {
83
- const anonymous = await global.kuzzle.ask('core:security:user:anonymous:get');
84
- this.anonymousUserId = anonymous._id;
85
- }
86
-
87
- runTimer () {
88
- if (this.tokens.array.length > 0) {
89
- const delay = Math.min(this.tokens.array[0].expiresAt - Date.now(), TIMEOUT_MAX);
90
-
91
- if (this.timer) {
92
- clearTimeout(this.timer);
93
- }
94
-
95
- this.timer = setTimeout(this.checkTokensValidity.bind(this), delay);
61
+ constructor() {
62
+ this.anonymousUserId = null;
63
+ this.tokensByConnection = new Map();
64
+ this.timer = null;
65
+ /*
66
+ * Tokens are sorted by their expiration date
67
+ *
68
+ * The token id is added to the key to handle
69
+ * equality between different tokens sharing
70
+ * the exact same expiration date
71
+ *
72
+ * We should always put infinite duration token at the end of the array
73
+ * because the loop that checks if a token is expired is always verifiying the first element of the array.
74
+ * Since an infinite token cannot be expired, if there is an infinite duration token at the first element
75
+ * the loop will verify the same token over and over again because the token cannot be removed from the queue
76
+ * and the other tokens will never be verifier.
77
+ */
78
+ this.tokens = new sorted_array_1.default([], (a, b) => {
79
+ if (a.idx === b.idx) {
80
+ return 0;
81
+ }
82
+ if (a.idx && a.idx[0] === '-') {
83
+ return 1;
84
+ }
85
+ if (b.idx && b.idx[0] === '-') {
86
+ return -1;
87
+ }
88
+ return a.idx < b.idx ? -1 : 1;
89
+ });
96
90
  }
97
- }
98
-
99
- /**
100
- * Link a connection and a token.
101
- * If one or another expires, associated subscriptions are cleaned up
102
- * @param {Token} token
103
- * @param {String} connectionId
104
- */
105
- link (token, connectionId) {
106
- // Embedded SDK does not use tokens
107
- if (! token || token._id === this.anonymousUserId) {
108
- return;
109
- }
110
-
111
- const idx = getTokenIndex(token);
112
- const currentToken = this.tokensByConnection.get(connectionId);
113
-
114
- if (currentToken) {
115
- if (currentToken._id === token._id) {
116
- return; // Connection and Token already linked
117
- }
118
- this._removeConnectionLinkedToToken(connectionId, currentToken);
119
- }
120
- const pos = this.tokens.search({idx});
121
-
122
- if (pos === -1) {
123
- this._add(token, [connectionId]);
91
+ async init() {
92
+ const anonymous = await global.kuzzle.ask('core:security:user:anonymous:get');
93
+ this.anonymousUserId = anonymous._id;
124
94
  }
125
- else {
126
- const data = this.tokens.array[pos];
127
- data.connectionIds.add(connectionId);
128
- this.tokensByConnection.set(connectionId, data);
95
+ runTimer() {
96
+ if (this.tokens.array.length > 0) {
97
+ const delay = Math.min(this.tokens.array[0].expiresAt - Date.now(), TIMEOUT_MAX);
98
+ if (this.timer) {
99
+ clearTimeout(this.timer);
100
+ }
101
+ this.timer = setTimeout(this.checkTokensValidity.bind(this), delay);
102
+ }
129
103
  }
130
- }
131
-
132
- /**
133
- * Unlink a connection from its associated token
134
- *
135
- * @param {Token} token
136
- * @param {String} connectionId
137
- */
138
- unlink (token, connectionId) {
139
- // Embedded SDK does not use tokens
140
- if (! token || token._id === this.anonymousUserId) {
141
- return;
142
- }
143
-
144
- const idx = getTokenIndex(token);
145
- const pos = this.tokens.search({ idx });
146
-
147
- if (pos === -1) {
148
- return;
149
- }
150
-
151
- this._removeConnectionLinkedToToken(connectionId, this.tokens.array[pos]);
152
-
153
- const currentToken = this.tokensByConnection.get(connectionId);
154
- if (currentToken && currentToken._id === token._id) {
155
- this.tokensByConnection.delete(connectionId);
104
+ /**
105
+ * Link a connection and a token.
106
+ * If one or another expires, associated subscriptions are cleaned up
107
+ * @param token
108
+ * @param connectionId
109
+ */
110
+ link(token, connectionId) {
111
+ // Embedded SDK does not use tokens
112
+ if (!token || token._id === this.anonymousUserId) {
113
+ return;
114
+ }
115
+ const idx = ManagedToken.indexFor(token);
116
+ const currentToken = this.tokensByConnection.get(connectionId);
117
+ if (currentToken) {
118
+ if (currentToken._id === token._id) {
119
+ return; // Connection and Token already linked
120
+ }
121
+ this.removeConnectionLinkedToToken(connectionId, currentToken);
122
+ }
123
+ const pos = this.tokens.search({ idx });
124
+ if (pos === -1) {
125
+ this.add(token, new Set([connectionId]));
126
+ }
127
+ else {
128
+ const managedToken = this.tokens.array[pos];
129
+ managedToken.connectionIds.add(connectionId);
130
+ this.tokensByConnection.set(connectionId, managedToken);
131
+ }
156
132
  }
157
- }
158
-
159
- /**
160
- * Called when a token expires before its time (e.g. following a
161
- * auth:logout action)
162
- * This method removes all maintained links and cleans up the
163
- * hotel clerk
164
- *
165
- * @param token
166
- */
167
- async expire (token) {
168
- if (token._id === this.anonymousUserId) {
169
- return;
133
+ /**
134
+ * Unlink a connection from its associated token
135
+ *
136
+ * @param token
137
+ * @param connectionId
138
+ */
139
+ unlink(token, connectionId) {
140
+ // Embedded SDK does not use tokens
141
+ if (!token || token._id === this.anonymousUserId) {
142
+ return;
143
+ }
144
+ const idx = ManagedToken.indexFor(token);
145
+ const pos = this.tokens.search({ idx });
146
+ if (pos === -1) {
147
+ return;
148
+ }
149
+ this.removeConnectionLinkedToToken(connectionId, this.tokens.array[pos]);
150
+ const currentToken = this.tokensByConnection.get(connectionId);
151
+ if (currentToken && currentToken._id === token._id) {
152
+ this.tokensByConnection.delete(connectionId);
153
+ }
170
154
  }
171
-
172
- const idx = getTokenIndex(token);
173
- const searchResult = this.tokens.search({idx});
174
-
175
- if (searchResult > -1) {
176
- const data = this.tokens.array[searchResult];
177
-
178
- for (const connectionId of data.connectionIds) {
179
- this.tokensByConnection.delete(connectionId);
180
- await global.kuzzle.ask('core:realtime:user:remove', connectionId);
181
- }
182
-
183
- this._deleteByIndex(searchResult);
155
+ /**
156
+ * Called when a token expires before its time (e.g. following a
157
+ * auth:logout action)
158
+ * This method removes all maintained links and cleans up the
159
+ * hotel clerk
160
+ *
161
+ * @param token
162
+ */
163
+ async expire(token) {
164
+ if (token._id === this.anonymousUserId) {
165
+ return;
166
+ }
167
+ const idx = ManagedToken.indexFor(token);
168
+ const searchResult = this.tokens.search({ idx });
169
+ if (searchResult > -1) {
170
+ const managedToken = this.tokens.array[searchResult];
171
+ for (const connectionId of managedToken.connectionIds) {
172
+ this.tokensByConnection.delete(connectionId);
173
+ await global.kuzzle.ask('core:realtime:user:remove', connectionId);
174
+ }
175
+ this.deleteByIndex(searchResult);
176
+ }
184
177
  }
185
- }
186
-
187
- /**
188
- * Refresh an existing token with a new one
189
- *
190
- * @param {Token} oldToken
191
- * @param {Token} newToken
192
- */
193
- refresh (oldToken, newToken) {
194
- const
195
- oldIndex = getTokenIndex(oldToken),
196
- pos = this.tokens.search({idx: oldIndex});
197
-
198
- // If the old token has been created and then refreshed within the same
199
- // second, then it has the exact same characteristics than the new one.
200
- // This should never happen, though, especially if we add at least 1
201
- // real-time subscribe in the middle of the login+refresh sequence (all
202
- // within the same second) but, oh, well... it costs nothing to fix a
203
- // potentially very, very, very hard to debug random problem before it
204
- // occurs
205
- if (pos > -1 && oldToken._id !== newToken._id) {
206
- const connectionIds = this.tokens.array[pos].connectionIds;
207
-
208
- this._add(newToken, connectionIds);
209
-
210
- // Delete old token
211
- this._deleteByIndex(pos);
178
+ /**
179
+ * Refresh an existing token with a new one
180
+ *
181
+ * @param oldToken
182
+ * @param newToken
183
+ */
184
+ refresh(oldToken, newToken) {
185
+ const oldIndex = ManagedToken.indexFor(oldToken);
186
+ const pos = this.tokens.search({ idx: oldIndex });
187
+ // If the old token has been created and then refreshed within the same
188
+ // second, then it has the exact same characteristics than the new one.
189
+ // This should never happen, though, especially if we add at least 1
190
+ // real-time subscribe in the middle of the login+refresh sequence (all
191
+ // within the same second) but, oh, well... it costs nothing to fix a
192
+ // potentially very, very, very hard to debug random problem before it
193
+ // occurs
194
+ if (pos > -1 && oldToken._id !== newToken._id) {
195
+ const connectionIds = this.tokens.array[pos].connectionIds;
196
+ this.add(newToken, connectionIds);
197
+ // Delete old token
198
+ this.deleteByIndex(pos);
199
+ }
212
200
  }
213
- }
214
-
215
- async checkTokensValidity() {
216
- const arr = this.tokens.array;
217
-
218
- // API key can never expire (-1)
219
- if (arr.length > 0 && (arr[0].expiresAt > 0 && arr[0].expiresAt < Date.now())) {
220
- const connectionIds = arr[0].connectionIds;
221
-
222
- arr.shift();
223
-
224
- for (const connectionId of connectionIds) {
225
- await global.kuzzle.ask('core:realtime:tokenExpired:notify', connectionId);
226
- }
227
- setImmediate(() => this.checkTokensValidity());
228
- return;
201
+ async checkTokensValidity() {
202
+ const arr = this.tokens.array;
203
+ // API key can never expire (-1)
204
+ if (arr.length > 0 && (arr[0].expiresAt > 0 && arr[0].expiresAt < Date.now())) {
205
+ const connectionIds = arr[0].connectionIds;
206
+ arr.shift();
207
+ for (const connectionId of connectionIds) {
208
+ await global.kuzzle.ask('core:realtime:tokenExpired:notify', connectionId);
209
+ }
210
+ setImmediate(() => this.checkTokensValidity());
211
+ return;
212
+ }
213
+ if (arr.length > 0) {
214
+ this.runTimer();
215
+ }
229
216
  }
230
-
231
- if (arr.length > 0) {
232
- this.runTimer();
217
+ /**
218
+ * Gets the token matching user & connection if any
219
+ */
220
+ getConnectedUserToken(userId, connectionId) {
221
+ const token = this.tokensByConnection.get(connectionId);
222
+ return token && token.userId === userId ? token : null;
233
223
  }
234
- }
235
-
236
- /**
237
- * Gets the token matching user & connection if any
238
- *
239
- * @param {string} userId
240
- * @param {string} connectionId
241
- * @returns {Token}
242
- */
243
- getConnectedUserToken(userId, connectionId) {
244
- const data = this.tokensByConnection.get(connectionId);
245
-
246
- return data && data.userId === userId
247
- ? new Token({...data, connectionId})
248
- : null;
249
- }
250
-
251
- /**
252
- * Adds token to internal collections
253
- *
254
- * @param {Token} token
255
- * @param {string} connectionId
256
- * @private
257
- */
258
- _add(token, connectionIds) {
259
- const data = Object.assign({}, token, {
260
- connectionIds: new Set(connectionIds),
261
- idx: getTokenIndex(token)
262
- });
263
-
264
- for (const connectionId of connectionIds) {
265
- this.tokensByConnection.set(connectionId, data);
224
+ /**
225
+ * Returns the kuid associated to a connection
226
+ */
227
+ getKuidFromConnection(connectionId) {
228
+ const token = this.tokensByConnection.get(connectionId);
229
+ if (!token) {
230
+ return null;
231
+ }
232
+ return token.userId;
266
233
  }
267
- this.tokens.insert(data);
268
-
269
- if (this.tokens.array[0].idx === data.idx) {
270
- this.runTimer();
234
+ /**
235
+ * Adds token to internal collections
236
+ *
237
+ * @param token
238
+ * @param connectionId
239
+ */
240
+ add(token, connectionIds) {
241
+ const orderedToken = Object.assign({}, token, {
242
+ connectionIds: new Set(connectionIds),
243
+ idx: ManagedToken.indexFor(token)
244
+ });
245
+ for (const connectionId of connectionIds) {
246
+ this.tokensByConnection.set(connectionId, orderedToken);
247
+ }
248
+ this.tokens.insert(orderedToken);
249
+ if (this.tokens.array[0].idx === orderedToken.idx) {
250
+ this.runTimer();
251
+ }
271
252
  }
272
- }
273
-
274
- _removeConnectionLinkedToToken(connectionId, token) {
275
- token.connectionIds.delete(connectionId);
276
-
277
- if (token.connectionIds.size === 0) {
278
- const pos = this.tokens.search({ idx: token.idx });
279
- this._deleteByIndex(pos);
253
+ removeConnectionLinkedToToken(connectionId, managedToken) {
254
+ managedToken.connectionIds.delete(connectionId);
255
+ if (managedToken.connectionIds.size === 0) {
256
+ const pos = this.tokens.search({ idx: managedToken.idx });
257
+ this.deleteByIndex(pos);
258
+ }
280
259
  }
281
- }
282
-
283
- _deleteByIndex(index) {
284
- const data = this.tokens.array[index];
285
-
286
- if (!data) {
287
- return;
260
+ deleteByIndex(index) {
261
+ const orderedToken = this.tokens.array[index];
262
+ if (!orderedToken) {
263
+ return;
264
+ }
265
+ this.tokens.array.splice(index, 1);
288
266
  }
289
-
290
- this.tokens.array.splice(index, 1);
291
- }
292
267
  }
293
-
294
- /**
295
- * Calculate a simple sortable token index
296
- * @param token
297
- * @returns {string}
298
- */
299
- function getTokenIndex(token) {
300
- return `${token.expiresAt};${token._id}`;
301
- }
302
-
303
- module.exports = TokenManager;
268
+ exports.TokenManager = TokenManager;
269
+ //# sourceMappingURL=tokenManager.js.map
@@ -161,7 +161,6 @@ class Backend {
161
161
  throw runtimeError.get('already_started', 'start');
162
162
  }
163
163
  this._kuzzle = new kuzzle_1.default(this.config.content);
164
- // we need to load the default plugins
165
164
  for (const plugin of this.config.content.plugins.common.include) {
166
165
  const { default: PluginClass } = await Promise.resolve().then(() => __importStar(require(plugin)));
167
166
  this.plugin.use(new PluginClass(), {
@@ -251,15 +250,15 @@ class Backend {
251
250
  if (depth === 0) {
252
251
  return null;
253
252
  }
254
- const gitDir = `${dir}/.gut`;
253
+ const gitDir = `${dir}/.git`;
255
254
  if (!fs_1.default.existsSync(gitDir) && depth > 0) {
256
255
  return this._readCommit(`${dir}/..`, depth - 1);
257
256
  }
258
257
  if (!fs_1.default.statSync(gitDir).isDirectory()) {
259
258
  return null;
260
259
  }
261
- const ref = fs_1.default.readFileSync(`${dir}/.gut/HEAD`, 'utf8').split('ref: ')[1];
262
- const refFile = `${dir}/.gut/${ref}`.replace('\n', '');
260
+ const ref = fs_1.default.readFileSync(`${dir}/.git/HEAD`, 'utf8').split('ref: ')[1];
261
+ const refFile = `${dir}/.git/${ref}`.replace('\n', '');
263
262
  if (!fs_1.default.existsSync(refFile)) {
264
263
  return null;
265
264
  }
@@ -44,6 +44,33 @@ export declare class BackendController extends ApplicationManager {
44
44
  * actions: {
45
45
  * send: {
46
46
  * handler: this.send
47
+ * http: [{
48
+ * verb: 'post',
49
+ * path: 'email/send/:object',
50
+ * openapi: {
51
+ * description: "Send an email",
52
+ * parameters: [{
53
+ * in: "path",
54
+ * name: "object",
55
+ * schema: {
56
+ * type: "string"
57
+ * },
58
+ * required: true,
59
+ * }],
60
+ * responses: {
61
+ * 200: {
62
+ * description: "Acknowledgement",
63
+ * content: {
64
+ * "application/json": {
65
+ * schema: {
66
+ * type: "string",
67
+ * }
68
+ * }
69
+ * }
70
+ * }
71
+ * }
72
+ * }
73
+ * }]
47
74
  * }
48
75
  * }
49
76
  * };
@@ -78,6 +78,33 @@ class BackendController extends index_1.ApplicationManager {
78
78
  * actions: {
79
79
  * send: {
80
80
  * handler: this.send
81
+ * http: [{
82
+ * verb: 'post',
83
+ * path: 'email/send/:object',
84
+ * openapi: {
85
+ * description: "Send an email",
86
+ * parameters: [{
87
+ * in: "path",
88
+ * name: "object",
89
+ * schema: {
90
+ * type: "string"
91
+ * },
92
+ * required: true,
93
+ * }],
94
+ * responses: {
95
+ * 200: {
96
+ * description: "Acknowledgement",
97
+ * content: {
98
+ * "application/json": {
99
+ * schema: {
100
+ * type: "string",
101
+ * }
102
+ * }
103
+ * }
104
+ * }
105
+ * }
106
+ * }
107
+ * }]
81
108
  * }
82
109
  * }
83
110
  * };
@@ -50,11 +50,11 @@ class BackendImport extends index_1.ApplicationManager {
50
50
  if (this._application.started) {
51
51
  throw runtimeError.get('already_started', 'import');
52
52
  }
53
- else if (!safeObject_1.isPlainObject(mappings)) {
53
+ else if (!(0, safeObject_1.isPlainObject)(mappings)) {
54
54
  throw assertionError.get('invalid_type', 'mappings', 'object');
55
55
  }
56
56
  for (const index of Object.keys(mappings)) {
57
- if (!safeObject_1.isPlainObject(mappings[index])) {
57
+ if (!(0, safeObject_1.isPlainObject)(mappings[index])) {
58
58
  throw assertionError.get('invalid_type', `mappings.${index}`, 'object');
59
59
  }
60
60
  // If some collections have already been defined, only their last mappings will be retained
@@ -78,7 +78,7 @@ class BackendImport extends index_1.ApplicationManager {
78
78
  if (this._application.started) {
79
79
  throw runtimeError.get('already_started', 'import');
80
80
  }
81
- else if (!safeObject_1.isPlainObject(profiles)) {
81
+ else if (!(0, safeObject_1.isPlainObject)(profiles)) {
82
82
  throw assertionError.get('invalid_type', 'profiles', 'object');
83
83
  }
84
84
  // If some profiles have already been defined, only their last definition will be retained
@@ -101,7 +101,7 @@ class BackendImport extends index_1.ApplicationManager {
101
101
  if (this._application.started) {
102
102
  throw runtimeError.get('already_started', 'import');
103
103
  }
104
- else if (!safeObject_1.isPlainObject(roles)) {
104
+ else if (!(0, safeObject_1.isPlainObject)(roles)) {
105
105
  throw assertionError.get('invalid_type', 'roles', 'object');
106
106
  }
107
107
  // If some roles have already been defined, only their last definition will be retained
@@ -126,7 +126,7 @@ class BackendImport extends index_1.ApplicationManager {
126
126
  if (this._application.started) {
127
127
  throw runtimeError.get('already_started', 'import');
128
128
  }
129
- else if (!safeObject_1.isPlainObject(mappings)) {
129
+ else if (!(0, safeObject_1.isPlainObject)(mappings)) {
130
130
  throw assertionError.get('invalid_type', 'mappings', 'object');
131
131
  }
132
132
  this._application._import.userMappings = mappings;
@@ -147,7 +147,7 @@ class BackendImport extends index_1.ApplicationManager {
147
147
  if (this._application.started) {
148
148
  throw runtimeError.get('already_started', 'import');
149
149
  }
150
- else if (!safeObject_1.isPlainObject(users)) {
150
+ else if (!(0, safeObject_1.isPlainObject)(users)) {
151
151
  throw assertionError.get('invalid_type', 'users', 'object');
152
152
  }
153
153
  else if (options.onExistingUsers) {