@lockerpm/desktop-service 1.0.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.
Files changed (158) hide show
  1. package/README.md +98 -0
  2. package/lib/cjs/abstractions/api.service.js +2 -0
  3. package/lib/cjs/abstractions/crypto.service.js +147 -0
  4. package/lib/cjs/abstractions/errors.js +99 -0
  5. package/lib/cjs/abstractions/event.service.js +2 -0
  6. package/lib/cjs/abstractions/index.js +2 -0
  7. package/lib/cjs/abstractions/socket.service.js +11 -0
  8. package/lib/cjs/abstractions/storage.service.js +2 -0
  9. package/lib/cjs/index.js +243 -0
  10. package/lib/cjs/misc/config.js +15 -0
  11. package/lib/cjs/misc/utils.js +37 -0
  12. package/lib/cjs/proto/google/api/annotations.js +2 -0
  13. package/lib/cjs/proto/google/api/http.js +477 -0
  14. package/lib/cjs/proto/google/protobuf/descriptor.js +4873 -0
  15. package/lib/cjs/proto/locker-service-grpc.js +1915 -0
  16. package/lib/cjs/services/api.service.js +182 -0
  17. package/lib/cjs/services/cache.service.js +50 -0
  18. package/lib/cjs/services/core-crypto.service.js +193 -0
  19. package/lib/cjs/services/crypto.service.js +101 -0
  20. package/lib/cjs/services/event.service.js +31 -0
  21. package/lib/cjs/services/fido.service.js +136 -0
  22. package/lib/cjs/services/grpc.service.js +130 -0
  23. package/lib/cjs/services/log.service.js +30 -0
  24. package/lib/cjs/services/pairing.service.js +122 -0
  25. package/lib/cjs/services/socket.service.js +280 -0
  26. package/lib/cjs/services/user.service.js +134 -0
  27. package/lib/cjs/types/abstractions/api.service.d.ts +40 -0
  28. package/lib/cjs/types/abstractions/api.service.d.ts.map +1 -0
  29. package/lib/cjs/types/abstractions/crypto.service.d.ts +46 -0
  30. package/lib/cjs/types/abstractions/crypto.service.d.ts.map +1 -0
  31. package/lib/cjs/types/abstractions/errors.d.ts +73 -0
  32. package/lib/cjs/types/abstractions/errors.d.ts.map +1 -0
  33. package/lib/cjs/types/abstractions/event.service.d.ts +23 -0
  34. package/lib/cjs/types/abstractions/event.service.d.ts.map +1 -0
  35. package/lib/cjs/types/abstractions/index.d.ts +56 -0
  36. package/lib/cjs/types/abstractions/index.d.ts.map +1 -0
  37. package/lib/cjs/types/abstractions/socket.service.d.ts +52 -0
  38. package/lib/cjs/types/abstractions/socket.service.d.ts.map +1 -0
  39. package/lib/cjs/types/abstractions/storage.service.d.ts +6 -0
  40. package/lib/cjs/types/abstractions/storage.service.d.ts.map +1 -0
  41. package/lib/cjs/types/index.d.ts +164 -0
  42. package/lib/cjs/types/index.d.ts.map +1 -0
  43. package/lib/cjs/types/misc/config.d.ts +6 -0
  44. package/lib/cjs/types/misc/config.d.ts.map +1 -0
  45. package/lib/cjs/types/misc/utils.d.ts +21 -0
  46. package/lib/cjs/types/misc/utils.d.ts.map +1 -0
  47. package/lib/cjs/types/proto/google/api/annotations.d.ts +2 -0
  48. package/lib/cjs/types/proto/google/api/annotations.d.ts.map +1 -0
  49. package/lib/cjs/types/proto/google/api/http.d.ts +195 -0
  50. package/lib/cjs/types/proto/google/api/http.d.ts.map +1 -0
  51. package/lib/cjs/types/proto/google/protobuf/descriptor.d.ts +3409 -0
  52. package/lib/cjs/types/proto/google/protobuf/descriptor.d.ts.map +1 -0
  53. package/lib/cjs/types/proto/locker-service-grpc.d.ts +622 -0
  54. package/lib/cjs/types/proto/locker-service-grpc.d.ts.map +1 -0
  55. package/lib/cjs/types/services/api.service.d.ts +37 -0
  56. package/lib/cjs/types/services/api.service.d.ts.map +1 -0
  57. package/lib/cjs/types/services/cache.service.d.ts +10 -0
  58. package/lib/cjs/types/services/cache.service.d.ts.map +1 -0
  59. package/lib/cjs/types/services/core-crypto.service.d.ts +17 -0
  60. package/lib/cjs/types/services/core-crypto.service.d.ts.map +1 -0
  61. package/lib/cjs/types/services/crypto.service.d.ts +23 -0
  62. package/lib/cjs/types/services/crypto.service.d.ts.map +1 -0
  63. package/lib/cjs/types/services/event.service.d.ts +14 -0
  64. package/lib/cjs/types/services/event.service.d.ts.map +1 -0
  65. package/lib/cjs/types/services/fido.service.d.ts +40 -0
  66. package/lib/cjs/types/services/fido.service.d.ts.map +1 -0
  67. package/lib/cjs/types/services/grpc.service.d.ts +34 -0
  68. package/lib/cjs/types/services/grpc.service.d.ts.map +1 -0
  69. package/lib/cjs/types/services/log.service.d.ts +13 -0
  70. package/lib/cjs/types/services/log.service.d.ts.map +1 -0
  71. package/lib/cjs/types/services/pairing.service.d.ts +37 -0
  72. package/lib/cjs/types/services/pairing.service.d.ts.map +1 -0
  73. package/lib/cjs/types/services/socket.service.d.ts +39 -0
  74. package/lib/cjs/types/services/socket.service.d.ts.map +1 -0
  75. package/lib/cjs/types/services/user.service.d.ts +32 -0
  76. package/lib/cjs/types/services/user.service.d.ts.map +1 -0
  77. package/lib/cjs/types/usecases/fido.d.ts +54 -0
  78. package/lib/cjs/types/usecases/fido.d.ts.map +1 -0
  79. package/lib/cjs/usecases/fido.js +227 -0
  80. package/lib/esm/abstractions/api.service.js +2 -0
  81. package/lib/esm/abstractions/crypto.service.js +165 -0
  82. package/lib/esm/abstractions/errors.js +100 -0
  83. package/lib/esm/abstractions/event.service.js +2 -0
  84. package/lib/esm/abstractions/index.js +2 -0
  85. package/lib/esm/abstractions/socket.service.js +11 -0
  86. package/lib/esm/abstractions/storage.service.js +2 -0
  87. package/lib/esm/index.mjs +227 -0
  88. package/lib/esm/misc/config.js +15 -0
  89. package/lib/esm/misc/utils.js +37 -0
  90. package/lib/esm/proto/google/api/annotations.js +2 -0
  91. package/lib/esm/proto/google/api/http.js +468 -0
  92. package/lib/esm/proto/google/protobuf/descriptor.js +4830 -0
  93. package/lib/esm/proto/locker-service-grpc.js +1892 -0
  94. package/lib/esm/services/api.service.js +177 -0
  95. package/lib/esm/services/cache.service.js +52 -0
  96. package/lib/esm/services/core-crypto.service.js +164 -0
  97. package/lib/esm/services/crypto.service.js +83 -0
  98. package/lib/esm/services/event.service.js +33 -0
  99. package/lib/esm/services/fido.service.js +139 -0
  100. package/lib/esm/services/grpc.service.js +119 -0
  101. package/lib/esm/services/log.service.js +31 -0
  102. package/lib/esm/services/pairing.service.js +107 -0
  103. package/lib/esm/services/socket.service.js +265 -0
  104. package/lib/esm/services/user.service.js +116 -0
  105. package/lib/esm/types/abstractions/api.service.d.ts +40 -0
  106. package/lib/esm/types/abstractions/api.service.d.ts.map +1 -0
  107. package/lib/esm/types/abstractions/crypto.service.d.ts +46 -0
  108. package/lib/esm/types/abstractions/crypto.service.d.ts.map +1 -0
  109. package/lib/esm/types/abstractions/errors.d.ts +73 -0
  110. package/lib/esm/types/abstractions/errors.d.ts.map +1 -0
  111. package/lib/esm/types/abstractions/event.service.d.ts +23 -0
  112. package/lib/esm/types/abstractions/event.service.d.ts.map +1 -0
  113. package/lib/esm/types/abstractions/index.d.ts +56 -0
  114. package/lib/esm/types/abstractions/index.d.ts.map +1 -0
  115. package/lib/esm/types/abstractions/socket.service.d.ts +52 -0
  116. package/lib/esm/types/abstractions/socket.service.d.ts.map +1 -0
  117. package/lib/esm/types/abstractions/storage.service.d.ts +6 -0
  118. package/lib/esm/types/abstractions/storage.service.d.ts.map +1 -0
  119. package/lib/esm/types/index.d.ts +164 -0
  120. package/lib/esm/types/index.d.ts.map +1 -0
  121. package/lib/esm/types/misc/config.d.ts +6 -0
  122. package/lib/esm/types/misc/config.d.ts.map +1 -0
  123. package/lib/esm/types/misc/utils.d.ts +21 -0
  124. package/lib/esm/types/misc/utils.d.ts.map +1 -0
  125. package/lib/esm/types/proto/google/api/annotations.d.ts +2 -0
  126. package/lib/esm/types/proto/google/api/annotations.d.ts.map +1 -0
  127. package/lib/esm/types/proto/google/api/http.d.ts +195 -0
  128. package/lib/esm/types/proto/google/api/http.d.ts.map +1 -0
  129. package/lib/esm/types/proto/google/protobuf/descriptor.d.ts +3409 -0
  130. package/lib/esm/types/proto/google/protobuf/descriptor.d.ts.map +1 -0
  131. package/lib/esm/types/proto/locker-service-grpc.d.ts +622 -0
  132. package/lib/esm/types/proto/locker-service-grpc.d.ts.map +1 -0
  133. package/lib/esm/types/services/api.service.d.ts +37 -0
  134. package/lib/esm/types/services/api.service.d.ts.map +1 -0
  135. package/lib/esm/types/services/cache.service.d.ts +10 -0
  136. package/lib/esm/types/services/cache.service.d.ts.map +1 -0
  137. package/lib/esm/types/services/core-crypto.service.d.ts +17 -0
  138. package/lib/esm/types/services/core-crypto.service.d.ts.map +1 -0
  139. package/lib/esm/types/services/crypto.service.d.ts +23 -0
  140. package/lib/esm/types/services/crypto.service.d.ts.map +1 -0
  141. package/lib/esm/types/services/event.service.d.ts +14 -0
  142. package/lib/esm/types/services/event.service.d.ts.map +1 -0
  143. package/lib/esm/types/services/fido.service.d.ts +40 -0
  144. package/lib/esm/types/services/fido.service.d.ts.map +1 -0
  145. package/lib/esm/types/services/grpc.service.d.ts +34 -0
  146. package/lib/esm/types/services/grpc.service.d.ts.map +1 -0
  147. package/lib/esm/types/services/log.service.d.ts +13 -0
  148. package/lib/esm/types/services/log.service.d.ts.map +1 -0
  149. package/lib/esm/types/services/pairing.service.d.ts +37 -0
  150. package/lib/esm/types/services/pairing.service.d.ts.map +1 -0
  151. package/lib/esm/types/services/socket.service.d.ts +39 -0
  152. package/lib/esm/types/services/socket.service.d.ts.map +1 -0
  153. package/lib/esm/types/services/user.service.d.ts +32 -0
  154. package/lib/esm/types/services/user.service.d.ts.map +1 -0
  155. package/lib/esm/types/usecases/fido.d.ts +54 -0
  156. package/lib/esm/types/usecases/fido.d.ts.map +1 -0
  157. package/lib/esm/usecases/fido.js +201 -0
  158. package/package.json +55 -0
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GRPCService = void 0;
7
+ const grpc_js_1 = require("@grpc/grpc-js");
8
+ const locker_service_grpc_1 = require("../proto/locker-service-grpc");
9
+ const find_process_1 = __importDefault(require("find-process"));
10
+ const utils_1 = require("../misc/utils");
11
+ const axios_1 = __importDefault(require("axios"));
12
+ const config_1 = require("../misc/config");
13
+ class GRPCService {
14
+ client;
15
+ currentPort = 0;
16
+ logger;
17
+ crypto;
18
+ api;
19
+ serviceChecksums = [];
20
+ credentials;
21
+ unsafe;
22
+ serviceAlias = '';
23
+ constructor(services, options) {
24
+ const { logger, cryptoService, apiService } = services;
25
+ const { ssl, unsafe, serviceAlias } = options;
26
+ this.logger = logger;
27
+ this.crypto = cryptoService;
28
+ this.api = apiService;
29
+ this.unsafe = !!unsafe;
30
+ if (serviceAlias) {
31
+ this.serviceAlias = serviceAlias;
32
+ }
33
+ const cred = ssl ? grpc_js_1.credentials.createSsl(ssl.rootCert) : grpc_js_1.credentials.createInsecure();
34
+ this.credentials = cred;
35
+ this.client = new locker_service_grpc_1.locker_service_grpc.LockerServiceClient(`localhost:${this.currentPort}`, cred);
36
+ this.initConnection();
37
+ }
38
+ get isReady() {
39
+ return this.currentPort !== 0;
40
+ }
41
+ async initConnection() {
42
+ this.currentPort = 0;
43
+ if (!this.unsafe) {
44
+ await this.loadServiceChecksums();
45
+ }
46
+ for (const httpPort of config_1.HTTP_PORTS) {
47
+ this.logger.debug(`Ping background service on port ${httpPort}`);
48
+ const { success, res } = await this.pingService(httpPort);
49
+ if (success && res) {
50
+ const port = res.tcpPort;
51
+ const client = await this.testConnection(port);
52
+ if (client) {
53
+ const isValidated = this.unsafe ? true : await this.validateConnection(port);
54
+ if (isValidated) {
55
+ this.client = client;
56
+ this.currentPort = port;
57
+ this.logger.debug(`GRPC server connected on port ${port}`);
58
+ return;
59
+ }
60
+ }
61
+ }
62
+ }
63
+ this.logger.error('Cannot connect to GRPC server');
64
+ }
65
+ // ---------------------- PRIVATE METHODS ----------------------
66
+ async pingService(port) {
67
+ try {
68
+ const url = `http://localhost:${port}/ping-locker-service`;
69
+ const res = await axios_1.default.get(url, {
70
+ timeout: config_1.GRPC_PING_TIMEOUT,
71
+ });
72
+ const data = res.data;
73
+ return {
74
+ success: data.message === 'pong' && data.alias === this.serviceAlias,
75
+ res: data,
76
+ };
77
+ }
78
+ catch (e) {
79
+ return {
80
+ success: false,
81
+ };
82
+ }
83
+ }
84
+ testConnection(port) {
85
+ return new Promise((resolve) => {
86
+ const client = new locker_service_grpc_1.locker_service_grpc.LockerServiceClient(`localhost:${port}`, this.credentials);
87
+ client.waitForReady(Date.now() + config_1.GRPC_CONNECTION_TIMEOUT, (error) => {
88
+ if (error) {
89
+ resolve(null);
90
+ }
91
+ else {
92
+ resolve(client);
93
+ }
94
+ });
95
+ });
96
+ }
97
+ async validateConnection(port) {
98
+ try {
99
+ const list = await (0, find_process_1.default)('port', port, true);
100
+ const service = list.find((s) => s.name === 'locker-service');
101
+ if (!service) {
102
+ return false;
103
+ }
104
+ // TODO: somehow, service.bin does not exist in type but exists in returned value when testing on MacOS
105
+ // @ts-ignore
106
+ const checksum = await this.crypto.getFileChecksum(service.bin);
107
+ return this.serviceChecksums.includes(checksum);
108
+ }
109
+ catch (error) {
110
+ return false;
111
+ }
112
+ }
113
+ async loadServiceChecksums() {
114
+ const _os = utils_1.Utils.getCurrentOS();
115
+ const res = await this.api.getReleases(_os);
116
+ this.serviceChecksums = res.map((version) => version.checksum.service);
117
+ }
118
+ }
119
+ exports.GRPCService = GRPCService;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LogService = exports.LogLevel = void 0;
4
+ var LogLevel;
5
+ (function (LogLevel) {
6
+ LogLevel[LogLevel["NONE"] = 0] = "NONE";
7
+ LogLevel[LogLevel["ERROR"] = 1] = "ERROR";
8
+ LogLevel[LogLevel["DEBUG"] = 2] = "DEBUG";
9
+ })(LogLevel || (exports.LogLevel = LogLevel = {}));
10
+ class LogService {
11
+ logLevel;
12
+ constructor(logLevel) {
13
+ this.logLevel = logLevel || LogLevel.ERROR;
14
+ }
15
+ setLogLevel(level) {
16
+ this.logLevel = level;
17
+ }
18
+ debug(e) {
19
+ if (this.logLevel >= LogLevel.DEBUG) {
20
+ console.log(new Date());
21
+ console.log(e);
22
+ }
23
+ }
24
+ error(e) {
25
+ if (this.logLevel >= LogLevel.ERROR) {
26
+ console.log(new Date());
27
+ console.error(e);
28
+ }
29
+ }
30
+ }
31
+ exports.LogService = LogService;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PairingService = void 0;
4
+ const errors_1 = require("../abstractions/errors");
5
+ const socket_service_1 = require("../abstractions/socket.service");
6
+ const locker_service_grpc_1 = require("../proto/locker-service-grpc");
7
+ const STORAGE_KEY = 'service_paired_clients';
8
+ class PairingService {
9
+ crypto;
10
+ storage;
11
+ events;
12
+ logger;
13
+ grpc;
14
+ clients = {};
15
+ isReady = false;
16
+ constructor(services) {
17
+ const { cryptoService, eventService, storageService, grpcService, logger } = services;
18
+ this.crypto = cryptoService;
19
+ this.events = eventService;
20
+ this.storage = storageService;
21
+ this.grpc = grpcService;
22
+ this.logger = logger;
23
+ this.loadFromStore().then(() => (this.isReady = true));
24
+ }
25
+ getClient(clientId) {
26
+ return this.clients[clientId];
27
+ }
28
+ isClientConfirmed(clientId) {
29
+ return this.clients[clientId]?.confirmed;
30
+ }
31
+ async getResponseForPairingRequest(clientId, publicKey, clientType) {
32
+ const keyPair = await this.crypto.createECDHKeyPair();
33
+ const sharedKey = await this.crypto.createEncKey(publicKey, keyPair.privateKey);
34
+ this.clients[clientId] = {
35
+ encKey: sharedKey.encKey,
36
+ confirmed: false,
37
+ };
38
+ await this.saveToStore();
39
+ this.events.emit('pairingConfirmation', {
40
+ clientId,
41
+ approveCode: sharedKey.approveCode,
42
+ clientType,
43
+ });
44
+ return (0, socket_service_1.buildOutgoingSocketMessage)('pairingResponse', {
45
+ key: keyPair.publicKey,
46
+ });
47
+ }
48
+ async confirmPairingClient(clientId, keepInKeyring) {
49
+ if (!this.clients[clientId]) {
50
+ this.logger.debug(`Pairing client with id ${clientId} not found`);
51
+ return;
52
+ }
53
+ this.clients[clientId].confirmed = true;
54
+ await Promise.all([this.saveToStore(), this.registerClientOnService(clientId, keepInKeyring)]);
55
+ }
56
+ async encryptDataForClient(clientId, data) {
57
+ const client = this.clients[clientId];
58
+ if (!client?.confirmed) {
59
+ throw new errors_1.ServiceError('3001');
60
+ }
61
+ const res = await this.crypto.aesEncrypt(data, client.encKey);
62
+ return res;
63
+ }
64
+ async decryptDataFromClient(clientId, data) {
65
+ const client = this.clients[clientId];
66
+ if (!client?.confirmed) {
67
+ throw new errors_1.ServiceError('3001');
68
+ }
69
+ const res = await this.crypto.aesDecrypt(data, client.encKey);
70
+ return res;
71
+ }
72
+ registerClientOnService(clientId, keepInKeyring = false) {
73
+ const client = this.clients[clientId];
74
+ const req = new locker_service_grpc_1.locker_service_grpc.WebCredRequest();
75
+ req.message = 'forwardingWebCred';
76
+ req.client_id = clientId;
77
+ req.enc_key = client.encKey;
78
+ req.save_to_keyring = keepInKeyring;
79
+ return new Promise((resolve, reject) => {
80
+ this.grpc.client.WebCredChannel(req, (err, res) => {
81
+ this.logger.debug(res);
82
+ if (err) {
83
+ reject(errors_1.ServiceError.fromError(err));
84
+ return;
85
+ }
86
+ resolve();
87
+ });
88
+ });
89
+ }
90
+ async loadFromStore() {
91
+ const data = await this.storage.getSecure(STORAGE_KEY);
92
+ if (data) {
93
+ // Remove unconfirmed client
94
+ const res = {};
95
+ Object.keys(data).forEach((k) => {
96
+ if (data[k].confirmed) {
97
+ res[k] = data[k];
98
+ }
99
+ });
100
+ this.clients = res;
101
+ }
102
+ }
103
+ saveToStore() {
104
+ return this.storage.setSecure(STORAGE_KEY, this.clients);
105
+ }
106
+ }
107
+ exports.PairingService = PairingService;
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SocketService = void 0;
7
+ const ws_1 = require("ws");
8
+ const socket_service_1 = require("../abstractions/socket.service");
9
+ const errors_1 = require("../abstractions/errors");
10
+ const https_1 = __importDefault(require("https"));
11
+ const http_1 = __importDefault(require("http"));
12
+ const config_1 = require("../misc/config");
13
+ class SocketService {
14
+ logger;
15
+ pairingService;
16
+ userService;
17
+ eventService;
18
+ sslConfig = undefined;
19
+ server = undefined;
20
+ clients = {};
21
+ serviceAlias = '';
22
+ currentPort = 0;
23
+ currentSslPort = 0;
24
+ constructor(params) {
25
+ const { logger, pairingService, userService, eventService, ssl } = params;
26
+ this.logger = logger;
27
+ this.pairingService = pairingService;
28
+ this.userService = userService;
29
+ this.eventService = eventService;
30
+ if (ssl) {
31
+ this.sslConfig = ssl;
32
+ this.initSslSocket();
33
+ }
34
+ this.initSocket();
35
+ }
36
+ get isReady() {
37
+ const isWsReady = this.currentPort !== 0;
38
+ const isWssReady = this.currentSslPort !== 0;
39
+ return this.sslConfig ? isWsReady && isWssReady : isWsReady;
40
+ }
41
+ async sendMessageToClient(clientId, envelop) {
42
+ const ws = this.clients[clientId];
43
+ if (!ws) {
44
+ throw new errors_1.ServiceError('4001');
45
+ }
46
+ let data = envelop.data;
47
+ if (envelop.secure) {
48
+ if (!this.pairingService.isClientConfirmed(clientId)) {
49
+ this.sendMessage(ws, (0, socket_service_1.buildOutgoingSocketMessage)('requestPairing', undefined));
50
+ return;
51
+ }
52
+ try {
53
+ data = await this.pairingService.encryptDataForClient(clientId, JSON.stringify(data));
54
+ }
55
+ catch (error) {
56
+ this.logger.debug(error);
57
+ this.sendMessage(ws, (0, socket_service_1.buildOutgoingSocketMessage)('requestPairing', undefined));
58
+ return;
59
+ }
60
+ }
61
+ this.sendMessage(ws, { ...envelop, data });
62
+ }
63
+ broadcastMessageToAll(envelop) {
64
+ if (envelop.secure) {
65
+ throw new errors_1.ServiceError('4002');
66
+ }
67
+ this.server?.clients?.forEach((ws) => {
68
+ if (ws.readyState === ws_1.WebSocket.OPEN) {
69
+ this.sendMessage(ws, envelop);
70
+ }
71
+ });
72
+ }
73
+ broadcastToAllExcept(clientId, envelop) {
74
+ if (envelop.secure) {
75
+ throw new errors_1.ServiceError('4002');
76
+ }
77
+ Object.keys(this.clients)
78
+ .filter((id) => id !== clientId)
79
+ .forEach((id) => {
80
+ if (this.clients[id].readyState === ws_1.WebSocket.OPEN) {
81
+ this.sendMessage(this.clients[id], envelop);
82
+ }
83
+ });
84
+ }
85
+ async initSocket() {
86
+ this.currentPort = 0;
87
+ for (const port of config_1.WEBSOCKET_PORTS) {
88
+ try {
89
+ const isSuccess = await this.initSocketOnPort(port);
90
+ if (isSuccess) {
91
+ this.currentPort = port;
92
+ return;
93
+ }
94
+ }
95
+ catch (error) {
96
+ //
97
+ }
98
+ }
99
+ }
100
+ async initSslSocket() {
101
+ this.currentSslPort = 0;
102
+ for (const port of config_1.WEBSOCKET_SSL_PORTS) {
103
+ try {
104
+ const isSuccess = await this.initSocketOnPort(port, true);
105
+ if (isSuccess) {
106
+ this.currentSslPort = port;
107
+ return;
108
+ }
109
+ }
110
+ catch (error) {
111
+ this.logger.debug(error);
112
+ }
113
+ }
114
+ }
115
+ // ---------------------- PRIVATE METHODS ----------------------
116
+ initSocketOnPort(port, enableSsl) {
117
+ return new Promise((resolve) => {
118
+ // Host API server
119
+ const apiHandler = (req, res) => {
120
+ if (req.method === 'GET' && req.url === '/ping-locker-desktop') {
121
+ res.writeHead(200, { 'Content-Type': 'application/json' });
122
+ res.end(JSON.stringify({
123
+ message: 'pong',
124
+ alias: this.serviceAlias,
125
+ port: this.currentPort,
126
+ sslPort: this.currentSslPort,
127
+ }));
128
+ }
129
+ else {
130
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
131
+ res.end('Not Found');
132
+ }
133
+ };
134
+ const server = enableSsl && this.sslConfig
135
+ ? https_1.default.createServer(this.sslConfig, apiHandler)
136
+ : http_1.default.createServer(apiHandler);
137
+ server.listen(port);
138
+ // Init socket
139
+ const wss = new ws_1.WebSocketServer({ server });
140
+ const debugName = `Socket${enableSsl ? ' with SSL' : ''}`;
141
+ wss.on('listening', () => {
142
+ this.logger.debug(`${debugName} listening at :${port}`);
143
+ resolve(true);
144
+ });
145
+ wss.on('error', (err) => {
146
+ server.close();
147
+ resolve(false);
148
+ });
149
+ // Setup handler
150
+ wss.on('connection', (ws) => {
151
+ this.logger.debug(`${debugName} connected at :${port}`);
152
+ ws.on('error', (err) => {
153
+ this.logger.error(err);
154
+ });
155
+ ws.on('message', async (data) => {
156
+ this.logger.debug(`Received from ${debugName}: ${data}`);
157
+ try {
158
+ const envelop = JSON.parse(data.toString());
159
+ // Decrypt message if needed
160
+ if (envelop.secure) {
161
+ if (!this.pairingService.isClientConfirmed(envelop.clientId)) {
162
+ this.sendMessage(ws, (0, socket_service_1.buildOutgoingSocketMessage)('requestPairing', undefined));
163
+ return;
164
+ }
165
+ try {
166
+ const decData = await this.pairingService.decryptDataFromClient(envelop.clientId, envelop.data);
167
+ envelop.data = JSON.parse(decData);
168
+ }
169
+ catch (error) {
170
+ this.logger.debug(error);
171
+ this.sendMessage(ws, (0, socket_service_1.buildOutgoingSocketMessage)('requestPairing', undefined));
172
+ return;
173
+ }
174
+ }
175
+ this.handleMessage(ws, envelop);
176
+ }
177
+ catch (error) {
178
+ this.logger.debug(`Invalid message: ${JSON.stringify(data)} - Error: ${error}`);
179
+ }
180
+ });
181
+ });
182
+ this.server = wss;
183
+ });
184
+ }
185
+ sendMessage(ws, envelop) {
186
+ this.logger.debug(`Send ${envelop.message}: ${JSON.stringify(envelop.data)}`);
187
+ ws.send(JSON.stringify(envelop));
188
+ }
189
+ async handleMessage(ws, envelop) {
190
+ if (envelop.clientId) {
191
+ this.clients[envelop.clientId] = ws;
192
+ }
193
+ switch (envelop.message) {
194
+ // Ping
195
+ case 'ping':
196
+ this.sendMessage(ws, (0, socket_service_1.buildOutgoingSocketMessage)('pingResponse', envelop.data));
197
+ this.eventService.emit('clientPing', envelop.data);
198
+ break;
199
+ // Pairing request
200
+ case 'pairingRequest': {
201
+ const d = envelop.data;
202
+ const res = await this.pairingService.getResponseForPairingRequest(envelop.clientId, d.key, d.clientType || 'web');
203
+ this.sendMessage(ws, res);
204
+ break;
205
+ }
206
+ // When a user login in any client
207
+ case 'userLogin': {
208
+ // Ignore unconfirmed client
209
+ if (!this.pairingService.isClientConfirmed(envelop.clientId)) {
210
+ break;
211
+ }
212
+ const d = envelop.data;
213
+ if (d.email !== this.userService.currentUser?.email) {
214
+ const cred = await this.userService.getCurrentUser();
215
+ if (cred) {
216
+ this.eventService.emit('userLogin', cred);
217
+ }
218
+ }
219
+ // Notify other clients
220
+ this.broadcastToAllExcept(envelop.clientId, (0, socket_service_1.buildOutgoingSocketMessage)('userLogin', { email: d.email }));
221
+ break;
222
+ }
223
+ // When a user logout in any client
224
+ case 'userLogout': {
225
+ // Ignore unconfirmed client
226
+ if (!this.pairingService.isClientConfirmed(envelop.clientId)) {
227
+ break;
228
+ }
229
+ const d = envelop.data;
230
+ if (d.email === this.userService.currentUser?.email) {
231
+ await this.userService.logout(true);
232
+ this.eventService.emit('userLogout', { email: d.email });
233
+ }
234
+ // Notify other clients
235
+ this.broadcastToAllExcept(envelop.clientId, (0, socket_service_1.buildOutgoingSocketMessage)('userLogout', { email: d.email }));
236
+ break;
237
+ }
238
+ // When a user lock in any client
239
+ case 'userLock': {
240
+ // Ignore unconfirmed client
241
+ if (!this.pairingService.isClientConfirmed(envelop.clientId)) {
242
+ break;
243
+ }
244
+ const d = envelop.data;
245
+ if (d.email === this.userService.currentUser?.email) {
246
+ await this.userService.lock(true);
247
+ this.eventService.emit('userLock', { email: d.email });
248
+ }
249
+ // Notify other clients
250
+ this.broadcastToAllExcept(envelop.clientId, (0, socket_service_1.buildOutgoingSocketMessage)('userLock', { email: d.email }));
251
+ break;
252
+ }
253
+ // Custom message
254
+ case 'customMessage': {
255
+ const d = envelop.data;
256
+ this.eventService.emit('customMessageReceived', d);
257
+ this.broadcastToAllExcept(envelop.clientId, (0, socket_service_1.buildOutgoingSocketMessage)('customMessage', d));
258
+ break;
259
+ }
260
+ default:
261
+ this.logger.debug(`Invalid message: ${envelop.message} - data: ${envelop.data}`);
262
+ }
263
+ }
264
+ }
265
+ exports.SocketService = SocketService;
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UserService = void 0;
4
+ const errors_1 = require("../abstractions/errors");
5
+ const locker_service_grpc_1 = require("../proto/locker-service-grpc");
6
+ const STORAGE_KEY = 'service_current_user';
7
+ var LoginSyncMessage;
8
+ (function (LoginSyncMessage) {
9
+ LoginSyncMessage["LOGIN"] = "loginSuccess";
10
+ LoginSyncMessage["LOGOUT"] = "logoutSuccess";
11
+ LoginSyncMessage["GET_USER"] = "storedCredRequest";
12
+ })(LoginSyncMessage || (LoginSyncMessage = {}));
13
+ class UserService {
14
+ logger;
15
+ storage;
16
+ grpc;
17
+ currentUser = null;
18
+ isLocked = true;
19
+ isReady = false;
20
+ constructor(services) {
21
+ const { logger, storageService, grpcService } = services;
22
+ this.logger = logger;
23
+ this.storage = storageService;
24
+ this.grpc = grpcService;
25
+ this.loadFromStore().then(() => (this.isReady = true));
26
+ }
27
+ async login(data) {
28
+ await Promise.all([this.loginService(data), this.setUser(data)]);
29
+ this.isLocked = false;
30
+ }
31
+ async logout(localOnly) {
32
+ if (localOnly) {
33
+ await this.setUser(null);
34
+ }
35
+ else {
36
+ await Promise.all([this.logoutService(), this.setUser(null)]);
37
+ }
38
+ this.isLocked = true;
39
+ }
40
+ async lock(localOnly) {
41
+ // TODO
42
+ this.isLocked = true;
43
+ }
44
+ async getCurrentUser() {
45
+ const res = await this.getCurrentUserFromService();
46
+ await this.setUser(res);
47
+ return res;
48
+ }
49
+ // ---------------------- PRIVATE METHODS ----------------------
50
+ getCurrentUserFromService() {
51
+ const req = new locker_service_grpc_1.locker_service_grpc.LoginSyncRequest();
52
+ req.message = LoginSyncMessage.GET_USER;
53
+ return new Promise((resolve, reject) => {
54
+ this.grpc.client.LoginSyncChannel(req, (err, res) => {
55
+ this.logger.debug(res);
56
+ if (err) {
57
+ reject(errors_1.ServiceError.fromError(err));
58
+ return;
59
+ }
60
+ resolve(res?.email
61
+ ? {
62
+ email: res.email,
63
+ key: res.key,
64
+ hashedPassword: res.hashed_pass,
65
+ }
66
+ : null);
67
+ });
68
+ });
69
+ }
70
+ loginService(data) {
71
+ const req = new locker_service_grpc_1.locker_service_grpc.LoginSyncRequest();
72
+ req.message = LoginSyncMessage.LOGIN;
73
+ req.email = data.email;
74
+ req.key = data.key;
75
+ req.hashed_pass = data.hashedPassword;
76
+ return new Promise((resolve, reject) => {
77
+ this.grpc.client.LoginSyncChannel(req, (err, res) => {
78
+ this.logger.debug(res);
79
+ if (err) {
80
+ reject(errors_1.ServiceError.fromError(err));
81
+ return;
82
+ }
83
+ resolve();
84
+ });
85
+ });
86
+ }
87
+ logoutService() {
88
+ const req = new locker_service_grpc_1.locker_service_grpc.LoginSyncRequest();
89
+ req.message = LoginSyncMessage.LOGOUT;
90
+ return new Promise((resolve, reject) => {
91
+ this.grpc.client.LoginSyncChannel(req, (err, res) => {
92
+ this.logger.debug(res);
93
+ if (err) {
94
+ reject(errors_1.ServiceError.fromError(err));
95
+ return;
96
+ }
97
+ resolve();
98
+ });
99
+ });
100
+ }
101
+ async setUser(data) {
102
+ this.currentUser = data;
103
+ await this.saveToStore();
104
+ }
105
+ async loadFromStore() {
106
+ const data = await this.storage.getSecure(STORAGE_KEY);
107
+ if (data) {
108
+ this.currentUser = data;
109
+ }
110
+ this.isLocked = !data?.key;
111
+ }
112
+ saveToStore() {
113
+ return this.storage.setSecure(STORAGE_KEY, this.currentUser);
114
+ }
115
+ }
116
+ exports.UserService = UserService;
@@ -0,0 +1,40 @@
1
+ import { OS, OmitKeys } from '../misc/utils';
2
+ export type PasswordlessType = 'prf' | 'hmac';
3
+ export type GetPublicPwlCredentialResponse = {
4
+ credential_id: string | null;
5
+ random: string | null;
6
+ name: string | null;
7
+ creation_date: number | null;
8
+ last_use_date: number | null;
9
+ type: PasswordlessType;
10
+ backup_keys: {
11
+ name: string;
12
+ credential_id: string;
13
+ random: string;
14
+ creation_date: number | null;
15
+ last_use_date: number | null;
16
+ type: PasswordlessType;
17
+ }[];
18
+ };
19
+ export type BackupKey = {
20
+ id: string;
21
+ name: string;
22
+ creation_date: number | null;
23
+ last_use_date: number | null;
24
+ master_password_hash: string;
25
+ key: string;
26
+ fd_credential_id: string;
27
+ fd_random: string;
28
+ type: PasswordlessType;
29
+ };
30
+ export type GetReleasesResponse = {
31
+ version: string;
32
+ environment: string;
33
+ platform: OS;
34
+ checksum: {
35
+ desktop: string;
36
+ service: string;
37
+ };
38
+ }[];
39
+ export type SetBackupPwlParams = OmitKeys<BackupKey, 'creation_date' | 'last_use_date' | 'id'>;
40
+ //# sourceMappingURL=api.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.service.d.ts","sourceRoot":"","sources":["../../../../src/abstractions/api.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE5C,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,MAAM,CAAA;AAE7C,MAAM,MAAM,8BAA8B,GAAG;IAC3C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,IAAI,EAAE,gBAAgB,CAAA;IACtB,WAAW,EAAE;QACX,IAAI,EAAE,MAAM,CAAA;QACZ,aAAa,EAAE,MAAM,CAAA;QACrB,MAAM,EAAE,MAAM,CAAA;QACd,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;QAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;QAC5B,IAAI,EAAE,gBAAgB,CAAA;KACvB,EAAE,CAAA;CACJ,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,gBAAgB,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,EAAE,CAAA;IACZ,QAAQ,EAAE;QACR,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,CAAA;KAChB,CAAA;CACF,EAAE,CAAA;AAEH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,SAAS,EAAE,eAAe,GAAG,eAAe,GAAG,IAAI,CAAC,CAAA"}