@sasjs/api 0.6.0 → 0.7.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/README.md +3 -2
- package/build/browser.js +2231 -118
- package/build/cjs/corpus/records.js +1222 -0
- package/build/cjs/corpus/runner.js +15 -6
- package/build/cjs/drive/deploy.js +49 -10
- package/build/cjs/host/dispatch.js +190 -2
- package/build/cjs/identity/memory-store.js +196 -11
- package/build/cjs/identity/records.js +398 -0
- package/build/cjs/index.js +8 -1
- package/build/cjs/routes/generated.js +184 -4
- package/build/cjs/routes/overlay.js +53 -58
- package/build/esm/corpus/records.d.ts +25 -0
- package/build/esm/corpus/records.d.ts.map +1 -0
- package/build/esm/corpus/records.js +1219 -0
- package/build/esm/corpus/runner.d.ts.map +1 -1
- package/build/esm/corpus/runner.js +15 -6
- package/build/esm/drive/deploy.d.ts +10 -0
- package/build/esm/drive/deploy.d.ts.map +1 -1
- package/build/esm/drive/deploy.js +48 -9
- package/build/esm/host/dispatch.d.ts +9 -0
- package/build/esm/host/dispatch.d.ts.map +1 -1
- package/build/esm/host/dispatch.js +190 -2
- package/build/esm/identity/fixture.d.ts +6 -0
- package/build/esm/identity/fixture.d.ts.map +1 -1
- package/build/esm/identity/memory-store.d.ts +29 -5
- package/build/esm/identity/memory-store.d.ts.map +1 -1
- package/build/esm/identity/memory-store.js +196 -11
- package/build/esm/identity/records.d.ts +102 -0
- package/build/esm/identity/records.d.ts.map +1 -0
- package/build/esm/identity/records.js +393 -0
- package/build/esm/identity/types.d.ts +102 -2
- package/build/esm/identity/types.d.ts.map +1 -1
- package/build/esm/index.d.ts +4 -1
- package/build/esm/index.d.ts.map +1 -1
- package/build/esm/index.js +2 -0
- package/build/esm/routes/generated.d.ts.map +1 -1
- package/build/esm/routes/generated.js +184 -4
- package/build/esm/routes/overlay.d.ts.map +1 -1
- package/build/esm/routes/overlay.js +53 -58
- package/package.json +1 -1
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRecordService = exports.sanitiseDisplayName = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Users, groups, permission rules and clients: the administration routes'
|
|
6
|
+
* decisions, over the same store the sign-in flow uses.
|
|
7
|
+
*
|
|
8
|
+
* The server answers these from four controllers and two middlewares - the
|
|
9
|
+
* statuses, the messages and the order of the checks are copied from there, so a
|
|
10
|
+
* mock and a server refuse a request the same way. What stays with the host is
|
|
11
|
+
* the *transport* (a session or a bearer token, a CSRF token) and the things a
|
|
12
|
+
* host owns: a new user's home folder, a directory bind, an audit trail.
|
|
13
|
+
*
|
|
14
|
+
* The admin gate is here rather than in a caller because `dispatch` has no
|
|
15
|
+
* middleware: `requireAdmin` is `verifyAdmin`, and `requireSelfOrAdmin` is
|
|
16
|
+
* `verifyAdminIfNeeded`. Both answer `401 Admin account required`, which is what
|
|
17
|
+
* the server sends.
|
|
18
|
+
*/
|
|
19
|
+
const types_js_1 = require("../types.js");
|
|
20
|
+
/** The name the server gives the group every account is added to on creation. */
|
|
21
|
+
const ALL_USERS_GROUP = 'all-users';
|
|
22
|
+
/** The group whose membership the API refuses to change. */
|
|
23
|
+
const PUBLIC_GROUP = 'public';
|
|
24
|
+
const unauthorized = () => new types_js_1.ApiError(401, 'Unauthorized');
|
|
25
|
+
/**
|
|
26
|
+
* The caller, while the account behind it may still act.
|
|
27
|
+
*
|
|
28
|
+
* Every administration route reads the caller through this: the server
|
|
29
|
+
* re-reads the user on every request and refuses a deactivated account, so a
|
|
30
|
+
* token or session that outlived the account must not administer anything.
|
|
31
|
+
*/
|
|
32
|
+
const requireCaller = async (store, caller) => {
|
|
33
|
+
if (!caller)
|
|
34
|
+
throw unauthorized();
|
|
35
|
+
const user = await store.userById(caller.userId);
|
|
36
|
+
if (!user || !user.isActive)
|
|
37
|
+
throw unauthorized();
|
|
38
|
+
return user;
|
|
39
|
+
};
|
|
40
|
+
/** `verifyAdmin`: the caller is an administrator, or the route refuses. */
|
|
41
|
+
const requireAdmin = async (store, caller) => {
|
|
42
|
+
const user = await requireCaller(store, caller);
|
|
43
|
+
if (!user.isAdmin)
|
|
44
|
+
throw new types_js_1.ApiError(401, 'Admin account required');
|
|
45
|
+
return user;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* `verifyAdminIfNeeded`: an administrator, or the account being acted on.
|
|
49
|
+
*
|
|
50
|
+
* The server compares `req.params.uid` / `.username` with the caller's own
|
|
51
|
+
* identifier, which is why the target is passed in rather than read from the
|
|
52
|
+
* body - a restful route names its subject in the path.
|
|
53
|
+
*/
|
|
54
|
+
const requireSelfOrAdmin = async (store, caller, target) => {
|
|
55
|
+
const user = await requireCaller(store, caller);
|
|
56
|
+
if (!user.isAdmin && user.uid !== target?.uid)
|
|
57
|
+
throw new types_js_1.ApiError(401, 'Admin account required');
|
|
58
|
+
return user;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* A display name, as the server stores one: control characters out, trimmed and
|
|
62
|
+
* bounded.
|
|
63
|
+
*
|
|
64
|
+
* The value reaches generated stored-program source and webout headers, so the
|
|
65
|
+
* server sanitises on the way in; a host that stored it verbatim would hold a
|
|
66
|
+
* different value from the server for the same request.
|
|
67
|
+
*/
|
|
68
|
+
const MAX_DISPLAY_NAME_LENGTH = 100;
|
|
69
|
+
const sanitiseDisplayName = (value) => String(value ?? '')
|
|
70
|
+
// eslint-disable-next-line no-control-regex
|
|
71
|
+
.replace(/[\u0000-\u001F\u007F]/g, '')
|
|
72
|
+
.trim()
|
|
73
|
+
.slice(0, MAX_DISPLAY_NAME_LENGTH);
|
|
74
|
+
exports.sanitiseDisplayName = sanitiseDisplayName;
|
|
75
|
+
const asUser = (user) => ({
|
|
76
|
+
uid: user.uid,
|
|
77
|
+
username: user.username,
|
|
78
|
+
displayName: user.displayName,
|
|
79
|
+
isAdmin: user.isAdmin
|
|
80
|
+
});
|
|
81
|
+
const asGroup = (group) => ({
|
|
82
|
+
uid: group.uid,
|
|
83
|
+
name: group.name,
|
|
84
|
+
description: group.description
|
|
85
|
+
});
|
|
86
|
+
const createRecordService = (store) => {
|
|
87
|
+
const groupDetails = async (group) => {
|
|
88
|
+
const members = [];
|
|
89
|
+
for (const uid of group.users) {
|
|
90
|
+
const user = await store.userById(uid);
|
|
91
|
+
if (user)
|
|
92
|
+
members.push(asUser(user));
|
|
93
|
+
}
|
|
94
|
+
return { ...asGroup(group), isActive: group.isActive, users: members };
|
|
95
|
+
};
|
|
96
|
+
const userDetails = async (user, withAutoExec) => {
|
|
97
|
+
const groups = [];
|
|
98
|
+
for (const uid of user.groups) {
|
|
99
|
+
const group = await store.groupById(uid);
|
|
100
|
+
if (group)
|
|
101
|
+
groups.push(asGroup(group));
|
|
102
|
+
}
|
|
103
|
+
const details = {
|
|
104
|
+
...asUser(user),
|
|
105
|
+
isActive: user.isActive,
|
|
106
|
+
needsToUpdatePassword: user.needsToUpdatePassword,
|
|
107
|
+
groups
|
|
108
|
+
};
|
|
109
|
+
if (withAutoExec)
|
|
110
|
+
details.autoExec = user.autoExec ?? '';
|
|
111
|
+
return details;
|
|
112
|
+
};
|
|
113
|
+
const findBy = async (by) => by.uid !== undefined
|
|
114
|
+
? store.userById(by.uid)
|
|
115
|
+
: store.userByName(by.username ?? '');
|
|
116
|
+
const permissionDetails = async (permission) => {
|
|
117
|
+
const response = {
|
|
118
|
+
uid: permission.uid,
|
|
119
|
+
path: permission.path,
|
|
120
|
+
type: permission.type,
|
|
121
|
+
setting: permission.setting
|
|
122
|
+
};
|
|
123
|
+
if (permission.user) {
|
|
124
|
+
const user = await store.userById(permission.user);
|
|
125
|
+
if (user)
|
|
126
|
+
response.user = asUser(user);
|
|
127
|
+
}
|
|
128
|
+
if (permission.group) {
|
|
129
|
+
const group = await store.groupById(permission.group);
|
|
130
|
+
if (group)
|
|
131
|
+
response.group = await groupDetails(group);
|
|
132
|
+
}
|
|
133
|
+
return response;
|
|
134
|
+
};
|
|
135
|
+
return {
|
|
136
|
+
async listUsers(caller) {
|
|
137
|
+
await requireCaller(store, caller);
|
|
138
|
+
const users = await store.users();
|
|
139
|
+
return users.map(asUser);
|
|
140
|
+
},
|
|
141
|
+
async getUser(caller, by) {
|
|
142
|
+
const reader = await requireCaller(store, caller);
|
|
143
|
+
const user = await findBy(by);
|
|
144
|
+
if (!user)
|
|
145
|
+
throw new types_js_1.ApiError(404, 'User is not found.');
|
|
146
|
+
// the server hands the auto-exec code to an administrator or to the user
|
|
147
|
+
// themselves and omits it for anyone else
|
|
148
|
+
const withAutoExec = reader.isAdmin || reader.uid === user.uid;
|
|
149
|
+
return userDetails(user, withAutoExec);
|
|
150
|
+
},
|
|
151
|
+
async createUser(caller, body) {
|
|
152
|
+
await requireAdmin(store, caller);
|
|
153
|
+
if (await store.userByName(body.username))
|
|
154
|
+
throw new types_js_1.ApiError(409, 'Username already exists.');
|
|
155
|
+
const created = await store.createUser({
|
|
156
|
+
username: body.username,
|
|
157
|
+
displayName: (0, exports.sanitiseDisplayName)(body.displayName),
|
|
158
|
+
isAdmin: body.isAdmin === true,
|
|
159
|
+
isActive: body.isActive !== false,
|
|
160
|
+
autoExec: body.autoExec
|
|
161
|
+
});
|
|
162
|
+
await store.setPassword(created.uid, body.password);
|
|
163
|
+
// the server adds every new account to the all-users group when the host
|
|
164
|
+
// has one, so a group-based grant reaches it without an explicit add
|
|
165
|
+
const allUsers = await store.groupByName(ALL_USERS_GROUP);
|
|
166
|
+
if (allUsers)
|
|
167
|
+
await store.addToGroup(allUsers.uid, created.uid);
|
|
168
|
+
const stored = await store.userById(created.uid);
|
|
169
|
+
return userDetails(stored ?? created, true);
|
|
170
|
+
},
|
|
171
|
+
async updateUser(caller, by, body) {
|
|
172
|
+
const current = await findBy(by);
|
|
173
|
+
await requireSelfOrAdmin(store, caller, current);
|
|
174
|
+
const change = {};
|
|
175
|
+
const displayName = (0, exports.sanitiseDisplayName)(body.displayName);
|
|
176
|
+
// the provider checks come before the uniqueness check and before the
|
|
177
|
+
// update, in the server's order: a provider-managed account cannot have
|
|
178
|
+
// its identity fields changed, and saying so does not depend on the new
|
|
179
|
+
// value being free
|
|
180
|
+
if (body.username &&
|
|
181
|
+
body.username !== current?.username &&
|
|
182
|
+
current?.authProvider)
|
|
183
|
+
throw new types_js_1.ApiError(405, 'Can not update username of user that is created by an external auth provider.');
|
|
184
|
+
if (displayName &&
|
|
185
|
+
displayName !== current?.displayName &&
|
|
186
|
+
current?.authProvider)
|
|
187
|
+
throw new types_js_1.ApiError(405, 'Can not update display name of user that is created by an external auth provider.');
|
|
188
|
+
if (body.username) {
|
|
189
|
+
const holder = await store.userByName(body.username);
|
|
190
|
+
if (holder && holder.uid !== current?.uid)
|
|
191
|
+
throw new types_js_1.ApiError(409, 'Username already exists.');
|
|
192
|
+
change.username = body.username;
|
|
193
|
+
}
|
|
194
|
+
if (displayName)
|
|
195
|
+
change.displayName = displayName;
|
|
196
|
+
if (body.isAdmin !== undefined)
|
|
197
|
+
change.isAdmin = body.isAdmin;
|
|
198
|
+
if (body.isActive !== undefined)
|
|
199
|
+
change.isActive = body.isActive;
|
|
200
|
+
if (body.autoExec !== undefined)
|
|
201
|
+
change.autoExec = body.autoExec;
|
|
202
|
+
const updated = current
|
|
203
|
+
? await store.updateUser(current.uid, change)
|
|
204
|
+
: undefined;
|
|
205
|
+
if (!updated)
|
|
206
|
+
throw new types_js_1.ApiError(404, `Unable to find user with ${by.uid || by.username}`);
|
|
207
|
+
if (body.password)
|
|
208
|
+
await store.setPassword(updated.uid, body.password);
|
|
209
|
+
const stored = await store.userById(updated.uid);
|
|
210
|
+
return userDetails(stored ?? updated, true);
|
|
211
|
+
},
|
|
212
|
+
async deleteUser(caller, by, body) {
|
|
213
|
+
const current = await findBy(by);
|
|
214
|
+
const actor = await requireSelfOrAdmin(store, caller, current);
|
|
215
|
+
if (!current)
|
|
216
|
+
throw new types_js_1.ApiError(404, 'User is not found.');
|
|
217
|
+
// an administrator deletes without proving the password; anyone else
|
|
218
|
+
// deleting their own account proves it. The caller's admin flag is the
|
|
219
|
+
// server's `@Hidden() isAdmin` query parameter, resolved here from the
|
|
220
|
+
// caller's own record rather than trusted from the request.
|
|
221
|
+
if (!actor.isAdmin) {
|
|
222
|
+
if (!(await store.passwordMatches(current, body.password ?? '')))
|
|
223
|
+
throw new types_js_1.ApiError(401, 'Invalid password.');
|
|
224
|
+
}
|
|
225
|
+
await store.deleteUser(current.uid);
|
|
226
|
+
},
|
|
227
|
+
async listGroups(caller) {
|
|
228
|
+
await requireCaller(store, caller);
|
|
229
|
+
const groups = await store.groups();
|
|
230
|
+
return groups.map(asGroup);
|
|
231
|
+
},
|
|
232
|
+
async getGroup(caller, by) {
|
|
233
|
+
await requireCaller(store, caller);
|
|
234
|
+
const group = by.uid !== undefined
|
|
235
|
+
? await store.groupById(by.uid)
|
|
236
|
+
: await store.groupByName(by.name ?? '');
|
|
237
|
+
if (!group)
|
|
238
|
+
throw new types_js_1.ApiError(404, 'Group not found.');
|
|
239
|
+
return groupDetails(group);
|
|
240
|
+
},
|
|
241
|
+
async createGroup(caller, body) {
|
|
242
|
+
await requireAdmin(store, caller);
|
|
243
|
+
if (await store.groupByName(body.name))
|
|
244
|
+
throw new types_js_1.ApiError(409, 'Group name already exists.');
|
|
245
|
+
const created = await store.createGroup({
|
|
246
|
+
name: body.name,
|
|
247
|
+
description: body.description,
|
|
248
|
+
isActive: body.isActive !== false
|
|
249
|
+
});
|
|
250
|
+
return groupDetails(created);
|
|
251
|
+
},
|
|
252
|
+
async deleteGroup(caller, uid) {
|
|
253
|
+
await requireAdmin(store, caller);
|
|
254
|
+
if (!(await store.groupById(uid)))
|
|
255
|
+
throw new types_js_1.ApiError(404, 'Group not found.');
|
|
256
|
+
await store.deleteGroup(uid);
|
|
257
|
+
},
|
|
258
|
+
async addUserToGroup(caller, groupUid, userUid) {
|
|
259
|
+
await requireAdmin(store, caller);
|
|
260
|
+
const group = await store.groupById(groupUid);
|
|
261
|
+
if (!group)
|
|
262
|
+
throw new types_js_1.ApiError(404, 'Group not found.');
|
|
263
|
+
if (group.name === PUBLIC_GROUP)
|
|
264
|
+
throw new types_js_1.ApiError(400, `Can't add/remove user to '${PUBLIC_GROUP}' group.`);
|
|
265
|
+
if (group.authProvider)
|
|
266
|
+
throw new types_js_1.ApiError(405, "Can't add/remove user to group created by external auth provider.");
|
|
267
|
+
const user = await store.userById(userUid);
|
|
268
|
+
if (!user)
|
|
269
|
+
throw new types_js_1.ApiError(404, 'User not found.');
|
|
270
|
+
if (user.authProvider)
|
|
271
|
+
throw new types_js_1.ApiError(405, "Can't add/remove user to group created by external auth provider.");
|
|
272
|
+
const updated = await store.addToGroup(groupUid, userUid);
|
|
273
|
+
if (!updated)
|
|
274
|
+
throw new types_js_1.ApiError(400, 'Unable to update group.');
|
|
275
|
+
return groupDetails(updated);
|
|
276
|
+
},
|
|
277
|
+
async removeUserFromGroup(caller, groupUid, userUid) {
|
|
278
|
+
await requireAdmin(store, caller);
|
|
279
|
+
const group = await store.groupById(groupUid);
|
|
280
|
+
if (!group)
|
|
281
|
+
throw new types_js_1.ApiError(404, 'Group not found.');
|
|
282
|
+
if (group.name === PUBLIC_GROUP)
|
|
283
|
+
throw new types_js_1.ApiError(400, `Can't add/remove user to '${PUBLIC_GROUP}' group.`);
|
|
284
|
+
if (group.authProvider)
|
|
285
|
+
throw new types_js_1.ApiError(405, "Can't add/remove user to group created by external auth provider.");
|
|
286
|
+
const user = await store.userById(userUid);
|
|
287
|
+
if (!user)
|
|
288
|
+
throw new types_js_1.ApiError(404, 'User not found.');
|
|
289
|
+
if (user.authProvider)
|
|
290
|
+
throw new types_js_1.ApiError(405, "Can't add/remove user to group created by external auth provider.");
|
|
291
|
+
const updated = await store.removeFromGroup(groupUid, userUid);
|
|
292
|
+
if (!updated)
|
|
293
|
+
throw new types_js_1.ApiError(400, 'Unable to update group.');
|
|
294
|
+
return groupDetails(updated);
|
|
295
|
+
},
|
|
296
|
+
async listPermissions(caller) {
|
|
297
|
+
const reader = await requireCaller(store, caller);
|
|
298
|
+
const rules = await store.permissions();
|
|
299
|
+
// an administrator sees every rule; anyone else sees the rules assigned to
|
|
300
|
+
// them and to the groups they belong to, which is what an app renders its
|
|
301
|
+
// menus from
|
|
302
|
+
const visible = reader.isAdmin
|
|
303
|
+
? rules
|
|
304
|
+
: rules.filter((rule) => rule.user === reader.uid ||
|
|
305
|
+
(rule.group !== undefined && reader.groups.includes(rule.group)));
|
|
306
|
+
const out = [];
|
|
307
|
+
for (const rule of visible)
|
|
308
|
+
out.push(await permissionDetails(rule));
|
|
309
|
+
return out;
|
|
310
|
+
},
|
|
311
|
+
async createPermission(caller, body) {
|
|
312
|
+
await requireAdmin(store, caller);
|
|
313
|
+
if (body.principalType === 'user') {
|
|
314
|
+
const user = await store.userById(body.principalId);
|
|
315
|
+
if (!user)
|
|
316
|
+
throw new types_js_1.ApiError(404, 'User not found.');
|
|
317
|
+
if (user.isAdmin)
|
|
318
|
+
throw new types_js_1.ApiError(400, 'Can not add permission for admin user.');
|
|
319
|
+
const exists = (await store.permissions()).some((rule) => rule.path === body.path &&
|
|
320
|
+
rule.type === body.type &&
|
|
321
|
+
rule.user === body.principalId);
|
|
322
|
+
if (exists)
|
|
323
|
+
throw new types_js_1.ApiError(409, 'Permission already exists with provided Path, Type and User.');
|
|
324
|
+
const created = await store.createPermission({
|
|
325
|
+
path: body.path,
|
|
326
|
+
type: body.type,
|
|
327
|
+
setting: body.setting,
|
|
328
|
+
user: body.principalId
|
|
329
|
+
});
|
|
330
|
+
return permissionDetails(created);
|
|
331
|
+
}
|
|
332
|
+
if (body.principalType === 'group') {
|
|
333
|
+
const group = await store.groupById(body.principalId);
|
|
334
|
+
if (!group)
|
|
335
|
+
throw new types_js_1.ApiError(404, 'Group not found.');
|
|
336
|
+
const exists = (await store.permissions()).some((rule) => rule.path === body.path &&
|
|
337
|
+
rule.type === body.type &&
|
|
338
|
+
rule.group === body.principalId);
|
|
339
|
+
if (exists)
|
|
340
|
+
throw new types_js_1.ApiError(409, 'Permission already exists with provided Path, Type and Group.');
|
|
341
|
+
const created = await store.createPermission({
|
|
342
|
+
path: body.path,
|
|
343
|
+
type: body.type,
|
|
344
|
+
setting: body.setting,
|
|
345
|
+
group: body.principalId
|
|
346
|
+
});
|
|
347
|
+
return permissionDetails(created);
|
|
348
|
+
}
|
|
349
|
+
throw new types_js_1.ApiError(400, 'Invalid principal type. Valid types are user or group.');
|
|
350
|
+
},
|
|
351
|
+
async updatePermission(caller, uid, body) {
|
|
352
|
+
await requireAdmin(store, caller);
|
|
353
|
+
const updated = await store.setPermissionSetting(uid, body.setting);
|
|
354
|
+
if (!updated)
|
|
355
|
+
throw new types_js_1.ApiError(404, 'Permission not found.');
|
|
356
|
+
return permissionDetails(updated);
|
|
357
|
+
},
|
|
358
|
+
async deletePermission(caller, uid) {
|
|
359
|
+
await requireAdmin(store, caller);
|
|
360
|
+
if (!(await store.permissionById(uid)))
|
|
361
|
+
throw new types_js_1.ApiError(404, 'Permission not found.');
|
|
362
|
+
await store.deletePermission(uid);
|
|
363
|
+
},
|
|
364
|
+
async listClients(caller) {
|
|
365
|
+
await requireAdmin(store, caller);
|
|
366
|
+
return store.clients();
|
|
367
|
+
},
|
|
368
|
+
async createClient(caller, body) {
|
|
369
|
+
await requireAdmin(store, caller);
|
|
370
|
+
// the server answers `res.status(403).send(err.toString())`, so the body a
|
|
371
|
+
// client receives carries the Error prefix
|
|
372
|
+
if (await store.client(body.clientId))
|
|
373
|
+
throw new types_js_1.ApiError(403, 'Error: Client ID already exists.');
|
|
374
|
+
return store.createClient({
|
|
375
|
+
clientId: body.clientId,
|
|
376
|
+
clientSecret: body.clientSecret,
|
|
377
|
+
accessTokenExpiration: body.accessTokenExpiration,
|
|
378
|
+
refreshTokenExpiration: body.refreshTokenExpiration
|
|
379
|
+
});
|
|
380
|
+
},
|
|
381
|
+
async updatePassword(caller, body) {
|
|
382
|
+
// requireCaller answers 401 for a caller that is absent or deactivated, so
|
|
383
|
+
// the document's 404 (a caller whose record has gone) cannot be reached
|
|
384
|
+
// here - the overlay records that as a host-only status.
|
|
385
|
+
const reader = await requireCaller(store, caller);
|
|
386
|
+
if (reader.authProvider)
|
|
387
|
+
throw new types_js_1.ApiError(405, 'Can not update password of user that is created by an external auth provider.');
|
|
388
|
+
if (!(await store.passwordMatches(reader, body.currentPassword)))
|
|
389
|
+
throw new types_js_1.ApiError(403, 'Invalid current password!');
|
|
390
|
+
await store.setPassword(reader.uid, body.newPassword);
|
|
391
|
+
// the flag is what makes the web interface ask, and the server clears it in
|
|
392
|
+
// the same write: it has to go through the store, or a host keeps refusing
|
|
393
|
+
// every call the caller makes after the change
|
|
394
|
+
await store.updateUser(reader.uid, { needsToUpdatePassword: false });
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
};
|
|
398
|
+
exports.createRecordService = createRecordService;
|
package/build/cjs/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.servicesFor = exports.runCase = exports.IDENTITY_CORPUS = exports.DRIVE_CORPUS = exports.parseIdentity = exports.IDENTITY_FORMAT = exports.DEFAULT_IDENTITY = exports.MemoryIdentityStore = exports.createIdentityService = exports.HANDLED_ROUTES = exports.dispatch = exports.TAG_CAPABILITIES = exports.OVERLAY = exports.findRoute = exports.routeById = exports.ROUTES = exports.MemoryDriveStore = exports.measureDeployTree = exports.isDeployTree = exports.deployTree = exports.decodeBase64 = exports.checkDeployTree = exports.DEPLOY_SUCCESS = exports.DEPLOY_PAYLOAD_BYTES = exports.DEPLOY_LIMITS = exports.createDriveService = exports.isRoot = exports.basename = exports.dirname = exports.hasBlockedExtension = exports.normaliseDrivePath = void 0;
|
|
17
|
+
exports.servicesFor = exports.runCase = exports.PROVIDER_MANAGED = exports.ADMIN_SIGNED_IN = exports.RECORD_CORPUS = exports.IDENTITY_CORPUS = exports.DRIVE_CORPUS = exports.parseIdentity = exports.IDENTITY_FORMAT = exports.DEFAULT_IDENTITY = exports.MemoryIdentityStore = exports.sanitiseDisplayName = exports.createRecordService = exports.createIdentityService = exports.HANDLED_ROUTES = exports.dispatch = exports.TAG_CAPABILITIES = exports.OVERLAY = exports.findRoute = exports.routeById = exports.ROUTES = exports.MemoryDriveStore = exports.measureDeployTree = exports.isDeployTree = exports.deployTree = exports.decodeBase64 = exports.checkDeployTree = exports.DEPLOY_SUCCESS = exports.DEPLOY_PAYLOAD_BYTES = exports.DEPLOY_LIMITS = exports.createDriveService = exports.isRoot = exports.basename = exports.dirname = exports.hasBlockedExtension = exports.normaliseDrivePath = void 0;
|
|
18
18
|
/**
|
|
19
19
|
* `@sasjs/api` - the SASjs Server API described once.
|
|
20
20
|
*
|
|
@@ -62,6 +62,9 @@ Object.defineProperty(exports, "dispatch", { enumerable: true, get: function ()
|
|
|
62
62
|
Object.defineProperty(exports, "HANDLED_ROUTES", { enumerable: true, get: function () { return dispatch_js_1.HANDLED_ROUTES; } });
|
|
63
63
|
var service_js_2 = require("./identity/service.js");
|
|
64
64
|
Object.defineProperty(exports, "createIdentityService", { enumerable: true, get: function () { return service_js_2.createIdentityService; } });
|
|
65
|
+
var records_js_1 = require("./identity/records.js");
|
|
66
|
+
Object.defineProperty(exports, "createRecordService", { enumerable: true, get: function () { return records_js_1.createRecordService; } });
|
|
67
|
+
Object.defineProperty(exports, "sanitiseDisplayName", { enumerable: true, get: function () { return records_js_1.sanitiseDisplayName; } });
|
|
65
68
|
var memory_store_js_2 = require("./identity/memory-store.js");
|
|
66
69
|
Object.defineProperty(exports, "MemoryIdentityStore", { enumerable: true, get: function () { return memory_store_js_2.MemoryIdentityStore; } });
|
|
67
70
|
var fixture_js_1 = require("./identity/fixture.js");
|
|
@@ -72,6 +75,10 @@ var drive_js_1 = require("./corpus/drive.js");
|
|
|
72
75
|
Object.defineProperty(exports, "DRIVE_CORPUS", { enumerable: true, get: function () { return drive_js_1.DRIVE_CORPUS; } });
|
|
73
76
|
var identity_js_1 = require("./corpus/identity.js");
|
|
74
77
|
Object.defineProperty(exports, "IDENTITY_CORPUS", { enumerable: true, get: function () { return identity_js_1.IDENTITY_CORPUS; } });
|
|
78
|
+
var records_js_2 = require("./corpus/records.js");
|
|
79
|
+
Object.defineProperty(exports, "RECORD_CORPUS", { enumerable: true, get: function () { return records_js_2.RECORD_CORPUS; } });
|
|
80
|
+
Object.defineProperty(exports, "ADMIN_SIGNED_IN", { enumerable: true, get: function () { return records_js_2.ADMIN_SIGNED_IN; } });
|
|
81
|
+
Object.defineProperty(exports, "PROVIDER_MANAGED", { enumerable: true, get: function () { return records_js_2.PROVIDER_MANAGED; } });
|
|
75
82
|
var runner_js_1 = require("./corpus/runner.js");
|
|
76
83
|
Object.defineProperty(exports, "runCase", { enumerable: true, get: function () { return runner_js_1.runCase; } });
|
|
77
84
|
Object.defineProperty(exports, "servicesFor", { enumerable: true, get: function () { return runner_js_1.servicesFor; } });
|