@nsshunt/stsdatamanagement 1.10.5 → 1.11.2
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/blcauth.js +180 -111
- package/dbaccess.js +7 -1
- package/dberrors.js +57 -0
- package/package.json +4 -4
package/blcauth.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { status: { status } } = require('@nsshunt/stsutils');
|
|
2
2
|
const bcrypt = require('bcryptjs');
|
|
3
|
+
const { STSResourceNotFoundError, STSDatabaseAccessError, STSResourceMalformedError } = require('./dberrors');
|
|
3
4
|
|
|
4
5
|
class BLCAuth
|
|
5
6
|
{
|
|
@@ -24,32 +25,35 @@ class BLCAuth
|
|
|
24
25
|
try
|
|
25
26
|
{
|
|
26
27
|
let userid = BLCAuth.USER_ID_PREFIX + email;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
let payload =
|
|
45
|
-
{
|
|
46
|
-
id: userid
|
|
47
|
-
,name: name
|
|
48
|
-
,email: email
|
|
49
|
-
,roles: roles
|
|
50
|
-
}
|
|
28
|
+
if (await this.#ResourceExists('User', userid)) {
|
|
29
|
+
return {
|
|
30
|
+
status: status.conflict,
|
|
31
|
+
error: 'User already exists.',
|
|
32
|
+
detail: { message: `User already exists: [${userid}]` }
|
|
33
|
+
};
|
|
34
|
+
} else {
|
|
35
|
+
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
|
36
|
+
|
|
37
|
+
let user = {
|
|
38
|
+
id: userid
|
|
39
|
+
,name: name
|
|
40
|
+
,email: email
|
|
41
|
+
,hash: hashedPassword
|
|
42
|
+
,roles: roles
|
|
43
|
+
};
|
|
51
44
|
|
|
52
|
-
|
|
45
|
+
await this.#accessLayer.saveResource(BLCAuth.SYSTEM_USER_ID, user.id, user);
|
|
46
|
+
|
|
47
|
+
let payload =
|
|
48
|
+
{
|
|
49
|
+
id: userid
|
|
50
|
+
,name: name
|
|
51
|
+
,email: email
|
|
52
|
+
,roles: roles
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { status: status.success, detail: payload };
|
|
56
|
+
}
|
|
53
57
|
} catch (error)
|
|
54
58
|
{
|
|
55
59
|
console.error(error);
|
|
@@ -63,20 +67,23 @@ class BLCAuth
|
|
|
63
67
|
{
|
|
64
68
|
const { name, permissions } = rolePermissions;
|
|
65
69
|
let roleId = BLCAuth.ROLE_ID_PREFIX + name;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
if (await this.#ResourceExists('Role', roleId)) {
|
|
71
|
+
return {
|
|
72
|
+
status: status.conflict,
|
|
73
|
+
error: 'Role already exists.',
|
|
74
|
+
detail: { message: `Role already exists: [${roleId}]` }
|
|
75
|
+
};
|
|
76
|
+
} else {
|
|
77
|
+
let roleResource = {
|
|
78
|
+
id: roleId,
|
|
79
|
+
name: name,
|
|
80
|
+
permissions: permissions
|
|
81
|
+
}
|
|
76
82
|
|
|
77
|
-
|
|
83
|
+
await this.#accessLayer.saveResource(BLCAuth.SYSTEM_USER_ID, roleId, roleResource);
|
|
78
84
|
|
|
79
|
-
|
|
85
|
+
return { status: status.success, detail: roleResource };
|
|
86
|
+
}
|
|
80
87
|
} catch (error)
|
|
81
88
|
{
|
|
82
89
|
console.error(error);
|
|
@@ -84,28 +91,62 @@ class BLCAuth
|
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
|
|
94
|
+
async #GetResource(resource, resourceId) {
|
|
95
|
+
let resourceRaw = null;
|
|
96
|
+
try {
|
|
97
|
+
resourceRaw = await this.#accessLayer.getLatestResource(resourceId);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw new STSDatabaseAccessError(resource, resourceId, error);
|
|
100
|
+
}
|
|
101
|
+
if (resourceRaw.status !== 200) {
|
|
102
|
+
throw new STSResourceNotFoundError(resource, resourceId);
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
return JSON.parse(resourceRaw.detail.resdesc);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw new STSResourceMalformedError(resource, resourceId, resourceRaw.detail.resdesc, error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async #ResourceExists(resource, resourceId) {
|
|
112
|
+
try {
|
|
113
|
+
await this.#GetResource(resource, resourceId);
|
|
114
|
+
return true;
|
|
115
|
+
} catch (error) {
|
|
116
|
+
if (error instanceof STSResourceNotFoundError) {
|
|
117
|
+
return false;
|
|
118
|
+
} else {
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async GetUser(email) {
|
|
125
|
+
return this.#GetResource('User', BLCAuth.USER_ID_PREFIX + email);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async GetRole(role) {
|
|
129
|
+
return this.#GetResource('Role', BLCAuth.ROLE_ID_PREFIX + role);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async GetApplication(application) {
|
|
133
|
+
return this.#GetResource('Application', BLCAuth.APPLICATION_ID_PREFIX + application);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async GetAPI(identifier) {
|
|
137
|
+
return this.#GetResource('API', BLCAuth.API_ID_PREFIX + identifier);
|
|
138
|
+
}
|
|
139
|
+
|
|
87
140
|
async GetUserPermissions(email)
|
|
88
141
|
{
|
|
89
142
|
try
|
|
90
143
|
{
|
|
91
|
-
let
|
|
92
|
-
let existingUser = await this.#accessLayer.getLatestResource(userid);
|
|
93
|
-
if (existingUser.status !== 200) {
|
|
94
|
-
return { status: status.notfound, error: 'User not found.', detail: { message: 'User not found.' }};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
let userResource = JSON.parse(existingUser.detail.resdesc);
|
|
98
|
-
|
|
144
|
+
let userResource = await this.GetUser(email);
|
|
99
145
|
let permissions = [ ];
|
|
100
146
|
|
|
101
147
|
for (let i=0; i < userResource.roles.length; i++) {
|
|
102
148
|
let role = userResource.roles[i];
|
|
103
|
-
let
|
|
104
|
-
let existingRole = await this.#accessLayer.getLatestResource(roleId);
|
|
105
|
-
if (existingRole.status !== 200) {
|
|
106
|
-
return { status: status.notfound, error: 'Role not found.', detail: { message: 'Role not found.' }};
|
|
107
|
-
}
|
|
108
|
-
let roleResource = JSON.parse(existingRole.detail.resdesc);
|
|
149
|
+
let roleResource = await this.GetRole(role);
|
|
109
150
|
for (let j=0; j < roleResource.permissions.length; j++) {
|
|
110
151
|
let permission = roleResource.permissions[j];
|
|
111
152
|
if (!permissions.includes(permission)) {
|
|
@@ -128,23 +169,25 @@ class BLCAuth
|
|
|
128
169
|
{
|
|
129
170
|
const { clientId } = application;
|
|
130
171
|
let applicationId = BLCAuth.APPLICATION_ID_PREFIX + clientId;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
{
|
|
138
|
-
|
|
139
|
-
|
|
172
|
+
if (await this.#ResourceExists('Application', applicationId)) {
|
|
173
|
+
return {
|
|
174
|
+
status: status.conflict,
|
|
175
|
+
error: 'Application already exists.',
|
|
176
|
+
detail: { message: `Application already exists: [${applicationId}]` }
|
|
177
|
+
};
|
|
178
|
+
} else {
|
|
179
|
+
let payload = {
|
|
180
|
+
applicationId,
|
|
181
|
+
...application
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
await this.#accessLayer.saveResource(BLCAuth.SYSTEM_USER_ID, applicationId, payload);
|
|
185
|
+
|
|
186
|
+
// Client Secret is not returned. Seperate function used to display this field.
|
|
187
|
+
delete payload.clientSecret;
|
|
188
|
+
|
|
189
|
+
return { status: status.success, detail: payload };
|
|
140
190
|
}
|
|
141
|
-
|
|
142
|
-
await this.#accessLayer.saveResource(BLCAuth.SYSTEM_USER_ID, applicationId, payload);
|
|
143
|
-
|
|
144
|
-
// Client Secret is not returned. Seperate function used to display this field.
|
|
145
|
-
delete payload.clientSecret;
|
|
146
|
-
|
|
147
|
-
return { status: status.success, detail: payload };
|
|
148
191
|
} catch (error)
|
|
149
192
|
{
|
|
150
193
|
console.error(error);
|
|
@@ -152,47 +195,73 @@ class BLCAuth
|
|
|
152
195
|
}
|
|
153
196
|
}
|
|
154
197
|
|
|
198
|
+
#ValidateApplication = async (apiPermissions, app) => {
|
|
199
|
+
const { clientId, clientName, permissions } = app;
|
|
200
|
+
|
|
201
|
+
let applicationResource = null;
|
|
202
|
+
try {
|
|
203
|
+
applicationResource = await this.GetApplication(clientId)
|
|
204
|
+
} catch (error) {
|
|
205
|
+
return { status: status.notfound , error: 'Cannot find client application.', detail: { message: error.message }};
|
|
206
|
+
}
|
|
207
|
+
if (applicationResource.clientName.localeCompare(clientName) !== 0) {
|
|
208
|
+
return { status: status.error, error: 'clientName mismatch.', detail: { message: `clientName mismatch: Value: [${clientName}], Expecting: [${applicationResource.clientName}]` }};
|
|
209
|
+
}
|
|
210
|
+
for (let j=0; j < permissions.length; j++) {
|
|
211
|
+
const permission = permissions[j];
|
|
212
|
+
if (!apiPermissions.includes(permission)) {
|
|
213
|
+
return { status: status.error, error: 'M2M permission not found within API available permission list.', detail: { message: `Permission not found within API available permission list: [${permission}]` }};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
155
219
|
async AddAPI(api)
|
|
156
220
|
{
|
|
157
221
|
try
|
|
158
222
|
{
|
|
159
|
-
const { M2MApplications, permissions, identifier } = api;
|
|
223
|
+
const { M2MApplications, permissions: apiPermissions, identifier, SPA } = api;
|
|
224
|
+
|
|
160
225
|
let APIidentifier = BLCAuth.API_ID_PREFIX + identifier;
|
|
161
|
-
let existingAPI = await this.#accessLayer.getLatestResource(APIidentifier);
|
|
162
|
-
if (existingAPI.status === 200) {
|
|
163
|
-
return { status: status.conflict, error: 'API already exists.', detail: { message: `API already exists: [${APIidentifier}]` }};
|
|
164
|
-
}
|
|
165
226
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
227
|
+
if (await this.#ResourceExists('API', APIidentifier)) {
|
|
228
|
+
return {
|
|
229
|
+
status: status.conflict,
|
|
230
|
+
error: 'API already exists.',
|
|
231
|
+
detail: { message: `API already exists: [${APIidentifier}]` }
|
|
232
|
+
};
|
|
233
|
+
} else {
|
|
234
|
+
// Validate M2MApplications
|
|
235
|
+
if (M2MApplications) {
|
|
236
|
+
for (let i=0; i < M2MApplications.length; i++) {
|
|
237
|
+
let retVal = await this.#ValidateApplication(apiPermissions, M2MApplications[i]);
|
|
238
|
+
if (retVal !== null) {
|
|
239
|
+
return retVal;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
177
242
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
243
|
+
|
|
244
|
+
// Validate SPA
|
|
245
|
+
if (SPA) {
|
|
246
|
+
for (let i=0; i < SPA.length; i++) {
|
|
247
|
+
let retVal = await this.#ValidateApplication(apiPermissions, SPA[i]);
|
|
248
|
+
if (retVal !== null) {
|
|
249
|
+
return retVal;
|
|
250
|
+
}
|
|
182
251
|
}
|
|
183
252
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
253
|
+
|
|
254
|
+
// Client Secret is not returned. Separate function used to display this field.
|
|
255
|
+
let payload =
|
|
256
|
+
{
|
|
257
|
+
APIidentifier,
|
|
258
|
+
...api
|
|
259
|
+
}
|
|
192
260
|
|
|
193
|
-
|
|
261
|
+
await this.#accessLayer.saveResource(BLCAuth.SYSTEM_USER_ID, APIidentifier, payload);
|
|
194
262
|
|
|
195
|
-
|
|
263
|
+
return { status: status.success, detail: payload };
|
|
264
|
+
}
|
|
196
265
|
} catch (error)
|
|
197
266
|
{
|
|
198
267
|
console.error(error);
|
|
@@ -200,23 +269,23 @@ class BLCAuth
|
|
|
200
269
|
}
|
|
201
270
|
}
|
|
202
271
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
return application;
|
|
272
|
+
/*
|
|
273
|
+
// Get all the scopes (permissions) defined for this application (client_id) using this API (audience).
|
|
274
|
+
// client_secret required for M2M applications. Not required for SPA.
|
|
275
|
+
async GetScopes(client_id, client_secret, audience) {
|
|
276
|
+
|
|
210
277
|
}
|
|
211
278
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
279
|
+
// Get all the scopes that the user (user_id) has previously consented for this application (client_id) using this API (audience).
|
|
280
|
+
async GetConsentedScopes(user_id, client_id, audience) {
|
|
281
|
+
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Update scopes that the user (user_id) has consented for this application (client_id) using this API (audience).
|
|
285
|
+
async UpdateConsentedScopes(user_id, client_id, audience, scopes) {
|
|
286
|
+
|
|
219
287
|
}
|
|
288
|
+
*/
|
|
220
289
|
}
|
|
221
290
|
|
|
222
291
|
module.exports = { BLCAuth };
|
package/dbaccess.js
CHANGED
|
@@ -5,5 +5,11 @@ const { PGUtils } = require('./pgutils');
|
|
|
5
5
|
const { BLCAuth } = require('./blcauth');
|
|
6
6
|
const { DatabaseUtils } = require('./databaseutils');
|
|
7
7
|
const { DataGenerator } = require('./datagenerator');
|
|
8
|
+
const { STSDataManagementError, STSResourceNotFoundError, STSResourceMalformedError, STSDatabaseAccessError } = require('./dberrors');
|
|
8
9
|
|
|
9
|
-
module.exports = {
|
|
10
|
+
module.exports = {
|
|
11
|
+
// Utilities and Helpers
|
|
12
|
+
PGPoolManager, L1Cache, PGAccessLayer, PGUtils, BLCAuth, DatabaseUtils, DataGenerator,
|
|
13
|
+
// Errors
|
|
14
|
+
STSDataManagementError, STSResourceNotFoundError, STSResourceMalformedError, STSDatabaseAccessError
|
|
15
|
+
};
|
package/dberrors.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const { status: { status } } = require('@nsshunt/stsutils');
|
|
2
|
+
|
|
3
|
+
// https://rclayton.silvrback.com/custom-errors-in-node-js
|
|
4
|
+
class STSDataManagementError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
|
|
8
|
+
// Ensure the name of this error is the same as the class name
|
|
9
|
+
this.name = this.constructor.name;
|
|
10
|
+
|
|
11
|
+
// This clips the constructor invocation from the stack trace.
|
|
12
|
+
// It's not absolutely essential, but it does make the stack trace a little nicer.
|
|
13
|
+
// @see Node.js reference (bottom)
|
|
14
|
+
Error.captureStackTrace(this, this.constructor);
|
|
15
|
+
|
|
16
|
+
this.detail = null;
|
|
17
|
+
this.status = status.error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class STSResourceNotFoundError extends STSDataManagementError {
|
|
22
|
+
constructor(resource, resourceId) {
|
|
23
|
+
super(`Resource [${resource}] not found using ID: [${resourceId}]`);
|
|
24
|
+
this.detail = {
|
|
25
|
+
resourceId,
|
|
26
|
+
resource
|
|
27
|
+
}
|
|
28
|
+
this.status = status.notfound;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class STSResourceMalformedError extends STSDataManagementError {
|
|
33
|
+
constructor(resource, resourceId, resourceRaw, error) {
|
|
34
|
+
super(`Resource [${resource}:{resourceId}] is malformed.`);
|
|
35
|
+
this.detail = {
|
|
36
|
+
resourceId,
|
|
37
|
+
resource,
|
|
38
|
+
resourceRaw,
|
|
39
|
+
error
|
|
40
|
+
}
|
|
41
|
+
this.status = status.error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class STSDatabaseAccessError extends STSDataManagementError {
|
|
46
|
+
constructor(resource, resourceId, error) {
|
|
47
|
+
super(`Database access error reading resource: [${resource}:{resourceId}]`);
|
|
48
|
+
this.detail = {
|
|
49
|
+
resourceId,
|
|
50
|
+
resource,
|
|
51
|
+
error
|
|
52
|
+
}
|
|
53
|
+
this.status = status.error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { STSDataManagementError, STSResourceNotFoundError, STSResourceMalformedError, STSDatabaseAccessError }
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nsshunt/stsdatamanagement",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.2",
|
|
4
4
|
"description": "STS Data Management Modules, Utilities and Services",
|
|
5
5
|
"main": "dbaccess.js",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@nsshunt/stsconfig": "^1.20.0",
|
|
8
8
|
"@nsshunt/stsinstrumentation": "^6.4.3",
|
|
9
|
-
"@nsshunt/stsutils": "^1.
|
|
9
|
+
"@nsshunt/stsutils": "^1.8.5",
|
|
10
10
|
"axios": "^0.26.0",
|
|
11
11
|
"bcryptjs": "^2.4.3",
|
|
12
12
|
"cli-progress": "^3.10.0",
|
|
13
13
|
"colors": "^1.4.0",
|
|
14
14
|
"debug": "^4.3.4",
|
|
15
|
-
"ioredis": "^
|
|
15
|
+
"ioredis": "^5.0.3",
|
|
16
16
|
"pg": "^8.7.3",
|
|
17
17
|
"pg-copy-streams": "^6.0.2",
|
|
18
18
|
"prompts": "^2.4.2",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@babel/eslint-parser": "^7.17.0",
|
|
32
32
|
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
|
33
33
|
"@babel/plugin-proposal-private-methods": "^7.16.11",
|
|
34
|
-
"eslint": "^8.
|
|
34
|
+
"eslint": "^8.12.0",
|
|
35
35
|
"jest": "^27.5.1"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|