dbgate-api-premium 6.5.5 → 6.5.6

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dbgate-api-premium",
3
3
  "main": "src/index.js",
4
- "version": "6.5.5",
4
+ "version": "6.5.6",
5
5
  "homepage": "https://dbgate.org/",
6
6
  "repository": {
7
7
  "type": "git",
@@ -30,10 +30,10 @@
30
30
  "compare-versions": "^3.6.0",
31
31
  "cors": "^2.8.5",
32
32
  "cross-env": "^6.0.3",
33
- "dbgate-datalib": "^6.5.5",
33
+ "dbgate-datalib": "^6.5.6",
34
34
  "dbgate-query-splitter": "^4.11.5",
35
- "dbgate-sqltree": "^6.5.5",
36
- "dbgate-tools": "^6.5.5",
35
+ "dbgate-sqltree": "^6.5.6",
36
+ "dbgate-tools": "^6.5.6",
37
37
  "debug": "^4.3.4",
38
38
  "diff": "^5.0.0",
39
39
  "diff2html": "^3.4.13",
@@ -85,7 +85,7 @@
85
85
  "devDependencies": {
86
86
  "@types/fs-extra": "^9.0.11",
87
87
  "@types/lodash": "^4.14.149",
88
- "dbgate-types": "^6.5.5",
88
+ "dbgate-types": "^6.5.6",
89
89
  "env-cmd": "^10.1.0",
90
90
  "jsdoc-to-markdown": "^9.0.5",
91
91
  "node-loader": "^1.0.2",
@@ -22,7 +22,7 @@ const _ = require('lodash');
22
22
  const { authProxyGetTokenFromCode, authProxyGetRedirectUrl } = require('../utility/authProxy');
23
23
  const { decryptUser } = require('../utility/crypting');
24
24
  const { sendToAuditLog } = require('../utility/auditlog');
25
- const { isLoginLicensed, LOGIN_LIMIT_ERROR } = require('../utility/loginchecker');
25
+ const { isLoginLicensed, LOGIN_LIMIT_ERROR, markTokenAsLoggedIn } = require('../utility/loginchecker');
26
26
 
27
27
  async function loadPermissionsForUserId(userId) {
28
28
  const rolePermissions = sortPermissionsFromTheSameLevel(await storageReadUserRolePermissions(userId));
@@ -31,7 +31,6 @@ async function loadPermissionsForUserId(userId) {
31
31
  return [...getPredefinedPermissions('logged-user'), ...loggedUserPermissions, ...rolePermissions, ...userPermissions];
32
32
  }
33
33
 
34
-
35
34
  class StorageProviderBase extends AuthProviderBase {
36
35
  constructor(config) {
37
36
  super();
@@ -72,16 +71,20 @@ class AnonymousProvider extends StorageProviderBase {
72
71
  message: 'Anonymous login',
73
72
  });
74
73
 
74
+ const licenseUid = 'anonymous';
75
+ const accessToken = jwt.sign(
76
+ {
77
+ amoid: this.amoid,
78
+ permissions,
79
+ licenseUid,
80
+ },
81
+ getTokenSecret(),
82
+ { expiresIn: getTokenLifetime() }
83
+ );
84
+ markTokenAsLoggedIn(licenseUid, accessToken);
85
+
75
86
  return {
76
- accessToken: jwt.sign(
77
- {
78
- amoid: this.amoid,
79
- permissions,
80
- licenseUid: 'anonymous',
81
- },
82
- getTokenSecret(),
83
- { expiresIn: getTokenLifetime() }
84
- ),
87
+ accessToken,
85
88
  };
86
89
  }
87
90
  }
@@ -114,18 +117,22 @@ class LocalAuthProvider extends StorageProviderBase {
114
117
  message: 'Local login successful',
115
118
  });
116
119
 
120
+ const licenseUid = `local:${login}`;
121
+ const accessToken = jwt.sign(
122
+ {
123
+ amoid: this.amoid,
124
+ login,
125
+ permissions,
126
+ userId,
127
+ licenseUid,
128
+ },
129
+ getTokenSecret(),
130
+ { expiresIn: getTokenLifetime() }
131
+ );
132
+ markTokenAsLoggedIn(licenseUid, accessToken);
133
+
117
134
  return {
118
- accessToken: jwt.sign(
119
- {
120
- amoid: this.amoid,
121
- login,
122
- permissions,
123
- userId,
124
- licenseUid: `local:${login}`,
125
- },
126
- getTokenSecret(),
127
- { expiresIn: getTokenLifetime() }
128
- ),
135
+ accessToken,
129
136
  };
130
137
  }
