@nsshunt/stsdatamanagement 1.8.1 → 1.10.1

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.
@@ -10,11 +10,4 @@ updates:
10
10
  - package-ecosystem: "npm" # See documentation for possible values
11
11
  directory: "/" # Location of package manifests
12
12
  schedule:
13
- interval: "weekly"
14
-
15
- - package-ecosystem: "github-actions"
16
- # Workflow files stored in the
17
- # default location of `.github/workflows`
18
- directory: "/"
19
- schedule:
20
- interval: "weekly"
13
+ interval: "monthly"
package/blcauth.js CHANGED
@@ -5,6 +5,9 @@ class BLCAuth
5
5
  {
6
6
  static SYSTEM_USER_ID = "STS_SYSTEM";
7
7
  static USER_ID_PREFIX = "USR_";
8
+ static ROLE_ID_PREFIX = "ROLE_";
9
+ static APPLICATION_ID_PREFIX = "APP_";
10
+ static API_ID_PREFIX = "API_";
8
11
 
9
12
  #accessLayer = null;
10
13
 
@@ -14,17 +17,15 @@ class BLCAuth
14
17
  }
15
18
 
16
19
  // Normally, register would be provided by a hardened dedicated authentication server.
17
- async registeruser(user)
20
+ async AddUser(user)
18
21
  {
19
- let { name, password, email } = user;
22
+ let { name, password, email, roles } = user;
20
23
  const saltRounds = 10;
21
24
  try
22
25
  {
23
26
  let userid = BLCAuth.USER_ID_PREFIX + email;
24
27
  let existingUser = await this.#accessLayer.getLatestResource(userid);
25
- if (existingUser.status === 200)
26
- {
27
- //@@ should throw exception here
28
+ if (existingUser.status === 200) {
28
29
  return { status: status.conflict, error: 'User already exists.', detail: { message: 'User already exists.' }};
29
30
  }
30
31
 
@@ -35,14 +36,17 @@ class BLCAuth
35
36
  ,name: name
36
37
  ,email: email
37
38
  ,hash: hashedPassword
39
+ ,roles: roles
38
40
  };
39
41
 
40
42
  await this.#accessLayer.saveResource(BLCAuth.SYSTEM_USER_ID, user.id, user);
41
43
 
42
44
  let payload =
43
45
  {
44
- name: user.name
45
- ,email: user.email
46
+ id: userid
47
+ ,name: name
48
+ ,email: email
49
+ ,roles: roles
46
50
  }
47
51
 
48
52
  return { status: status.success, detail: payload };
@@ -52,6 +56,167 @@ class BLCAuth
52
56
  throw new Error({ status: status.error, error: 'Operation was not successful', detail: error });
53
57
  }
54
58
  }
