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.
- package/lib/api/controllers/securityController.js +49 -13
- package/lib/api/funnel.js +13 -24
- package/lib/api/openApiGenerator.js +6 -0
- package/lib/api/request/kuzzleRequest.js +9 -9
- package/lib/config/default.config.js +10 -1
- package/lib/core/auth/tokenManager.d.ts +65 -0
- package/lib/core/auth/tokenManager.js +222 -256
- package/lib/core/backend/backend.js +3 -4
- package/lib/core/backend/backendController.d.ts +27 -0
- package/lib/core/backend/backendController.js +27 -0
- package/lib/core/backend/backendImport.js +6 -6
- package/lib/core/backend/backendPlugin.js +1 -1
- package/lib/core/plugin/plugin.js +42 -2
- package/lib/core/plugin/pluginContext.js +2 -2
- package/lib/core/plugin/pluginsManager.js +1 -0
- package/lib/core/realtime/hotelClerk.js +30 -16
- package/lib/core/security/profileRepository.js +5 -22
- package/lib/core/security/roleRepository.js +11 -5
- package/lib/core/security/tokenRepository.js +1 -1
- package/lib/core/security/userRepository.js +2 -2
- package/lib/core/shared/KoncordeWrapper.d.ts +1 -2
- package/lib/core/shared/KoncordeWrapper.js +10 -9
- package/lib/core/shared/repository.js +3 -3
- package/lib/core/shared/sdk/embeddedSdk.d.ts +2 -2
- package/lib/core/shared/sdk/embeddedSdk.js +2 -2
- package/lib/kerror/codes/4-plugin.json +6 -0
- package/lib/kuzzle/kuzzle.js +1 -1
- package/lib/model/security/token.d.ts +29 -0
- package/lib/model/security/token.js +25 -24
- package/lib/service/storage/elasticsearch.js +23 -5
- package/lib/types/ControllerDefinition.d.ts +51 -1
- package/lib/types/Plugin.js +1 -1
- package/lib/util/koncordeCompat.js +1 -1
- package/lib/util/mutex.js +2 -2
- package/package-lock.json +559 -1889
- 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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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}/.
|
|
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}/.
|
|
262
|
-
const refFile = `${dir}/.
|
|
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) {
|