131
138
 
@@ -252,18 +259,22 @@ class OauthProvider extends StorageProviderBase {
252
259
  message: `User ${login} logged in via OAUTH`,
253
260
  });
254
261
 
262
+ const licenseUid = `oauth:${login}`;
263
+ const accessToken = jwt.sign(
264
+ {
265
+ amoid: this.amoid,
266
+ login,
267
+ permissions,
268
+ userId: loginRows[0]?.id,
269
+ licenseUid,
270
+ },
271
+ getTokenSecret(),
272
+ { expiresIn: getTokenLifetime() }
273
+ );
274
+ markTokenAsLoggedIn(licenseUid, accessToken);
275
+
255
276
  return {
256
- accessToken: jwt.sign(
257
- {
258
- amoid: this.amoid,
259
- login,
260
- permissions,
261
- userId: loginRows[0]?.id,
262
- licenseUid: `oauth:${login}`,
263
- },
264
- getTokenSecret(),
265
- { expiresIn: getTokenLifetime() }
266
- ),
277
+ accessToken,
267
278
  };
268
279
  }
269
280
 
@@ -339,17 +350,21 @@ class ADProvider extends StorageProviderBase {
339
350
  message: `User ${login} logged in via AD`,
340
351
  });
341
352
 
353
+ const licenseUid = `ad:${login}`;
354
+ const accessToken = jwt.sign(
355
+ {
356
+ amoid: this.amoid,
357
+ login,
358
+ permissions,
359
+ licenseUid,
360
+ },
361
+ getTokenSecret(),
362
+ { expiresIn: getTokenLifetime() }
363
+ );
364
+ markTokenAsLoggedIn(licenseUid, accessToken);
365
+
342
366
  return {
343
- accessToken: jwt.sign(
344
- {
345
- amoid: this.amoid,
346
- login,
347
- permissions,
348
- licenseUid: `ad:${login}`,
349
- },
350
- getTokenSecret(),
351
- { expiresIn: getTokenLifetime() }
352
- ),
367
+ accessToken,
353
368
  };
354
369
  } catch (e) {
355
370
  sendToAuditLog(req, {
@@ -422,19 +437,23 @@ class DatabaseProvider extends StorageProviderBase {
422
437
  message: `User ${login} logged in via database`,
423
438
  });
424
439
 
440
+ const licenseUid = `db:${login}`;
441
+ const accessToken = jwt.sign(
442
+ {
443
+ amoid: this.amoid,
444
+ login,
445
+ permissions,
446
+ userId,
447
+ conid,
448
+ licenseUid,
449
+ },
450
+ getTokenSecret(),
451
+ { expiresIn: getTokenLifetime() }
452
+ );
453
+ markTokenAsLoggedIn(licenseUid, accessToken);
454
+
425
455
  return {
426
- accessToken: jwt.sign(
427
- {
428
- amoid: this.amoid,
429
- login,
430
- permissions,
431
- userId,
432
- conid,
433
- licenseUid: `db:${login}`,
434
- },
435
- getTokenSecret(),
436
- { expiresIn: getTokenLifetime() }
437
- ),
456
+ accessToken,
438
457
  };
439
458
  }
440
459
 
@@ -504,19 +523,23 @@ class MsEntraProvider extends StorageProviderBase {
504
523
  message: `User ${payload.unique_name} logged in via MS Entra`,
505
524
  });
506
525
 
