@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,1222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RECORD_CORPUS = exports.PROVIDER_MANAGED = exports.ADMIN_SIGNED_IN = void 0;
|
|
4
|
+
const fixture_js_1 = require("../identity/fixture.js");
|
|
5
|
+
/** The admin and the restricted user, signed in. */
|
|
6
|
+
const SIGNED_IN_TOKENS = [
|
|
7
|
+
{
|
|
8
|
+
username: 'admin',
|
|
9
|
+
clientId: 'web_app',
|
|
10
|
+
accessToken: 'admin-access',
|
|
11
|
+
refreshToken: 'admin-refresh'
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
username: 'user2',
|
|
15
|
+
clientId: 'web_app',
|
|
16
|
+
accessToken: 'user-access',
|
|
17
|
+
refreshToken: 'user-refresh'
|
|
18
|
+
}
|
|
19
|
+
];
|
|
20
|
+
/** The default identity, with an administrator and the restricted user signed in. */
|
|
21
|
+
exports.ADMIN_SIGNED_IN = {
|
|
22
|
+
...fixture_js_1.DEFAULT_IDENTITY,
|
|
23
|
+
tokens: SIGNED_IN_TOKENS
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The same identity, plus what the provider and public-group refusals need: an
|
|
27
|
+
* account and a group managed by an external provider, and the reserved group.
|
|
28
|
+
*/
|
|
29
|
+
exports.PROVIDER_MANAGED = {
|
|
30
|
+
...exports.ADMIN_SIGNED_IN,
|
|
31
|
+
tokens: [
|
|
32
|
+
...SIGNED_IN_TOKENS,
|
|
33
|
+
{
|
|
34
|
+
username: 'diruser',
|
|
35
|
+
clientId: 'web_app',
|
|
36
|
+
accessToken: 'dir-access',
|
|
37
|
+
refreshToken: 'dir-refresh'
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
users: [
|
|
41
|
+
...exports.ADMIN_SIGNED_IN.users,
|
|
42
|
+
{
|
|
43
|
+
uid: 'user-directory',
|
|
44
|
+
username: 'diruser',
|
|
45
|
+
displayName: 'Directory User',
|
|
46
|
+
password: 'dir123',
|
|
47
|
+
isAdmin: false,
|
|
48
|
+
isActive: true,
|
|
49
|
+
needsToUpdatePassword: false,
|
|
50
|
+
authProvider: 'oidc'
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
groups: [
|
|
54
|
+
...exports.ADMIN_SIGNED_IN.groups,
|
|
55
|
+
{
|
|
56
|
+
uid: 'group-directory',
|
|
57
|
+
name: 'Directory',
|
|
58
|
+
description: 'Managed by the directory',
|
|
59
|
+
isActive: true,
|
|
60
|
+
users: [],
|
|
61
|
+
authProvider: 'oidc'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
uid: 'group-public',
|
|
65
|
+
name: 'public',
|
|
66
|
+
description: 'Everyone',
|
|
67
|
+
isActive: true,
|
|
68
|
+
users: []
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
};
|
|
72
|
+
const ADMIN = 'admin-access';
|
|
73
|
+
const RESTRICTED = 'user-access';
|
|
74
|
+
const RESTRICTED_UID = 'user-restricted';
|
|
75
|
+
const ADMIN_UID = 'user-admin';
|
|
76
|
+
exports.RECORD_CORPUS = [
|
|
77
|
+
// ------------------------------------------------------------------ users
|
|
78
|
+
{
|
|
79
|
+
id: 'list-users',
|
|
80
|
+
route: 'GET /SASjsApi/user',
|
|
81
|
+
description: 'lists the accounts any signed-in caller may see',
|
|
82
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
83
|
+
request: { token: ADMIN },
|
|
84
|
+
expect: {
|
|
85
|
+
status: 200,
|
|
86
|
+
body: [
|
|
87
|
+
{
|
|
88
|
+
uid: ADMIN_UID,
|
|
89
|
+
username: 'admin',
|
|
90
|
+
displayName: 'Demo Admin',
|
|
91
|
+
isAdmin: true
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
uid: RESTRICTED_UID,
|
|
95
|
+
username: 'user2',
|
|
96
|
+
displayName: 'Demo User',
|
|
97
|
+
isAdmin: false
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: 'get-user-self',
|
|
104
|
+
route: 'GET /SASjsApi/user/{uid}',
|
|
105
|
+
description: 'returns the caller their own details, with the auto-exec code',
|
|
106
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
107
|
+
request: { token: RESTRICTED, params: { uid: RESTRICTED_UID } },
|
|
108
|
+
expect: {
|
|
109
|
+
status: 200,
|
|
110
|
+
partial: {
|
|
111
|
+
uid: RESTRICTED_UID,
|
|
112
|
+
username: 'user2',
|
|
113
|
+
isActive: true,
|
|
114
|
+
isAdmin: false,
|
|
115
|
+
autoExec: ''
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'get-user-unauthenticated',
|
|
121
|
+
route: 'GET /SASjsApi/user/{uid}',
|
|
122
|
+
description: 'refuses a read with no caller at all',
|
|
123
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
124
|
+
request: { params: { uid: RESTRICTED_UID } },
|
|
125
|
+
expect: { status: 401, error: 'Unauthorized' }
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: 'get-user-missing',
|
|
129
|
+
route: 'GET /SASjsApi/user/{uid}',
|
|
130
|
+
description: 'answers 404 for an identifier no account has',
|
|
131
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
132
|
+
request: { token: ADMIN, params: { uid: 'user-none' } },
|
|
133
|
+
expect: { status: 404, error: 'User is not found.' }
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: 'get-user-by-username',
|
|
137
|
+
route: 'GET /SASjsApi/user/by/username/{username}',
|
|
138
|
+
description: 'finds an account by username',
|
|
139
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
140
|
+
request: { token: ADMIN, params: { username: 'user2' } },
|
|
141
|
+
expect: {
|
|
142
|
+
status: 200,
|
|
143
|
+
partial: { uid: RESTRICTED_UID, username: 'user2', isAdmin: false }
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 'get-user-by-username-unauthenticated',
|
|
148
|
+
route: 'GET /SASjsApi/user/by/username/{username}',
|
|
149
|
+
description: 'refuses the by-username read with no caller',
|
|
150
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
151
|
+
request: { params: { username: 'user2' } },
|
|
152
|
+
expect: { status: 401, error: 'Unauthorized' }
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'get-user-by-username-missing',
|
|
156
|
+
route: 'GET /SASjsApi/user/by/username/{username}',
|
|
157
|
+
description: 'answers 404 for a username no account has',
|
|
158
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
159
|
+
request: { token: ADMIN, params: { username: 'nobody' } },
|
|
160
|
+
expect: { status: 404, error: 'User is not found.' }
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: 'create-user',
|
|
164
|
+
route: 'POST /SASjsApi/user',
|
|
165
|
+
description: 'creates an account, sanitised, and reports it',
|
|
166
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
167
|
+
request: {
|
|
168
|
+
token: ADMIN,
|
|
169
|
+
body: {
|
|
170
|
+
username: 'newuser',
|
|
171
|
+
displayName: ' New\u0001 User ',
|
|
172
|
+
password: 'newpass',
|
|
173
|
+
isAdmin: false,
|
|
174
|
+
isActive: true
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
expect: {
|
|
178
|
+
status: 200,
|
|
179
|
+
partial: {
|
|
180
|
+
username: 'newuser',
|
|
181
|
+
displayName: 'New User',
|
|
182
|
+
isAdmin: false,
|
|
183
|
+
isActive: true
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: 'create-user-not-admin',
|
|
189
|
+
route: 'POST /SASjsApi/user',
|
|
190
|
+
description: 'refuses a creation by a caller who is not an administrator',
|
|
191
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
192
|
+
request: {
|
|
193
|
+
token: RESTRICTED,
|
|
194
|
+
body: {
|
|
195
|
+
username: 'newuser',
|
|
196
|
+
displayName: 'New User',
|
|
197
|
+
password: 'newpass'
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: 'create-user-anonymous-incomplete',
|
|
204
|
+
route: 'POST /SASjsApi/user',
|
|
205
|
+
description: 'answers 401 before the payload schema, anonymously and empty',
|
|
206
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
207
|
+
request: { body: {} },
|
|
208
|
+
expect: { status: 401, error: 'Unauthorized' }
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: 'create-user-duplicate',
|
|
212
|
+
route: 'POST /SASjsApi/user',
|
|
213
|
+
description: 'refuses a username that is taken',
|
|
214
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
215
|
+
request: {
|
|
216
|
+
token: ADMIN,
|
|
217
|
+
body: { username: 'user2', displayName: 'Another', password: 'newpass' }
|
|
218
|
+
},
|
|
219
|
+
expect: { status: 409, error: 'Username already exists.' }
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: 'update-user',
|
|
223
|
+
route: 'PATCH /SASjsApi/user/{uid}',
|
|
224
|
+
description: "changes an account's display name",
|
|
225
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
226
|
+
request: {
|
|
227
|
+
token: ADMIN,
|
|
228
|
+
params: { uid: RESTRICTED_UID },
|
|
229
|
+
body: {
|
|
230
|
+
displayName: 'Renamed User',
|
|
231
|
+
username: 'user2',
|
|
232
|
+
password: 'user123'
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
expect: {
|
|
236
|
+
status: 200,
|
|
237
|
+
partial: { uid: RESTRICTED_UID, displayName: 'Renamed User' }
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: 'update-user-not-admin',
|
|
242
|
+
route: 'PATCH /SASjsApi/user/{uid}',
|
|
243
|
+
description: "refuses a change to somebody else's account",
|
|
244
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
245
|
+
request: {
|
|
246
|
+
token: RESTRICTED,
|
|
247
|
+
params: { uid: ADMIN_UID },
|
|
248
|
+
body: { displayName: 'Hijacked', username: 'admin', password: 'admin123' }
|
|
249
|
+
},
|
|
250
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
id: 'update-user-missing',
|
|
254
|
+
route: 'PATCH /SASjsApi/user/{uid}',
|
|
255
|
+
description: 'answers 404 with the identifier it looked up',
|
|
256
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
257
|
+
request: {
|
|
258
|
+
token: ADMIN,
|
|
259
|
+
params: { uid: 'user-none' },
|
|
260
|
+
body: { displayName: 'Nobody', username: 'nobody', password: 'none123' }
|
|
261
|
+
},
|
|
262
|
+
expect: { status: 404, error: 'Unable to find user with user-none' }
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: 'update-user-provider',
|
|
266
|
+
route: 'PATCH /SASjsApi/user/{uid}',
|
|
267
|
+
description: 'refuses a username change on a provider-managed account',
|
|
268
|
+
identity: exports.PROVIDER_MANAGED,
|
|
269
|
+
request: {
|
|
270
|
+
token: ADMIN,
|
|
271
|
+
params: { uid: 'user-directory' },
|
|
272
|
+
body: {
|
|
273
|
+
username: 'renamed',
|
|
274
|
+
displayName: 'Directory User',
|
|
275
|
+
password: 'dir123'
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
expect: {
|
|
279
|
+
status: 405,
|
|
280
|
+
error: 'Can not update username of user that is created by an external auth provider.'
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
id: 'update-user-username-taken',
|
|
285
|
+
route: 'PATCH /SASjsApi/user/{uid}',
|
|
286
|
+
description: 'refuses a username another account already has',
|
|
287
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
288
|
+
request: {
|
|
289
|
+
token: ADMIN,
|
|
290
|
+
params: { uid: RESTRICTED_UID },
|
|
291
|
+
body: { username: 'admin', displayName: 'Demo User', password: 'user123' }
|
|
292
|
+
},
|
|
293
|
+
expect: { status: 409, error: 'Username already exists.' }
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: 'update-user-by-username',
|
|
297
|
+
route: 'PATCH /SASjsApi/user/by/username/{username}',
|
|
298
|
+
description: 'changes an account looked up by username',
|
|
299
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
300
|
+
request: {
|
|
301
|
+
token: ADMIN,
|
|
302
|
+
params: { username: 'user2' },
|
|
303
|
+
body: {
|
|
304
|
+
isActive: false,
|
|
305
|
+
username: 'user2',
|
|
306
|
+
displayName: 'Demo User',
|
|
307
|
+
password: 'user123'
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
expect: { status: 200, partial: { username: 'user2', isActive: false } }
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: 'update-user-by-username-not-admin',
|
|
314
|
+
route: 'PATCH /SASjsApi/user/by/username/{username}',
|
|
315
|
+
description: 'refuses a change to another account by username',
|
|
316
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
317
|
+
request: {
|
|
318
|
+
token: RESTRICTED,
|
|
319
|
+
params: { username: 'admin' },
|
|
320
|
+
body: { displayName: 'Hijacked', username: 'admin', password: 'admin123' }
|
|
321
|
+
},
|
|
322
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: 'update-user-by-username-missing',
|
|
326
|
+
route: 'PATCH /SASjsApi/user/by/username/{username}',
|
|
327
|
+
description: 'answers 404 with the username it looked up',
|
|
328
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
329
|
+
request: {
|
|
330
|
+
token: ADMIN,
|
|
331
|
+
params: { username: 'nobody' },
|
|
332
|
+
body: { displayName: 'Nobody', username: 'nobody', password: 'none123' }
|
|
333
|
+
},
|
|
334
|
+
expect: { status: 404, error: 'Unable to find user with nobody' }
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
id: 'update-user-by-username-provider',
|
|
338
|
+
route: 'PATCH /SASjsApi/user/by/username/{username}',
|
|
339
|
+
description: 'refuses a display-name change on a provider-managed account',
|
|
340
|
+
identity: exports.PROVIDER_MANAGED,
|
|
341
|
+
request: {
|
|
342
|
+
token: ADMIN,
|
|
343
|
+
params: { username: 'diruser' },
|
|
344
|
+
body: { displayName: 'Renamed', username: 'diruser', password: 'dir123' }
|
|
345
|
+
},
|
|
346
|
+
expect: {
|
|
347
|
+
status: 405,
|
|
348
|
+
error: 'Can not update display name of user that is created by an external auth provider.'
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
id: 'update-user-by-username-taken',
|
|
353
|
+
route: 'PATCH /SASjsApi/user/by/username/{username}',
|
|
354
|
+
description: 'refuses a username another account already has',
|
|
355
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
356
|
+
request: {
|
|
357
|
+
token: ADMIN,
|
|
358
|
+
params: { username: 'user2' },
|
|
359
|
+
body: { username: 'admin', displayName: 'Demo User', password: 'user123' }
|
|
360
|
+
},
|
|
361
|
+
expect: { status: 409, error: 'Username already exists.' }
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
id: 'delete-user',
|
|
365
|
+
route: 'DELETE /SASjsApi/user/{uid}',
|
|
366
|
+
description: 'deletes an account as an administrator, without a password',
|
|
367
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
368
|
+
request: { token: ADMIN, params: { uid: RESTRICTED_UID }, body: {} },
|
|
369
|
+
expect: { status: 204 }
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: 'delete-user-not-admin',
|
|
373
|
+
route: 'DELETE /SASjsApi/user/{uid}',
|
|
374
|
+
description: "refuses a deletion of somebody else's account",
|
|
375
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
376
|
+
request: { token: RESTRICTED, params: { uid: ADMIN_UID }, body: {} },
|
|
377
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
id: 'delete-user-missing',
|
|
381
|
+
route: 'DELETE /SASjsApi/user/{uid}',
|
|
382
|
+
description: 'answers 404 for an identifier no account has',
|
|
383
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
384
|
+
request: { token: ADMIN, params: { uid: 'user-none' }, body: {} },
|
|
385
|
+
expect: { status: 404, error: 'User is not found.' }
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
id: 'delete-user-by-username',
|
|
389
|
+
route: 'DELETE /SASjsApi/user/by/username/{username}',
|
|
390
|
+
description: 'deletes an account looked up by username',
|
|
391
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
392
|
+
request: {
|
|
393
|
+
token: ADMIN,
|
|
394
|
+
params: { username: 'user2' },
|
|
395
|
+
body: {}
|
|
396
|
+
},
|
|
397
|
+
expect: { status: 204 }
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
id: 'delete-user-by-username-wrong-password',
|
|
401
|
+
route: 'DELETE /SASjsApi/user/by/username/{username}',
|
|
402
|
+
description: 'refuses a self-deletion whose password is wrong',
|
|
403
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
404
|
+
request: {
|
|
405
|
+
token: RESTRICTED,
|
|
406
|
+
params: { username: 'user2' },
|
|
407
|
+
body: { password: 'wrong' }
|
|
408
|
+
},
|
|
409
|
+
expect: { status: 401, error: 'Invalid password.' }
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
id: 'delete-user-by-username-missing',
|
|
413
|
+
route: 'DELETE /SASjsApi/user/by/username/{username}',
|
|
414
|
+
description: 'answers 404 for a username no account has',
|
|
415
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
416
|
+
request: { token: ADMIN, params: { username: 'nobody' }, body: {} },
|
|
417
|
+
expect: { status: 404, error: 'User is not found.' }
|
|
418
|
+
},
|
|
419
|
+
// ----------------------------------------------------------------- groups
|
|
420
|
+
{
|
|
421
|
+
id: 'list-groups',
|
|
422
|
+
route: 'GET /SASjsApi/group',
|
|
423
|
+
description: 'lists the groups',
|
|
424
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
425
|
+
request: { token: RESTRICTED },
|
|
426
|
+
expect: {
|
|
427
|
+
status: 200,
|
|
428
|
+
body: [
|
|
429
|
+
{
|
|
430
|
+
uid: 'group-data-controller',
|
|
431
|
+
name: 'DCGroup',
|
|
432
|
+
description: 'Data Controller users'
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
uid: 'group-admins',
|
|
436
|
+
name: 'Admins',
|
|
437
|
+
description: 'Everything the API offers'
|
|
438
|
+
}
|
|
439
|
+
]
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
id: 'get-group',
|
|
444
|
+
route: 'GET /SASjsApi/group/{uid}',
|
|
445
|
+
description: 'returns a group and its members',
|
|
446
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
447
|
+
request: { token: RESTRICTED, params: { uid: 'group-admins' } },
|
|
448
|
+
expect: {
|
|
449
|
+
status: 200,
|
|
450
|
+
partial: {
|
|
451
|
+
uid: 'group-admins',
|
|
452
|
+
name: 'Admins',
|
|
453
|
+
isActive: true,
|
|
454
|
+
users: [
|
|
455
|
+
{
|
|
456
|
+
uid: ADMIN_UID,
|
|
457
|
+
username: 'admin',
|
|
458
|
+
displayName: 'Demo Admin',
|
|
459
|
+
isAdmin: true
|
|
460
|
+
}
|
|
461
|
+
]
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
id: 'get-group-missing',
|
|
467
|
+
route: 'GET /SASjsApi/group/{uid}',
|
|
468
|
+
description: 'answers 404 for an identifier no group has',
|
|
469
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
470
|
+
request: { token: ADMIN, params: { uid: 'group-none' } },
|
|
471
|
+
expect: { status: 404, error: 'Group not found.' }
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
id: 'get-group-by-name',
|
|
475
|
+
route: 'GET /SASjsApi/group/by/groupname/{name}',
|
|
476
|
+
description: 'finds a group by name',
|
|
477
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
478
|
+
request: { token: ADMIN, params: { name: 'DCGroup' } },
|
|
479
|
+
expect: {
|
|
480
|
+
status: 200,
|
|
481
|
+
partial: { uid: 'group-data-controller', name: 'DCGroup' }
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
id: 'get-group-by-name-missing',
|
|
486
|
+
route: 'GET /SASjsApi/group/by/groupname/{name}',
|
|
487
|
+
description: 'answers 404 for a name no group has',
|
|
488
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
489
|
+
request: { token: ADMIN, params: { name: 'nope' } },
|
|
490
|
+
expect: { status: 404, error: 'Group not found.' }
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
id: 'create-group',
|
|
494
|
+
route: 'POST /SASjsApi/group',
|
|
495
|
+
description: 'creates a group with no members',
|
|
496
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
497
|
+
request: {
|
|
498
|
+
token: ADMIN,
|
|
499
|
+
body: { name: 'NewGroup', description: 'A new group', isActive: true }
|
|
500
|
+
},
|
|
501
|
+
expect: {
|
|
502
|
+
status: 200,
|
|
503
|
+
partial: {
|
|
504
|
+
name: 'NewGroup',
|
|
505
|
+
description: 'A new group',
|
|
506
|
+
isActive: true,
|
|
507
|
+
users: []
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
id: 'create-group-not-admin',
|
|
513
|
+
route: 'POST /SASjsApi/group',
|
|
514
|
+
description: 'refuses a creation by a caller who is not an administrator',
|
|
515
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
516
|
+
request: {
|
|
517
|
+
token: RESTRICTED,
|
|
518
|
+
body: { name: 'NewGroup', description: 'x' }
|
|
519
|
+
},
|
|
520
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
id: 'create-group-duplicate',
|
|
524
|
+
route: 'POST /SASjsApi/group',
|
|
525
|
+
description: 'refuses a name that is taken',
|
|
526
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
527
|
+
request: { token: ADMIN, body: { name: 'DCGroup', description: 'again' } },
|
|
528
|
+
expect: { status: 409, error: 'Group name already exists.' }
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
id: 'add-user-to-group',
|
|
532
|
+
route: 'POST /SASjsApi/group/{groupUid}/{userUid}',
|
|
533
|
+
description: 'adds a member and returns the group',
|
|
534
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
535
|
+
request: {
|
|
536
|
+
token: ADMIN,
|
|
537
|
+
params: { groupUid: 'group-admins', userUid: RESTRICTED_UID }
|
|
538
|
+
},
|
|
539
|
+
expect: {
|
|
540
|
+
status: 200,
|
|
541
|
+
partial: { uid: 'group-admins', name: 'Admins' }
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
id: 'add-user-to-public-group',
|
|
546
|
+
route: 'POST /SASjsApi/group/{groupUid}/{userUid}',
|
|
547
|
+
description: 'refuses a change to the reserved public group',
|
|
548
|
+
identity: exports.PROVIDER_MANAGED,
|
|
549
|
+
request: {
|
|
550
|
+
token: ADMIN,
|
|
551
|
+
params: { groupUid: 'group-public', userUid: RESTRICTED_UID }
|
|
552
|
+
},
|
|
553
|
+
expect: { status: 400, error: "Can't add/remove user to 'public' group." }
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
id: 'add-user-to-group-not-admin',
|
|
557
|
+
route: 'POST /SASjsApi/group/{groupUid}/{userUid}',
|
|
558
|
+
description: 'refuses a membership change by a non-administrator',
|
|
559
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
560
|
+
request: {
|
|
561
|
+
token: RESTRICTED,
|
|
562
|
+
params: { groupUid: 'group-admins', userUid: RESTRICTED_UID }
|
|
563
|
+
},
|
|
564
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
id: 'add-user-to-group-missing-group',
|
|
568
|
+
route: 'POST /SASjsApi/group/{groupUid}/{userUid}',
|
|
569
|
+
description: 'answers 404 for a group that does not exist',
|
|
570
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
571
|
+
request: {
|
|
572
|
+
token: ADMIN,
|
|
573
|
+
params: { groupUid: 'group-none', userUid: RESTRICTED_UID }
|
|
574
|
+
},
|
|
575
|
+
expect: { status: 404, error: 'Group not found.' }
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
id: 'add-user-to-group-provider',
|
|
579
|
+
route: 'POST /SASjsApi/group/{groupUid}/{userUid}',
|
|
580
|
+
description: 'refuses a membership change on a provider-managed group',
|
|
581
|
+
identity: exports.PROVIDER_MANAGED,
|
|
582
|
+
request: {
|
|
583
|
+
token: ADMIN,
|
|
584
|
+
params: { groupUid: 'group-directory', userUid: RESTRICTED_UID }
|
|
585
|
+
},
|
|
586
|
+
expect: {
|
|
587
|
+
status: 405,
|
|
588
|
+
error: "Can't add/remove user to group created by external auth provider."
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
id: 'remove-user-from-group',
|
|
593
|
+
route: 'DELETE /SASjsApi/group/{groupUid}/{userUid}',
|
|
594
|
+
description: 'removes a member and returns the group',
|
|
595
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
596
|
+
request: {
|
|
597
|
+
token: ADMIN,
|
|
598
|
+
params: { groupUid: 'group-data-controller', userUid: RESTRICTED_UID }
|
|
599
|
+
},
|
|
600
|
+
expect: {
|
|
601
|
+
status: 200,
|
|
602
|
+
partial: { uid: 'group-data-controller', name: 'DCGroup', users: [] }
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
id: 'add-provider-user-to-group',
|
|
607
|
+
route: 'POST /SASjsApi/group/{groupUid}/{userUid}',
|
|
608
|
+
description: 'refuses a membership change for a provider-managed user',
|
|
609
|
+
identity: exports.PROVIDER_MANAGED,
|
|
610
|
+
request: {
|
|
611
|
+
token: ADMIN,
|
|
612
|
+
params: { groupUid: 'group-admins', userUid: 'user-directory' }
|
|
613
|
+
},
|
|
614
|
+
expect: {
|
|
615
|
+
status: 405,
|
|
616
|
+
error: "Can't add/remove user to group created by external auth provider."
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
id: 'remove-provider-user-from-group',
|
|
621
|
+
route: 'DELETE /SASjsApi/group/{groupUid}/{userUid}',
|
|
622
|
+
description: 'refuses a membership change for a provider-managed user',
|
|
623
|
+
identity: exports.PROVIDER_MANAGED,
|
|
624
|
+
request: {
|
|
625
|
+
token: ADMIN,
|
|
626
|
+
params: { groupUid: 'group-admins', userUid: 'user-directory' }
|
|
627
|
+
},
|
|
628
|
+
expect: {
|
|
629
|
+
status: 405,
|
|
630
|
+
error: "Can't add/remove user to group created by external auth provider."
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
id: 'remove-user-from-public-group',
|
|
635
|
+
route: 'DELETE /SASjsApi/group/{groupUid}/{userUid}',
|
|
636
|
+
description: 'refuses a change to the reserved public group',
|
|
637
|
+
identity: exports.PROVIDER_MANAGED,
|
|
638
|
+
request: {
|
|
639
|
+
token: ADMIN,
|
|
640
|
+
params: { groupUid: 'group-public', userUid: RESTRICTED_UID }
|
|
641
|
+
},
|
|
642
|
+
expect: { status: 400, error: "Can't add/remove user to 'public' group." }
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
id: 'remove-user-from-group-not-admin',
|
|
646
|
+
route: 'DELETE /SASjsApi/group/{groupUid}/{userUid}',
|
|
647
|
+
description: 'refuses a membership change by a non-administrator',
|
|
648
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
649
|
+
request: {
|
|
650
|
+
token: RESTRICTED,
|
|
651
|
+
params: { groupUid: 'group-data-controller', userUid: RESTRICTED_UID }
|
|
652
|
+
},
|
|
653
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
id: 'remove-user-from-group-missing-user',
|
|
657
|
+
route: 'DELETE /SASjsApi/group/{groupUid}/{userUid}',
|
|
658
|
+
description: 'answers 404 for a member that does not exist',
|
|
659
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
660
|
+
request: {
|
|
661
|
+
token: ADMIN,
|
|
662
|
+
params: { groupUid: 'group-admins', userUid: 'user-none' }
|
|
663
|
+
},
|
|
664
|
+
expect: { status: 404, error: 'User not found.' }
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
id: 'remove-user-from-group-provider',
|
|
668
|
+
route: 'DELETE /SASjsApi/group/{groupUid}/{userUid}',
|
|
669
|
+
description: 'refuses a membership change on a provider-managed group',
|
|
670
|
+
identity: exports.PROVIDER_MANAGED,
|
|
671
|
+
request: {
|
|
672
|
+
token: ADMIN,
|
|
673
|
+
params: { groupUid: 'group-directory', userUid: RESTRICTED_UID }
|
|
674
|
+
},
|
|
675
|
+
expect: {
|
|
676
|
+
status: 405,
|
|
677
|
+
error: "Can't add/remove user to group created by external auth provider."
|
|
678
|
+
}
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
id: 'delete-group',
|
|
682
|
+
route: 'DELETE /SASjsApi/group/{uid}',
|
|
683
|
+
description: 'deletes a group',
|
|
684
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
685
|
+
request: { token: ADMIN, params: { uid: 'group-data-controller' } },
|
|
686
|
+
expect: { status: 200 }
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
id: 'delete-group-not-admin',
|
|
690
|
+
route: 'DELETE /SASjsApi/group/{uid}',
|
|
691
|
+
description: 'refuses a deletion by a caller who is not an administrator',
|
|
692
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
693
|
+
request: { token: RESTRICTED, params: { uid: 'group-admins' } },
|
|
694
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
id: 'delete-group-missing',
|
|
698
|
+
route: 'DELETE /SASjsApi/group/{uid}',
|
|
699
|
+
description: 'answers 404 for an identifier no group has',
|
|
700
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
701
|
+
request: { token: ADMIN, params: { uid: 'group-none' } },
|
|
702
|
+
expect: { status: 404, error: 'Group not found.' }
|
|
703
|
+
},
|
|
704
|
+
// ------------------------------------------------------------ permissions
|
|
705
|
+
{
|
|
706
|
+
id: 'list-permissions-as-admin',
|
|
707
|
+
route: 'GET /SASjsApi/permission',
|
|
708
|
+
description: 'returns every rule to an administrator',
|
|
709
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
710
|
+
request: { token: ADMIN },
|
|
711
|
+
expect: {
|
|
712
|
+
status: 200,
|
|
713
|
+
body: [
|
|
714
|
+
{
|
|
715
|
+
uid: 'rule-routes',
|
|
716
|
+
path: '/SASjsApi/info',
|
|
717
|
+
type: 'Route',
|
|
718
|
+
setting: 'Grant',
|
|
719
|
+
group: {
|
|
720
|
+
uid: 'group-data-controller',
|
|
721
|
+
name: 'DCGroup',
|
|
722
|
+
description: 'Data Controller users',
|
|
723
|
+
isActive: true,
|
|
724
|
+
users: [
|
|
725
|
+
{
|
|
726
|
+
uid: RESTRICTED_UID,
|
|
727
|
+
username: 'user2',
|
|
728
|
+
displayName: 'Demo User',
|
|
729
|
+
isAdmin: false
|
|
730
|
+
}
|
|
731
|
+
]
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
uid: 'rule-execute',
|
|
736
|
+
path: '/SASjsApi/stp/execute',
|
|
737
|
+
type: 'Route',
|
|
738
|
+
setting: 'Grant',
|
|
739
|
+
group: {
|
|
740
|
+
uid: 'group-data-controller',
|
|
741
|
+
name: 'DCGroup',
|
|
742
|
+
description: 'Data Controller users',
|
|
743
|
+
isActive: true,
|
|
744
|
+
users: [
|
|
745
|
+
{
|
|
746
|
+
uid: RESTRICTED_UID,
|
|
747
|
+
username: 'user2',
|
|
748
|
+
displayName: 'Demo User',
|
|
749
|
+
isAdmin: false
|
|
750
|
+
}
|
|
751
|
+
]
|
|
752
|
+
}
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
uid: 'rule-code',
|
|
756
|
+
path: '/SASjsApi/code/execute',
|
|
757
|
+
type: 'Route',
|
|
758
|
+
setting: 'Deny',
|
|
759
|
+
user: {
|
|
760
|
+
uid: RESTRICTED_UID,
|
|
761
|
+
username: 'user2',
|
|
762
|
+
displayName: 'Demo User',
|
|
763
|
+
isAdmin: false
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
]
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
id: 'list-permissions-as-user',
|
|
771
|
+
route: 'GET /SASjsApi/permission',
|
|
772
|
+
description: "returns a caller only their own rules and their groups'",
|
|
773
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
774
|
+
request: { token: RESTRICTED },
|
|
775
|
+
expect: {
|
|
776
|
+
status: 200,
|
|
777
|
+
body: [
|
|
778
|
+
{
|
|
779
|
+
uid: 'rule-routes',
|
|
780
|
+
path: '/SASjsApi/info',
|
|
781
|
+
type: 'Route',
|
|
782
|
+
setting: 'Grant',
|
|
783
|
+
group: {
|
|
784
|
+
uid: 'group-data-controller',
|
|
785
|
+
name: 'DCGroup',
|
|
786
|
+
description: 'Data Controller users',
|
|
787
|
+
isActive: true,
|
|
788
|
+
users: [
|
|
789
|
+
{
|
|
790
|
+
uid: RESTRICTED_UID,
|
|
791
|
+
username: 'user2',
|
|
792
|
+
displayName: 'Demo User',
|
|
793
|
+
isAdmin: false
|
|
794
|
+
}
|
|
795
|
+
]
|
|
796
|
+
}
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
uid: 'rule-execute',
|
|
800
|
+
path: '/SASjsApi/stp/execute',
|
|
801
|
+
type: 'Route',
|
|
802
|
+
setting: 'Grant',
|
|
803
|
+
group: {
|
|
804
|
+
uid: 'group-data-controller',
|
|
805
|
+
name: 'DCGroup',
|
|
806
|
+
description: 'Data Controller users',
|
|
807
|
+
isActive: true,
|
|
808
|
+
users: [
|
|
809
|
+
{
|
|
810
|
+
uid: RESTRICTED_UID,
|
|
811
|
+
username: 'user2',
|
|
812
|
+
displayName: 'Demo User',
|
|
813
|
+
isAdmin: false
|
|
814
|
+
}
|
|
815
|
+
]
|
|
816
|
+
}
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
uid: 'rule-code',
|
|
820
|
+
path: '/SASjsApi/code/execute',
|
|
821
|
+
type: 'Route',
|
|
822
|
+
setting: 'Deny',
|
|
823
|
+
user: {
|
|
824
|
+
uid: RESTRICTED_UID,
|
|
825
|
+
username: 'user2',
|
|
826
|
+
displayName: 'Demo User',
|
|
827
|
+
isAdmin: false
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
]
|
|
831
|
+
}
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
id: 'list-permissions-unauthenticated',
|
|
835
|
+
route: 'GET /SASjsApi/permission',
|
|
836
|
+
description: 'refuses the list with no caller',
|
|
837
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
838
|
+
request: {},
|
|
839
|
+
expect: { status: 401, error: 'Unauthorized' }
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
id: 'create-permission-user',
|
|
843
|
+
route: 'POST /SASjsApi/permission',
|
|
844
|
+
description: 'creates a rule for a user',
|
|
845
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
846
|
+
request: {
|
|
847
|
+
token: ADMIN,
|
|
848
|
+
body: {
|
|
849
|
+
path: '/SASjsApi/code/lint',
|
|
850
|
+
type: 'Route',
|
|
851
|
+
setting: 'Grant',
|
|
852
|
+
principalType: 'user',
|
|
853
|
+
principalId: RESTRICTED_UID
|
|
854
|
+
}
|
|
855
|
+
},
|
|
856
|
+
expect: {
|
|
857
|
+
status: 200,
|
|
858
|
+
partial: { path: '/SASjsApi/code/lint', setting: 'Grant' }
|
|
859
|
+
}
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
id: 'create-permission-group',
|
|
863
|
+
route: 'POST /SASjsApi/permission',
|
|
864
|
+
description: 'creates a rule for a group',
|
|
865
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
866
|
+
request: {
|
|
867
|
+
token: ADMIN,
|
|
868
|
+
body: {
|
|
869
|
+
path: '/SASjsApi/code/lint',
|
|
870
|
+
type: 'Route',
|
|
871
|
+
setting: 'Grant',
|
|
872
|
+
principalType: 'group',
|
|
873
|
+
principalId: 'group-data-controller'
|
|
874
|
+
}
|
|
875
|
+
},
|
|
876
|
+
expect: {
|
|
877
|
+
status: 200,
|
|
878
|
+
partial: {
|
|
879
|
+
path: '/SASjsApi/code/lint',
|
|
880
|
+
setting: 'Grant',
|
|
881
|
+
group: { uid: 'group-data-controller', name: 'DCGroup' }
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
id: 'create-permission-invalid-principal-type',
|
|
887
|
+
route: 'POST /SASjsApi/permission',
|
|
888
|
+
description: 'refuses a principal type that is neither a user nor a group',
|
|
889
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
890
|
+
request: {
|
|
891
|
+
token: ADMIN,
|
|
892
|
+
body: {
|
|
893
|
+
path: '/SASjsApi/code/lint',
|
|
894
|
+
type: 'Route',
|
|
895
|
+
setting: 'Grant',
|
|
896
|
+
principalType: 'role',
|
|
897
|
+
principalId: 'anything'
|
|
898
|
+
}
|
|
899
|
+
},
|
|
900
|
+
expect: {
|
|
901
|
+
status: 400,
|
|
902
|
+
error: 'Invalid principal type. Valid types are user or group.'
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
id: 'create-permission-admin-principal',
|
|
907
|
+
route: 'POST /SASjsApi/permission',
|
|
908
|
+
description: 'refuses a rule assigned to an administrator',
|
|
909
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
910
|
+
request: {
|
|
911
|
+
token: ADMIN,
|
|
912
|
+
body: {
|
|
913
|
+
path: '/SASjsApi/code/lint',
|
|
914
|
+
type: 'Route',
|
|
915
|
+
setting: 'Grant',
|
|
916
|
+
principalType: 'user',
|
|
917
|
+
principalId: ADMIN_UID
|
|
918
|
+
}
|
|
919
|
+
},
|
|
920
|
+
expect: {
|
|
921
|
+
status: 400,
|
|
922
|
+
error: 'Can not add permission for admin user.'
|
|
923
|
+
}
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
id: 'create-permission-not-admin',
|
|
927
|
+
route: 'POST /SASjsApi/permission',
|
|
928
|
+
description: 'refuses a creation by a non-administrator',
|
|
929
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
930
|
+
request: {
|
|
931
|
+
token: RESTRICTED,
|
|
932
|
+
body: {
|
|
933
|
+
path: '/SASjsApi/code/lint',
|
|
934
|
+
type: 'Route',
|
|
935
|
+
setting: 'Grant',
|
|
936
|
+
principalType: 'user',
|
|
937
|
+
principalId: RESTRICTED_UID
|
|
938
|
+
}
|
|
939
|
+
},
|
|
940
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
id: 'create-permission-missing-principal',
|
|
944
|
+
route: 'POST /SASjsApi/permission',
|
|
945
|
+
description: 'answers 404 for a principal that does not exist',
|
|
946
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
947
|
+
request: {
|
|
948
|
+
token: ADMIN,
|
|
949
|
+
body: {
|
|
950
|
+
path: '/SASjsApi/code/lint',
|
|
951
|
+
type: 'Route',
|
|
952
|
+
setting: 'Grant',
|
|
953
|
+
principalType: 'group',
|
|
954
|
+
principalId: 'group-none'
|
|
955
|
+
}
|
|
956
|
+
},
|
|
957
|
+
expect: { status: 404, error: 'Group not found.' }
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
id: 'create-permission-duplicate-group',
|
|
961
|
+
route: 'POST /SASjsApi/permission',
|
|
962
|
+
description: 'refuses a group rule that already exists',
|
|
963
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
964
|
+
request: {
|
|
965
|
+
token: ADMIN,
|
|
966
|
+
body: {
|
|
967
|
+
path: '/SASjsApi/info',
|
|
968
|
+
type: 'Route',
|
|
969
|
+
setting: 'Grant',
|
|
970
|
+
principalType: 'group',
|
|
971
|
+
principalId: 'group-data-controller'
|
|
972
|
+
}
|
|
973
|
+
},
|
|
974
|
+
expect: {
|
|
975
|
+
status: 409,
|
|
976
|
+
error: 'Permission already exists with provided Path, Type and Group.'
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
id: 'create-permission-duplicate',
|
|
981
|
+
route: 'POST /SASjsApi/permission',
|
|
982
|
+
description: 'refuses a rule that already exists',
|
|
983
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
984
|
+
request: {
|
|
985
|
+
token: ADMIN,
|
|
986
|
+
body: {
|
|
987
|
+
path: '/SASjsApi/code/execute',
|
|
988
|
+
type: 'Route',
|
|
989
|
+
setting: 'Deny',
|
|
990
|
+
principalType: 'user',
|
|
991
|
+
principalId: RESTRICTED_UID
|
|
992
|
+
}
|
|
993
|
+
},
|
|
994
|
+
expect: {
|
|
995
|
+
status: 409,
|
|
996
|
+
error: 'Permission already exists with provided Path, Type and User.'
|
|
997
|
+
}
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
id: 'update-permission',
|
|
1001
|
+
route: 'PATCH /SASjsApi/permission/{uid}',
|
|
1002
|
+
description: 'flips a rule from Deny to Grant',
|
|
1003
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1004
|
+
request: {
|
|
1005
|
+
token: ADMIN,
|
|
1006
|
+
params: { uid: 'rule-code' },
|
|
1007
|
+
body: { setting: 'Grant' }
|
|
1008
|
+
},
|
|
1009
|
+
expect: { status: 200, partial: { uid: 'rule-code', setting: 'Grant' } }
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
id: 'update-permission-not-admin',
|
|
1013
|
+
route: 'PATCH /SASjsApi/permission/{uid}',
|
|
1014
|
+
description: 'refuses a change by a non-administrator',
|
|
1015
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1016
|
+
request: {
|
|
1017
|
+
token: RESTRICTED,
|
|
1018
|
+
params: { uid: 'rule-code' },
|
|
1019
|
+
body: { setting: 'Grant' }
|
|
1020
|
+
},
|
|
1021
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
id: 'update-permission-missing',
|
|
1025
|
+
route: 'PATCH /SASjsApi/permission/{uid}',
|
|
1026
|
+
description: 'answers 404 for a rule that does not exist',
|
|
1027
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1028
|
+
request: {
|
|
1029
|
+
token: ADMIN,
|
|
1030
|
+
params: { uid: 'rule-none' },
|
|
1031
|
+
body: { setting: 'Grant' }
|
|
1032
|
+
},
|
|
1033
|
+
expect: { status: 404, error: 'Permission not found.' }
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
id: 'delete-permission',
|
|
1037
|
+
route: 'DELETE /SASjsApi/permission/{uid}',
|
|
1038
|
+
description: 'deletes a rule',
|
|
1039
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1040
|
+
request: { token: ADMIN, params: { uid: 'rule-code' } },
|
|
1041
|
+
expect: { status: 204 }
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
id: 'delete-permission-not-admin',
|
|
1045
|
+
route: 'DELETE /SASjsApi/permission/{uid}',
|
|
1046
|
+
description: 'refuses a deletion by a non-administrator',
|
|
1047
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1048
|
+
request: { token: RESTRICTED, params: { uid: 'rule-code' } },
|
|
1049
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
id: 'delete-permission-missing',
|
|
1053
|
+
route: 'DELETE /SASjsApi/permission/{uid}',
|
|
1054
|
+
description: 'answers 404 for a rule that does not exist',
|
|
1055
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1056
|
+
request: { token: ADMIN, params: { uid: 'rule-none' } },
|
|
1057
|
+
expect: { status: 404, error: 'Permission not found.' }
|
|
1058
|
+
},
|
|
1059
|
+
// --------------------------------------------------------------- clients
|
|
1060
|
+
{
|
|
1061
|
+
id: 'list-clients',
|
|
1062
|
+
route: 'GET /SASjsApi/client',
|
|
1063
|
+
description: 'lists the registered clients',
|
|
1064
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1065
|
+
request: { token: ADMIN },
|
|
1066
|
+
expect: {
|
|
1067
|
+
status: 200,
|
|
1068
|
+
body: [
|
|
1069
|
+
{
|
|
1070
|
+
clientId: 'web_app',
|
|
1071
|
+
clientSecret: 'web_app',
|
|
1072
|
+
accessTokenExpiration: 86400,
|
|
1073
|
+
refreshTokenExpiration: 2592000
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
clientId: 'sasjs',
|
|
1077
|
+
clientSecret: 'sasjs',
|
|
1078
|
+
accessTokenExpiration: 3600,
|
|
1079
|
+
refreshTokenExpiration: 86400
|
|
1080
|
+
}
|
|
1081
|
+
]
|
|
1082
|
+
}
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
id: 'list-clients-not-admin',
|
|
1086
|
+
route: 'GET /SASjsApi/client',
|
|
1087
|
+
description: 'refuses the list to a caller who is not an administrator',
|
|
1088
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1089
|
+
request: { token: RESTRICTED },
|
|
1090
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
id: 'create-client',
|
|
1094
|
+
route: 'POST /SASjsApi/client',
|
|
1095
|
+
description: 'registers a client',
|
|
1096
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1097
|
+
request: {
|
|
1098
|
+
token: ADMIN,
|
|
1099
|
+
body: {
|
|
1100
|
+
clientId: 'new_app',
|
|
1101
|
+
clientSecret: 'new_secret',
|
|
1102
|
+
accessTokenExpiration: 3600,
|
|
1103
|
+
refreshTokenExpiration: 86400
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
expect: {
|
|
1107
|
+
status: 200,
|
|
1108
|
+
body: {
|
|
1109
|
+
clientId: 'new_app',
|
|
1110
|
+
clientSecret: 'new_secret',
|
|
1111
|
+
accessTokenExpiration: 3600,
|
|
1112
|
+
refreshTokenExpiration: 86400
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
},
|
|
1116
|
+
{
|
|
1117
|
+
id: 'create-client-defaults',
|
|
1118
|
+
route: 'POST /SASjsApi/client',
|
|
1119
|
+
description: 'applies the schema defaults when the lifetimes are omitted',
|
|
1120
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1121
|
+
request: {
|
|
1122
|
+
token: ADMIN,
|
|
1123
|
+
body: { clientId: 'default_app', clientSecret: 'default_secret' }
|
|
1124
|
+
},
|
|
1125
|
+
expect: {
|
|
1126
|
+
status: 200,
|
|
1127
|
+
body: {
|
|
1128
|
+
clientId: 'default_app',
|
|
1129
|
+
clientSecret: 'default_secret',
|
|
1130
|
+
accessTokenExpiration: 86400,
|
|
1131
|
+
refreshTokenExpiration: 2592000
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
},
|
|
1135
|
+
{
|
|
1136
|
+
id: 'create-client-not-admin',
|
|
1137
|
+
route: 'POST /SASjsApi/client',
|
|
1138
|
+
description: 'refuses a registration by a non-administrator',
|
|
1139
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1140
|
+
request: {
|
|
1141
|
+
token: RESTRICTED,
|
|
1142
|
+
body: {
|
|
1143
|
+
clientId: 'new_app',
|
|
1144
|
+
clientSecret: 'new_secret',
|
|
1145
|
+
accessTokenExpiration: 3600,
|
|
1146
|
+
refreshTokenExpiration: 86400
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
expect: { status: 401, error: 'Admin account required' }
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
id: 'create-client-duplicate',
|
|
1153
|
+
route: 'POST /SASjsApi/client',
|
|
1154
|
+
description: 'refuses a client id that is registered, with the 403 the server sends',
|
|
1155
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1156
|
+
request: {
|
|
1157
|
+
token: ADMIN,
|
|
1158
|
+
body: {
|
|
1159
|
+
clientId: 'web_app',
|
|
1160
|
+
clientSecret: 'other',
|
|
1161
|
+
accessTokenExpiration: 3600,
|
|
1162
|
+
refreshTokenExpiration: 86400
|
|
1163
|
+
}
|
|
1164
|
+
},
|
|
1165
|
+
expect: { status: 403, error: 'Error: Client ID already exists.' }
|
|
1166
|
+
},
|
|
1167
|
+
// -------------------------------------------------------------- passwords
|
|
1168
|
+
{
|
|
1169
|
+
id: 'update-password',
|
|
1170
|
+
route: 'PATCH /SASjsApi/auth/updatePassword',
|
|
1171
|
+
description: 'changes the caller’s own password',
|
|
1172
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1173
|
+
request: {
|
|
1174
|
+
token: RESTRICTED,
|
|
1175
|
+
body: { currentPassword: 'user123', newPassword: 'changed123' }
|
|
1176
|
+
},
|
|
1177
|
+
expect: { status: 204 }
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
id: 'update-password-missing-field',
|
|
1181
|
+
route: 'PATCH /SASjsApi/auth/updatePassword',
|
|
1182
|
+
description: 'refuses a body without the current password',
|
|
1183
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1184
|
+
request: { token: RESTRICTED, body: { newPassword: 'changed123' } },
|
|
1185
|
+
expect: { status: 400, error: '"currentPassword" is required' }
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
id: 'update-password-unauthenticated',
|
|
1189
|
+
route: 'PATCH /SASjsApi/auth/updatePassword',
|
|
1190
|
+
description: 'refuses the change with no caller',
|
|
1191
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1192
|
+
request: {
|
|
1193
|
+
body: { currentPassword: 'user123', newPassword: 'changed123' }
|
|
1194
|
+
},
|
|
1195
|
+
expect: { status: 401, error: 'Unauthorized' }
|
|
1196
|
+
},
|
|
1197
|
+
{
|
|
1198
|
+
id: 'update-password-wrong-current',
|
|
1199
|
+
route: 'PATCH /SASjsApi/auth/updatePassword',
|
|
1200
|
+
description: 'refuses a change whose current password is wrong',
|
|
1201
|
+
identity: exports.ADMIN_SIGNED_IN,
|
|
1202
|
+
request: {
|
|
1203
|
+
token: RESTRICTED,
|
|
1204
|
+
body: { currentPassword: 'wrong', newPassword: 'changed123' }
|
|
1205
|
+
},
|
|
1206
|
+
expect: { status: 403, error: 'Invalid current password!' }
|
|
1207
|
+
},
|
|
1208
|
+
{
|
|
1209
|
+
id: 'update-password-provider',
|
|
1210
|
+
route: 'PATCH /SASjsApi/auth/updatePassword',
|
|
1211
|
+
description: 'refuses a password change on a provider-managed account',
|
|
1212
|
+
identity: exports.PROVIDER_MANAGED,
|
|
1213
|
+
request: {
|
|
1214
|
+
token: 'dir-access',
|
|
1215
|
+
body: { currentPassword: 'dir123', newPassword: 'changed123' }
|
|
1216
|
+
},
|
|
1217
|
+
expect: {
|
|
1218
|
+
status: 405,
|
|
1219
|
+
error: 'Can not update password of user that is created by an external auth provider.'
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
];
|