59
+
60
+ async AddRolePermissions(rolePermissions)
61
+ {
62
+ try
63
+ {
64
+ const { name, permissions } = rolePermissions;
65
+ let roleId = BLCAuth.ROLE_ID_PREFIX + name;
66
+ let existingRole = await this.#accessLayer.getLatestResource(roleId);
67
+ if (existingRole.status === 200) {
68
+ return { status: status.conflict, error: 'Role already exists.', detail: { message: 'Role already exists.' }};
69
+ }
70
+
71
+ let roleResource = {
72
+ id: roleId,
73
+ name: name,
74
+ permissions: permissions
75
+ }
76
+
77
+ await this.#accessLayer.saveResource(BLCAuth.SYSTEM_USER_ID, roleId, roleResource);
78
+
79
+ return { status: status.success, detail: roleResource };
80
+ } catch (error)
81
+ {
82
+ console.error(error);
83
+ throw new Error({ status: status.error, error: 'Operation was not successful', detail: error });
84
+ }
85
+ }
86
+
87
+ async GetUserPermissions(email)
88
+ {
89
+ try
90
+ {
91
+ let userid = BLCAuth.USER_ID_PREFIX + email;
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
+
99
+ let permissions = [ ];
100
+
101
+ for (let i=0; i < userResource.roles.length; i++) {
102
+ let role = userResource.roles[i];
103
+ let roleId = BLCAuth.ROLE_ID_PREFIX + role;
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);
109
+ for (let j=0; j < roleResource.permissions.length; j++) {
110
+ let permission = roleResource.permissions[j];
111
+ if (!permissions.includes(permission)) {
112
+ permissions.push(permission);
113
+ }
114
+ }
115
+ }
116
+
117
+ return { status: status.success, detail: permissions };
118
+ } catch (error)
119
+ {
120
+ console.error(error);
121
+ throw new Error({ status: status.error, error: 'Operation was not successful', detail: error });
122
+ }
123
+ }
124
+
125
+ async AddApplication(application)
126
+ {
127
+ try
128
+ {
129
+ const { clientId } = application;
130
+ let applicationId = BLCAuth.APPLICATION_ID_PREFIX + clientId;
131
+ let existingApplication = await this.#accessLayer.getLatestResource(applicationId);
132
+ if (existingApplication.status === 200) {
133
+ return { status: status.conflict, error: 'Application already exists.', detail: { message: `Application already exists: [${applicationId}]` }};
134
+ }
135
+
136
+ let payload =
137
+ {
138
+ applicationId: applicationId,
139
+ ...application
140
+ }
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
+ } catch (error)
149
+ {
150
+ console.error(error);
151
+ throw new Error({ status: status.error, error: 'Operation was not successful', detail: error });
152
+ }
153
+ }
154
+
155
+ async AddAPI(api)
156
+ {
157
+ try
158
+ {
159
+ const { APIId, M2MApplications, permissions } = api;
160
+ let APIidentifier = BLCAuth.API_ID_PREFIX + APIId;
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
+
166
+ // Validate M2MApplications
167
+ for (let i=0; i < M2MApplications.length; i++) {
168
+ const m2mapplication = M2MApplications[i];
169
+ let appid = BLCAuth.APPLICATION_ID_PREFIX + m2mapplication.clientId;
170
+ let retVal = await this.#accessLayer.getLatestResource(appid);
171
+ if (retVal.status !== 200) {
172
+ return { status: status.notfound , error: 'Cannot find client application.', detail: { message: `Cannot find client application: [${appid}].` }};
173
+ }
174
+ let applicationResource = JSON.parse(retVal.detail.resdesc);
175
+ if (applicationResource.clientName.localeCompare(m2mapplication.clientName) !== 0) {
176
+ return { status: status.error, error: 'clientName mismatch.', detail: { message: `clientName mismatch: Value: [${m2mapplication.clientName}], Expecting: [${applicationResource.clientName}]` }};
177
+ }
178
+ for (let j=0; j < m2mapplication.permissions.length; j++) {
179
+ const permission = m2mapplication.permissions[j];
180
+ if (!permissions.includes(permission)) {
181
+ return { status: status.error, error: 'M2M permission not found within API available permission list.', detail: { message: `M2M permission not found within API available permission list: [${permission}]` }};
182
+ }
183
+ }
184
+ }
185
+
186
+ // Client Secret is not returned. Seperate function used to display this field.
187
+ let payload =
188
+ {
189
+ APIidentifier: APIidentifier,
190
+ ...api
191
+ }
192
+
193
+ await this.#accessLayer.saveResource(BLCAuth.SYSTEM_USER_ID, APIidentifier, payload);
194
+
195
+ return { status: status.success, detail: payload };
196
+ } catch (error)
197
+ {
198
+ console.error(error);
199
+ throw new Error({ status: status.error, error: 'Operation was not successful', detail: error });
200
+ }
201
+ }
202
+
203
+ async GetApplication(clientId) {
204
+ let clientIdentifier = BLCAuth.APPLICATION_ID_PREFIX + clientId;
205
+ let application = await this.#accessLayer.getLatestResource(clientIdentifier);
206
+ if (application.status !== 200) {
207
+ return { status: status.notfound, error: 'Application not found.', detail: { message: `Application not found: [${clientId}]` }};
208
+ }
209
+ return application;
210
+ }
211
+
212
+ async GetAPI(APIId) {
213
+ let APIidentifier = BLCAuth.API_ID_PREFIX + APIId;
214
+ let api = await this.#accessLayer.getLatestResource(APIidentifier);
215
+ if (api.status !== 200) {
216
+ return { status: status.notfound, error: 'API not found.', detail: { message: `API not found: [${APIId}]` }};
217
+ }
218
+ return api;
219
+ }
55
220
  }
56
221
 
57
222
  module.exports = { BLCAuth };
package/databaseutils.js CHANGED
@@ -1,6 +1,9 @@
1
1
  const prompts = require('prompts');
2
- const goptions = require('@nsshunt/stsconfig').$options;
2
+ const fs = require('fs');
3
3
  require('colors');
4
+ let crypto = require('crypto');
5
+
6
+ const goptions = require('@nsshunt/stsconfig').$options;
4
7
 
5
8
  const { PGPoolManager } = require('./pgpoolmanager');
6
9
  const { PGAccessLayer } = require('./pgaccesslayer');
@@ -20,22 +23,93 @@ class DatabaseUtils
20
23
  this.#accessLayer = accessLayer;
21
24
  }
22
25
 