526
+ const licenseUid = `msentra:${payload.unique_name}`;
527
+ const accessToken = jwt.sign(
528
+ {
529
+ amoid: this.amoid,
530
+ permissions,
531
+ msentraToken: token,
532
+ userId: loginRows[0]?.id,
533
+ login: payload.unique_name,
534
+ licenseUid,
535
+ },
536
+ getTokenSecret(),
537
+ { expiresIn: getTokenLifetime() }
538
+ );
539
+ markTokenAsLoggedIn(licenseUid, accessToken);
540
+
507
541
  return {
508
- accessToken: jwt.sign(
509
- {
510
- amoid: this.amoid,
511
- permissions,
512
- msentraToken: token,
513
- userId: loginRows[0]?.id,
514
- login: payload.unique_name,
515
- licenseUid: `msentra:${payload.unique_name}`,
516
- },
517
- getTokenSecret(),
518
- { expiresIn: getTokenLifetime() }
519
- ),
542
+ accessToken,
520
543
  };
521
544
  }
522
545
 
@@ -21,7 +21,13 @@ const {
21
21
  } = require('../utility/cloudIntf');
22
22
  const socket = require('../utility/socket');
23
23
  const { sendToAuditLog } = require('../utility/auditlog');
24
- const { isLoginLicensed, LOGIN_LIMIT_ERROR } = require('../utility/loginchecker');
24
+ const {
25
+ isLoginLicensed,
26
+ LOGIN_LIMIT_ERROR,
27
+ markTokenAsLoggedIn,
28
+ markUserAsActive,
29
+ markLoginAsLoggedOut,
30
+ } = require('../utility/loginchecker');
25
31
 
26
32
  const logger = getLogger('auth');
27
33
 
@@ -61,6 +67,11 @@ function authMiddleware(req, res, next) {
61
67
 
62
68
  // const isAdminPage = req.headers['x-is-admin-page'] == 'true';
63
69
 
70
+ if (process.env.SKIP_ALL_AUTH) {
71
+ // API is not authorized for basic auth
72
+ return next();
73
+ }
74
+
64
75
  if (process.env.BASIC_AUTH) {
65
76
  // API is not authorized for basic auth
66
77
  return next();
@@ -79,7 +90,7 @@ function authMiddleware(req, res, next) {
79
90
  try {
80
91
  const decoded = jwt.verify(token, getTokenSecret());
81
92
  req.user = decoded;
82
- storage.markUserAsActive(decoded.licenseUid);
93
+ markUserAsActive(decoded.licenseUid, token);
83
94
 
84
95
  return next();
85
96
  } catch (err) {
@@ -124,19 +135,23 @@ module.exports = {
124
135
  message: 'Administration login successful',
125
136
  });
126
137
 
138
+ const licenseUid = `superadmin`;
139
+ const accessToken = jwt.sign(
140
+ {
141
+ login: 'superadmin',
142
+ permissions: await storage.loadSuperadminPermissions(),
143
+ roleId: -3,
144
+ licenseUid,
145
+ },
146
+ getTokenSecret(),
147
+ {
148
+ expiresIn: getTokenLifetime(),
149
+ }
150
+ );
151
+ markTokenAsLoggedIn(licenseUid, accessToken);
152
+
127
153
  return {
128
- accessToken: jwt.sign(
129
- {
130
- login: 'superadmin',
131
- permissions: await storage.loadSuperadminPermissions(),
132
- roleId: -3,
133
- licenseUid: `superadmin`,
134
- },
135
- getTokenSecret(),
136
- {
137
- expiresIn: getTokenLifetime(),
138
- }
139
- ),
154
+ accessToken,
140
155
  };
141
156
  }
142
157
 
@@ -192,5 +207,17 @@ module.exports = {
192
207
  return tokenHolder;
193
208
  },
194
209
 
210
+ logoutAdmin_meta: true,
211
+ async logoutAdmin() {
212
+ await markLoginAsLoggedOut('superadmin');
213
+ return true;
214
+ },
215
+
216
+ logoutUser_meta: true,
217
+ async logoutUser({}, req) {
218
+ await markLoginAsLoggedOut(req?.user?.licenseUid);
219
+ return true;
220
+ },
221
+
195
222
  authMiddleware,
196
223
  };
@@ -16,7 +16,7 @@ const connections = require('../controllers/connections');
16
16
  const { getAuthProviderFromReq } = require('../auth/authProvider');
17
17
  const { checkLicense, checkLicenseKey } = require('../utility/checkLicense');
18
18
  const storage = require('./storage');
19
- const { getAuthProxyUrl } = require('../utility/authProxy');
19
+ const { getAuthProxyUrl, tryToGetRefreshedLicense } = require('../utility/authProxy');
20
20
  const { getPublicHardwareFingerprint } = require('../utility/hardwareFingerprint');
21
21
  const { extractErrorMessage } = require('dbgate-tools');
22
22
  const {
@@ -109,6 +109,7 @@ module.exports = {
109
109
  ),
110
110
  isAdminPasswordMissing,
111
111
  isInvalidToken: req?.isInvalidToken,
112
+ skipAllAuth: !!process.env.SKIP_ALL_AUTH,
112
113
  adminPasswordState: adminConfig?.adminPasswordState,
113
114
  storageDatabase: process.env.STORAGE_DATABASE,
114
115
  logsFilePath: getLogsFilePath(),
@@ -191,6 +192,7 @@ module.exports = {
191
192
  return {
192
193
  ...this.fillMissingSettings(JSON.parse(settingsText)),
193
194
  'other.licenseKey': platformInfo.isElectron ? await this.loadLicenseKey() : undefined,
195
+ // 'other.licenseKey': await this.loadLicenseKey(),
194
196
  };
195
197
  }
196
198
  } catch (err) {
@@ -208,21 +210,34 @@ module.exports = {
208
210
  },
209
211
 
210
212
  saveLicenseKey_meta: true,
211
- async saveLicenseKey({ licenseKey }) {
212
- const decoded = jwt.decode(licenseKey?.trim());
213
- if (!decoded) {
214
- return {
215
- status: 'error',
216
- errorMessage: 'Invalid license key',
217
- };
218
- }
213
+ async saveLicenseKey({ licenseKey, forceSave = false, tryToRenew = false }) {
214
+ if (!forceSave) {
215
+ const decoded = jwt.decode(licenseKey?.trim());
216
+ if (!decoded) {
217
+ return {
218
+ status: 'error',
219
+ errorMessage: 'Invalid license key',
220
+ };
221
+ }
219
222
 
220
- const { exp } = decoded;
221
- if (exp * 1000 < Date.now()) {
222
- return {
223
- status: 'error',
224
- errorMessage: 'License key is expired',
225
- };
223
+ const { exp } = decoded;
224
+ if (exp * 1000 < Date.now()) {
225
+ let renewed = false;
226
+ if (tryToRenew) {
227
+ const newLicenseKey = await tryToGetRefreshedLicense(licenseKey);
228
+ if (newLicenseKey.status == 'ok') {
229
+ licenseKey = newLicenseKey.token;
230
+ renewed = true;
231
+ }
232
+ }
233
+
234
+ if (!renewed) {
235
+ return {
236
+ status: 'error',
237
+ errorMessage: 'License key is expired',
238
+ };
239
+ }
240
+ }
226
241
  }
227
242
 
228
243
  try {
@@ -297,7 +312,7 @@ module.exports = {
297
312
  // this.settingsValue = updated;
298
313
 
299
314
  if (currentValue['other.licenseKey'] != values['other.licenseKey']) {
300
- await this.saveLicenseKey({ licenseKey: values['other.licenseKey'] });
315
+ await this.saveLicenseKey({ licenseKey: values['other.licenseKey'], forceSave: true });
301
316
  socket.emitChanged(`config-changed`);
302
317
  }
303
318
  }
@@ -327,6 +342,16 @@ module.exports = {
327
342
  return resp;
328
343
  },
329
344
 
345
+ getNewLicense_meta: true,
346
+ async getNewLicense({ oldLicenseKey }) {
347
+ const newLicenseKey = await tryToGetRefreshedLicense(oldLicenseKey);
348
+ const res = await checkLicenseKey(newLicenseKey.token);
349
+ if (res.status == 'ok') {
350
+ res.licenseKey = newLicenseKey.token;
351
+ }
352
+ return res;
353
+ },
354
+
330
355
  recryptDatabaseForExport(db) {
331
356
  const encryptionKey = generateTransportEncryptionKey();
332
357
  const transportEncryptor = createTransportEncryptor(encryptionKey);
@@ -40,7 +40,6 @@ const crypto = require('crypto');
40
40
  const dataReplicator = require('../shell/dataReplicator');
41
41
  const storageReplicatorItems = require('../utility/storageReplicatorItems');
42
42
  const { sendToAuditLog } = require('../utility/auditlog');
43
- const { markUserAsActive } = require('../utility/loginchecker');
44
43
 
45
44
  const logger = getLogger('storage');
46
45
 
@@ -881,8 +880,4 @@ module.exports = {
881
880
  sendToAuditLog(req, props);
882
881
  return null;
883
882
  },
884
-
885
- markUserAsActive(licenseUid) {
886
- markUserAsActive(licenseUid);
887
- },
888
883
  };
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '6.5.5',
4
- buildTime: '2025-07-04T07:10:42.669Z'
3
+ version: '6.5.6',
4
+ buildTime: '2025-07-17T07:05:49.044Z'
5
5
  };
@@ -519,6 +519,12 @@ module.exports = {
519
519
  "dataType": "int",
520
520
  "notNull": false
521
521
  },
522
+ {
523
+ "pureName": "connections",
524
+ "columnName": "useSeparateSchemas",
525
+ "dataType": "int",
526
+ "notNull": false
527
+ },
522
528
  {
523
529
  "pureName": "connections",
524
530
  "columnName": "defaultDatabase",
@@ -682,6 +688,49 @@ module.exports = {
682
688
  ]
683
689
  }
684
690
  },
691
+ {
692
+ "pureName": "roles",
693
+ "columns": [
694
+ {
695
+ "pureName": "roles",
696
+ "columnName": "id",
697
+ "dataType": "int",
698
+ "autoIncrement": true,
699
+ "notNull": true
700
+ },
701
+ {
702
+ "pureName": "roles",
703
+ "columnName": "name",
704
+ "dataType": "varchar(250)",
705
+ "notNull": false
706
+ }
707
+ ],
708
+ "foreignKeys": [],
709
+ "primaryKey": {
710
+ "pureName": "roles",
711
+ "constraintType": "primaryKey",
712
+ "constraintName": "PK_roles",
713
+ "columns": [
714
+ {
715
+ "columnName": "id"
716
+ }
717
+ ]
718
+ },
719
+ "preloadedRows": [
720
+ {
721
+ "id": -1,
722
+ "name": "anonymous-user"
723
+ },
724
+ {
725
+ "id": -2,
726
+ "name": "logged-user"
727
+ },
728
+ {
729
+ "id": -3,
730
+ "name": "superadmin"
731
+ }
732
+ ]
733
+ },
685
734
  {
686
735
  "pureName": "role_connections",
687
736
  "columns": [
@@ -794,47 +843,45 @@ module.exports = {
794
843
  }
795
844
  },
796
845
  {
797
- "pureName": "roles",
846
+ "pureName": "users",
798
847
  "columns": [
799
848
  {
800
- "pureName": "roles",
849
+ "pureName": "users",
801
850
  "columnName": "id",
802
851
  "dataType": "int",
803
852
  "autoIncrement": true,
804
853
  "notNull": true
805
854
  },
806
855
  {
807
- "pureName": "roles",
808
- "columnName": "name",
856
+ "pureName": "users",
857
+ "columnName": "login",
858
+ "dataType": "varchar(250)",
859
+ "notNull": false
860
+ },
861
+ {
862
+ "pureName": "users",
863
+ "columnName": "password",
864
+ "dataType": "varchar(250)",
865
+ "notNull": false
866
+ },
867
+ {
868
+ "pureName": "users",
869
+ "columnName": "email",
809
870
  "dataType": "varchar(250)",
810
871
  "notNull": false
811
872
  }
812
873
  ],
813
874
  "foreignKeys": [],
814
875
  "primaryKey": {
815
- "pureName": "roles",
876
+ "pureName": "users",
816
877
  "constraintType": "primaryKey",
817
- "constraintName": "PK_roles",
878
+ "constraintName": "PK_users",
818
879
  "columns": [
819
880
  {
820
881
  "columnName": "id"
821
882
  }
822
883
  ]
823
- },
824
- "preloadedRows": [
825
- {
826
- "id": -1,
827
- "name": "anonymous-user"
828
- },
829
- {
830
- "id": -2,
831
- "name": "logged-user"
832
- },
833
- {
834
- "id": -3,
835
- "name": "superadmin"
836
- }
837
- ]
884
+ }
838
885
  },
839
886
  {
840
887
  "pureName": "user_connections",
@@ -1008,47 +1055,6 @@ module.exports = {
1008
1055
  }
1009
1056
  ]
1010
1057
  }
1011
- },
1012
- {
1013
- "pureName": "users",
1014
- "columns": [
1015
- {
1016
- "pureName": "users",
1017
- "columnName": "id",
1018
- "dataType": "int",
1019
- "autoIncrement": true,
1020
- "notNull": true
1021
- },
1022
- {
1023
- "pureName": "users",
1024
- "columnName": "login",
1025
- "dataType": "varchar(250)",
1026
- "notNull": false
1027
- },
1028
- {
1029
- "pureName": "users",
1030
- "columnName": "password",
1031
- "dataType": "varchar(250)",
1032
- "notNull": false
1033
- },
1034
- {
1035
- "pureName": "users",
1036
- "columnName": "email",
1037
- "dataType": "varchar(250)",
1038
- "notNull": false
1039
- }
1040
- ],
1041
- "foreignKeys": [],
1042
- "primaryKey": {
1043
- "pureName": "users",
1044
- "constraintType": "primaryKey",
1045
- "constraintName": "PK_users",
1046
- "columns": [
1047
- {
1048
- "columnName": "id"
1049
- }
1050
- ]
1051
- }
1052
1058
  }
1053
1059
  ],
1054
1060
  "collections": [],
@@ -185,6 +185,27 @@ async function obtainRefreshedLicense() {
185
185
  }
186
186
  }