26
+ #_RegisterRolesAndPermissions = async(blcauth) => {
27
+ this.#debug(`Registering Roles and Role Permissions.`.yellow);
28
+ console.log(`Registering Roles and Role Permissions.`.yellow);
29
+ const rolePermissionFile = goptions.databasescriptfolder + '/role-permission.json'
30
+ const rawdata = fs.readFileSync(rolePermissionFile);
31
+ let rolePermissions = JSON.parse(rawdata);
32
+ for (const [, rolePermission] of Object.entries(rolePermissions)) {
33
+ let retVal = await blcauth.AddRolePermissions(rolePermission);
34
+ if (retVal.status !== 200) {
35
+ console.log(`Role Permission registered: ${JSON.stringify(retVal)}`.red);
36
+ return false;
37
+ } else {
38
+ console.log(`Role Permission registered: ${JSON.stringify(retVal)}`);
39
+ }
40
+ }
41
+ return true;
42
+ }
43
+
44
+ #_RegisterUsersAndRoles = async(blcauth) => {
45
+ this.#debug(`Registering Users and Roles.`.yellow);
46
+ console.log(`Registering Users and Roles.`.yellow);
47
+ const roleFile = goptions.databasescriptfolder + '/user-role.json'
48
+ let rawdata = fs.readFileSync(roleFile);
49
+ let userroles = JSON.parse(rawdata);
50
+ for (const [, user] of Object.entries(userroles)) {
51
+ let retVal = await blcauth.AddUser(user);
52
+ if (retVal.status !== 200) {
53
+ console.log(`Role registered: ${JSON.stringify(retVal)}`.red);
54
+ return false;
55
+ } else {
56
+ console.log(`Role registered: ${JSON.stringify(retVal)}`);
57
+ }
58
+ }
59
+ return true;
60
+ }
61
+
62
+ #_RegisterApplications = async(blcauth) => {
63
+ this.#debug(`Registering Applications.`.yellow);
64
+ console.log(`Registering Applications.`.yellow);
65
+ const roleFile = goptions.databasescriptfolder + '/applications.json'
66
+ let rawdata = fs.readFileSync(roleFile);
67
+ let applications = JSON.parse(rawdata);
68
+ for (const [, application] of Object.entries(applications)) {
69
+ application.clientSecret = crypto.randomBytes(32).toString('base64');
70
+ let retVal = await blcauth.AddApplication(application);
71
+ if (retVal.status !== 200) {
72
+ console.log(`Application registered: ${JSON.stringify(retVal)}`.red);
73
+ return false;
74
+ } else {
75
+ console.log(`Application registered: ${JSON.stringify(retVal)}`);
76
+ }
77
+ }
78
+ return true;
79
+ }
80
+
81
+ #_RegisterAPIs = async(blcauth) => {
82
+ this.#debug(`Registering APIs.`.yellow);
83
+ console.log(`Registering APIs.`.yellow);
84
+ const roleFile = goptions.databasescriptfolder + '/apis.json'
85
+ let rawdata = fs.readFileSync(roleFile);
86
+ let apis = JSON.parse(rawdata);
87
+ for (const [, api] of Object.entries(apis)) {
88
+ let retVal = await blcauth.AddAPI(api);
89
+ if (retVal.status !== 200) {
90
+ console.log(`API registered: ${JSON.stringify(retVal)}`.red);
91
+ return false;
92
+ } else {
93
+ console.log(`API registered: ${JSON.stringify(retVal)}`);
94
+ }
95
+ }
96
+ return true;
97
+ }
98
+
23
99
  // options ::= { start: <int>, entries: <int>, minextradata: <int>, maxextradata: <int>,
24
100
  // user: { name: <string>, password: <string>, email: <string> } }