187
187
 
188
+ async function tryToGetRefreshedLicense(oldLicenseKey) {
189
+ try {
190
+ const respToken = await axios.default.post(
191
+ `${AUTH_PROXY_URL}/refresh-license`,
192
+ {},
193
+ {
194
+ headers: {
195
+ 'Content-Type': 'application/json',
196
+ Authorization: `Bearer ${oldLicenseKey}`,
197
+ },
198
+ }
199
+ );
200
+ return respToken.data;
201
+ } catch (err) {
202
+ return {
203
+ status: 'error',
204
+ message: err.message,
205
+ };
206
+ }
207
+ }
208
+
188
209
  /**
189
210
  * @param {import('dbgate-types').DatabaseInfo} structure
190
211
  * @returns {import('dbgate-types').DatabaseInfoTiny}
@@ -303,4 +324,5 @@ module.exports = {
303
324
  callCompleteOnCursorApi,
304
325
  callRefactorSqlQueryApi,
305
326
  getLicenseHttpHeaders,
327
+ tryToGetRefreshedLicense,
306
328
  };
@@ -161,6 +161,7 @@ function checkLicenseKey(licenseKey) {
161
161
  status: 'error',
162
162
  isExpired: true,
163
163
  errorMessage: 'License key is expired',
164
+ expiration: exp * 1000,
164
165
  };
165
166
  }
166
167
  }
@@ -1,4 +1,5 @@
1
1
  const axios = require('axios');
2
+ const crypto = require('crypto');
2
3
  const fs = require('fs-extra');
3
4
  const _ = require('lodash');
4
5
  const path = require('path');
@@ -216,7 +217,7 @@ async function updateCloudFiles(isRefresh) {
216
217
  {
217
218
  headers: {
218
219
  ...getLicenseHttpHeaders(),
219
- ...(await getCloudSigninHeaders()),
220
+ ...(await getCloudInstanceHeaders()),
220
221
  'x-app-version': currentVersion.version,
221
222
  },
222
223
  }
@@ -300,6 +301,17 @@ async function callCloudApiGet(endpoint, signinHolder = null, additionalHeaders
300
301
  return resp.data;
301
302
  }
302
303
 
304
+ async function getCloudInstanceHeaders() {
305
+ if (!(await fs.exists(path.join(datadir(), 'cloud-instance.txt')))) {
306
+ const newInstanceId = crypto.randomUUID();
307
+ await fs.writeFile(path.join(datadir(), 'cloud-instance.txt'), newInstanceId);
308
+ }
309
+ const instanceId = await fs.readFile(path.join(datadir(), 'cloud-instance.txt'), 'utf-8');
310
+ return {
311
+ 'x-cloud-instance': instanceId,
312
+ };
313
+ }
314
+
303
315
  async function callCloudApiPost(endpoint, body, signinHolder = null) {
304
316
  if (!signinHolder) {
305
317
  signinHolder = await getCloudSigninHolder();
@@ -4,22 +4,32 @@ const _ = require('lodash');
4
4
  const { sendToAuditLog } = require('./auditlog');
5
5
  const { checkLicense } = require('./checkLicense');
6
6
  const LOGIN_LIMIT_ERROR = 'Your limit of concurrent logins has been reached';
7
+ const jwt = require('jsonwebtoken');
7
8
 
8
9
  // map string (user key) => time to expiration
9
10
  let activeLoggedUsers = {};
10
11
 
11
- function markUserAsActive(licenseUid) {
12
+ // map string (user key) => token expiration time
13
+ let activeLoggedTokens = {};
14
+
15
+ function markUserAsActive(licenseUid, accessToken) {
12
16
  activeLoggedUsers[licenseUid] = Date.now() + 60 * 1000; // mark user as active for 1 minute
13
17
  }
14
18
 
15
19
  async function isLoginLicensed(req, licenseUid) {
16
20
  const license = await checkLicense();
17
21
  activeLoggedUsers = _.pickBy(activeLoggedUsers, value => value > Date.now());
22
+ activeLoggedTokens = _.pickBy(activeLoggedTokens, value => value > Date.now());
18
23
  if (licenseUid in activeLoggedUsers) {
19
24
  return true;
20
25
  }
26
+ if (licenseUid in activeLoggedTokens) {
27
+ return true;
28
+ }
21
29
  if (license.licenseId == 'f0346efe-ebc2-4822-9a83-4b4668a897e5' && license?.users != null) {
22
- const currentUserCount = Object.keys(activeLoggedUsers).length;
30
+ const uniqUsers = _.uniq([...Object.keys(activeLoggedUsers), ...Object.keys(activeLoggedTokens)]);
31
+ const currentUserCount = uniqUsers.length;
32
+
23
33
  if (currentUserCount >= license?.users) {
24
34
  sendToAuditLog(req, {
25
35
  category: 'auth',
@@ -37,8 +47,23 @@ async function isLoginLicensed(req, licenseUid) {
37
47
  return true;
38
48
  }
39
49
 
50
+ function markTokenAsLoggedIn(licenseUid, token) {
51
+ if (licenseUid != 'anonymous') {
52
+ const decoded = jwt.decode(token);
53
+ activeLoggedTokens[licenseUid] = decoded.exp * 1000;
54
+ }
55
+ activeLoggedUsers[licenseUid] = Date.now() + 60 * 1000; // mark user as active for 1 minute
56
+ }
57
+
58
+ function markLoginAsLoggedOut(licenseUid) {
59
+ delete activeLoggedUsers[licenseUid];
60
+ delete activeLoggedTokens[licenseUid];
61
+ }
62
+
40
63
  module.exports = {
41
64
  markUserAsActive,
65
+ markTokenAsLoggedIn,
42
66
  isLoginLicensed,
43
67
  LOGIN_LIMIT_ERROR,
68
+ markLoginAsLoggedOut,
44
69
  };