25
- static createfreshdatabase = async (options) =>
101
+ createfreshdatabase = async (options) =>
26
102
  {
27
- let ns = `proc:${process.pid}:DatabaseUtils`; // namespace for debug
28
- let debug = require('debug')(ns);
29
103
  let fname = 'createfreshdatabase';
30
- const { start, entries, minextradata, maxextradata, user } = options;
104
+ const { start, entries, minextradata, maxextradata } = options;
31
105
  let builddbscript = goptions.databasescriptfolder + '/builddb.sql'
32
- debug(`Database Build Script: [${builddbscript}]`.yellow);
106
+ this.#debug(`Database Build Script: [${builddbscript}]`.yellow);
33
107
 
34
108
  try
35
109
  {
36
110
  try
37
111
  {
38
- debug(`Dropping database.`.yellow);
112
+ this.#debug(`Dropping database.`.yellow);
39
113
  await PGUtils.dropdatabase();
40
114
  } catch (error)
41
115
  {
@@ -44,35 +118,54 @@ class DatabaseUtils
44
118
  }
45
119
 
46
120
  // Create a new empty database
47
- debug(`Creating new empty database.`.yellow);
121
+ this.#debug(`Creating new empty database.`.yellow);
48
122
  await PGUtils.createdatabase();
49
123
 
50
124
  let localAccesslayer = new PGAccessLayer(new PGPoolManager());
51
125
 
52
126
  // Build the database assets (tables, functions, etc.)
53
- debug(`Building database assets (tables, functions, etc.).`.yellow);
127
+ this.#debug(`Building database assets (tables, functions, etc.).`.yellow);
54
128
  await localAccesslayer.executedbscript(builddbscript);
55
129
 
56
130
  if (typeof entries !== 'undefined') {
57
131
  // Add new faker entries
58
- debug(`Adding test data.`.yellow);
132
+ this.#debug(`Adding test data.`.yellow);
59
133
  let dg = new DataGenerator(localAccesslayer);
60
134
  await dg.RunAddFakerBulkInsert(start, entries, minextradata, maxextradata);
61
135
  }
62
136
 
63
137
  const blcauth = new BLCAuth(localAccesslayer);
138
+ let status = true;
64
139
 
65
- // Now register a new K6 test user
66
- debug(`Registering test users.`.yellow);
67
- let retVal = await blcauth.registeruser(user);
68
- console.log(`User registered: ${JSON.stringify(retVal)}`);
140
+ status = await this.#_RegisterUsersAndRoles(blcauth);
141
+ if (status) {
142
+ status = await this.#_RegisterRolesAndPermissions(blcauth);
143
+ }
144
+ if (status) {
145
+ status = await this.#_RegisterApplications(blcauth);
146
+ }
147
+ if (status) {
148
+ status = await this.#_RegisterAPIs(blcauth);
149
+ }
150
+
151
+ if (status) {
152
+ let retVal = await blcauth.GetUserPermissions('STSREST01ServiceUser@stsmda.com');
153
+ console.log(`User Permissions: ${JSON.stringify(retVal)}`);
154
+ }
69
155
 
70
156
  localAccesslayer.enddatabase();
71
- debug(`Database successfully initiailized.`.green);
157
+
158
+ if (status) {
159
+ this.#debug(`Database successfully initiailized.`.green);
160
+ console.log(`Database successfully initiailized.`.green);
161
+ } else {
162
+ this.#debug(`Database did not initiailize correctly.`.red);
163
+ console.log(`Database did not initiailize correctly.`.red);
164
+ }
72
165
  } catch (error)
73
166
  {
74
167
  console.error(`[${fname}]: Could not create fresh database: ${error}`);
75
- debug(`[${fname}]: Could not create fresh database: ${error}`);
168
+ this.#debug(`[${fname}]: Could not create fresh database: ${error}`);
76
169
  }
77
170
  };
78
171
 
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@nsshunt/stsdatamanagement",
3
- "version": "1.8.1",
3
+ "version": "1.10.1",
4
4
  "description": "STS Data Management Modules, Utilities and Services",
5
5
  "main": "dbaccess.js",
6
6
  "dependencies": {
7
- "@nsshunt/stsconfig": "^1.9.1",
8
- "@nsshunt/stsinstrumentation": "^6.4.1",
9
- "@nsshunt/stsutils": "^1.7.1",
7
+ "@nsshunt/stsconfig": "^1.17.1",
8
+ "@nsshunt/stsinstrumentation": "^6.4.2",
9
+ "@nsshunt/stsutils": "^1.7.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
- "debug": "^4.3.3",
14
+ "debug": "^4.3.4",
15
15
  "ioredis": "^4.28.5",
16
16
  "pg": "^8.7.3",
17
17
  "pg-copy-streams": "^6.0.2",
@@ -27,11 +27,11 @@
27
27
  "parser": "@babel/eslint-parser"
28
28
  },
29
29
  "devDependencies": {
30
- "@babel/core": "^7.17.5",
30
+ "@babel/core": "^7.17.8",
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.9.0",
34
+ "eslint": "^8.11.0",
35
35
  "jest": "^27.5.1"
36
36
  },
37
37
  "scripts": {
package/pgaccesslayer.js CHANGED
@@ -154,7 +154,7 @@ class PGAccessLayer
154
154
  try {
155
155
  const { rows } = await client.query(createQuery);
156
156
  const dbResponse = rows[0];
157
- return { status: status.created, detail: dbResponse };
157
+ return { status: status.success, detail: dbResponse };
158
158
  } catch (error) {
159
159
  return { status: status.error, error: `[${fname}]: Operation was not successful`, detail: error }; // Set default
160
160
  } finally {