@harperfast/harper-pro 5.0.0-alpha.2 → 5.0.0-alpha.3
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/core/CONTRIBUTING.md +2 -0
- package/core/package.json +2 -2
- package/core/resources/DatabaseTransaction.ts +1 -1
- package/core/resources/LMDBTransaction.ts +9 -4
- package/core/resources/databases.ts +1 -1
- package/core/unitTests/resources/permissions.test.js +7 -2
- package/core/unitTests/resources/txn-tracking.test.js +10 -4
- package/core/unitTests/resources/vectorIndex.test.js +1 -0
- package/dist/bin/harper.js +1 -1
- package/dist/bin/harper.js.map +1 -1
- package/dist/core/resources/DatabaseTransaction.js +1 -1
- package/dist/core/resources/DatabaseTransaction.js.map +1 -1
- package/dist/core/resources/LMDBTransaction.js +9 -5
- package/dist/core/resources/LMDBTransaction.js.map +1 -1
- package/dist/core/resources/databases.js +1 -1
- package/dist/core/resources/databases.js.map +1 -1
- package/dist/licensing/usageLicensing.js +246 -0
- package/dist/licensing/usageLicensing.js.map +1 -0
- package/dist/licensing/validation.js +149 -0
- package/dist/licensing/validation.js.map +1 -0
- package/dist/replication/replicator.js +5 -2
- package/dist/replication/replicator.js.map +1 -1
- package/dist/replication/setNode.js +0 -1
- package/dist/replication/setNode.js.map +1 -1
- package/dist/security/certificate.js +206 -6
- package/dist/security/certificate.js.map +1 -1
- package/dist/security/keyService.js +58 -0
- package/dist/security/keyService.js.map +1 -0
- package/dist/security/sshKeyOperations.js +343 -0
- package/dist/security/sshKeyOperations.js.map +1 -0
- package/licensing/usageLicensing.ts +262 -0
- package/licensing/validation.ts +191 -0
- package/npm-shrinkwrap.json +253 -253
- package/package.json +3 -2
- package/replication/replicator.ts +6 -2
- package/replication/setNode.ts +0 -1
- package/security/certificate.ts +259 -7
- package/security/keyService.ts +74 -0
- package/security/sshKeyOperations.ts +405 -0
- package/static/defaultConfig.yaml +2 -0
|
@@ -0,0 +1,343 @@
|
|
|
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
|
+
const joi_1 = __importDefault(require("joi"));
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const promises_1 = require("node:fs/promises");
|
|
9
|
+
const validationWrapper_js_1 = require("../core/validation/validationWrapper.js");
|
|
10
|
+
const harper_logger_js_1 = __importDefault(require("../core/utility/logging/harper_logger.js"));
|
|
11
|
+
const hdbError_js_1 = require("../core/utility/errors/hdbError.js");
|
|
12
|
+
const hdbTerms_ts_1 = require("../core/utility/hdbTerms.js");
|
|
13
|
+
const environmentManager_js_1 = __importDefault(require("../core/utility/environment/environmentManager.js"));
|
|
14
|
+
const replicator_ts_1 = require("../replication/replicator.js");
|
|
15
|
+
// SSH key name can only be alphanumeric, dash and underscores
|
|
16
|
+
const SSH_KEY_NAME_REGEX = /^[a-zA-Z0-9-_]+$/;
|
|
17
|
+
const SSH_KEY_NAME_ERROR_MSG = 'SSH key name can only contain alphanumeric, dash and underscore characters';
|
|
18
|
+
// Helper function to check if a file or directory exists
|
|
19
|
+
const exists = async (path) => (0, promises_1.access)(path, promises_1.constants.F_OK)
|
|
20
|
+
.then(() => true)
|
|
21
|
+
.catch(() => false);
|
|
22
|
+
// Helper function to write a file ensuring the directory exists
|
|
23
|
+
async function writeFileEnsureDir(filePath, data) {
|
|
24
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)(filePath), { recursive: true });
|
|
25
|
+
await (0, promises_1.writeFile)(filePath, data);
|
|
26
|
+
}
|
|
27
|
+
const addValidationSchema = joi_1.default.object({
|
|
28
|
+
name: joi_1.default.string().pattern(SSH_KEY_NAME_REGEX).required().messages({ 'string.pattern.base': SSH_KEY_NAME_ERROR_MSG }),
|
|
29
|
+
key: joi_1.default.string().required(),
|
|
30
|
+
host: joi_1.default.string().required(),
|
|
31
|
+
hostname: joi_1.default.string().required(),
|
|
32
|
+
known_hosts: joi_1.default.string().optional(),
|
|
33
|
+
});
|
|
34
|
+
const getSSHKeyValidationSchema = joi_1.default.object({
|
|
35
|
+
name: joi_1.default.string().required(),
|
|
36
|
+
});
|
|
37
|
+
const updateSSHKeyValidationSchema = joi_1.default.object({
|
|
38
|
+
name: joi_1.default.string().required(),
|
|
39
|
+
key: joi_1.default.string().required(),
|
|
40
|
+
});
|
|
41
|
+
const deleteSSHKeyValidationSchema = joi_1.default.object({
|
|
42
|
+
name: joi_1.default.string().required(),
|
|
43
|
+
});
|
|
44
|
+
const setSSHKnownHostsValidationSchema = joi_1.default.object({
|
|
45
|
+
known_hosts: joi_1.default.string().required(),
|
|
46
|
+
});
|
|
47
|
+
function getSSHPaths(keyName) {
|
|
48
|
+
const rootDir = environmentManager_js_1.default.get(hdbTerms_ts_1.CONFIG_PARAMS.ROOTPATH);
|
|
49
|
+
const sshDir = (0, node_path_1.join)(rootDir, 'ssh');
|
|
50
|
+
const filePath = keyName ? (0, node_path_1.join)(sshDir, keyName + '.key') : undefined;
|
|
51
|
+
const configFile = (0, node_path_1.join)(sshDir, 'config');
|
|
52
|
+
const knownHostsFile = (0, node_path_1.join)(sshDir, 'known_hosts');
|
|
53
|
+
return { sshDir, filePath, configFile, knownHostsFile };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Adds a new SSH key along with its associated SSH config block and optional
|
|
57
|
+
* known_hosts entries. If the hostname is `github.com`, GitHub's public SSH
|
|
58
|
+
* keys are automatically fetched and added to the known_hosts file.
|
|
59
|
+
*
|
|
60
|
+
* @param req - The request object containing the SSH key details.
|
|
61
|
+
* @param req.name - The name of the SSH key to add.
|
|
62
|
+
* @param req.key - The SSH key contents to write to disk.
|
|
63
|
+
* @param req.host - The Host alias to use in the SSH config block.
|
|
64
|
+
* @param req.hostname - The HostName (real hostname) to use in the SSH config block.
|
|
65
|
+
* @param req.known_hosts - Optional known_hosts entries to append to the known_hosts file.
|
|
66
|
+
* @returns An object containing a success message and optional replication results.
|
|
67
|
+
*/
|
|
68
|
+
async function addSSHKey(req) {
|
|
69
|
+
const validation = (0, validationWrapper_js_1.validateBySchema)(req, addValidationSchema);
|
|
70
|
+
if (validation)
|
|
71
|
+
throw new hdbError_js_1.ClientError(validation.message);
|
|
72
|
+
const { name, key, host, hostname, known_hosts } = req;
|
|
73
|
+
harper_logger_js_1.default?.trace('adding ssh key', name);
|
|
74
|
+
const { filePath, configFile, knownHostsFile } = getSSHPaths(name);
|
|
75
|
+
// Check if the key already exists
|
|
76
|
+
if (await exists(filePath)) {
|
|
77
|
+
throw new hdbError_js_1.ClientError('Key already exists. Use update_ssh_key or delete_ssh_key and then add_ssh_key');
|
|
78
|
+
}
|
|
79
|
+
// Create the key file
|
|
80
|
+
await writeFileEnsureDir(filePath, key);
|
|
81
|
+
await (0, promises_1.chmod)(filePath, 0o600);
|
|
82
|
+
// Build the config block string
|
|
83
|
+
const configBlock = `#${name}
|
|
84
|
+
Host ${host}
|
|
85
|
+
HostName ${hostname}
|
|
86
|
+
User git
|
|
87
|
+
IdentityFile ${filePath}
|
|
88
|
+
IdentitiesOnly yes`;
|
|
89
|
+
// If the file already exists, add a new config block, otherwise write the file for the first time
|
|
90
|
+
if (await exists(configFile)) {
|
|
91
|
+
await (0, promises_1.appendFile)(configFile, '\n' + configBlock);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
await writeFileEnsureDir(configFile, configBlock);
|
|
95
|
+
}
|
|
96
|
+
let additionalMessage = '';
|
|
97
|
+
// Create the known_hosts file and set permissions if missing
|
|
98
|
+
if (!(await exists(knownHostsFile))) {
|
|
99
|
+
await writeFileEnsureDir(knownHostsFile, '');
|
|
100
|
+
await (0, promises_1.chmod)(knownHostsFile, 0o600);
|
|
101
|
+
}
|
|
102
|
+
// If adding a github.com ssh key download it automatically
|
|
103
|
+
if (hostname === 'github.com') {
|
|
104
|
+
const fileContents = await (0, promises_1.readFile)(knownHostsFile, 'utf8');
|
|
105
|
+
// Check if there's already github.com entries
|
|
106
|
+
if (!fileContents.includes('github.com')) {
|
|
107
|
+
try {
|
|
108
|
+
const response = await fetch('https://api.github.com/meta');
|
|
109
|
+
const respJson = await response.json();
|
|
110
|
+
const sshKeys = respJson['ssh_keys'];
|
|
111
|
+
for (const knownHost of sshKeys) {
|
|
112
|
+
await (0, promises_1.appendFile)(knownHostsFile, 'github.com ' + knownHost + '\n');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
additionalMessage =
|
|
117
|
+
'. Unable to get known hosts from github.com. Set your known hosts manually using set_ssh_known_hosts.';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (known_hosts) {
|
|
122
|
+
await (0, promises_1.appendFile)(knownHostsFile, known_hosts);
|
|
123
|
+
}
|
|
124
|
+
let response = await (0, replicator_ts_1.replicateOperation)(req);
|
|
125
|
+
response.message = `Added ssh key: ${name}${additionalMessage}`;
|
|
126
|
+
return response;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Retrieves an SSH key by name, along with any associated Host and HostName
|
|
130
|
+
* configuration from the SSH config file.
|
|
131
|
+
*
|
|
132
|
+
* @param req - The request object containing the key name.
|
|
133
|
+
* @param req.name - The name of the SSH key to retrieve.
|
|
134
|
+
* @returns An object containing the key name, the key contents, and optionally
|
|
135
|
+
* the Host and HostName from the SSH config file.
|
|
136
|
+
*/
|
|
137
|
+
async function getSSHKey(req) {
|
|
138
|
+
const validation = (0, validationWrapper_js_1.validateBySchema)(req, getSSHKeyValidationSchema);
|
|
139
|
+
if (validation)
|
|
140
|
+
throw new hdbError_js_1.ClientError(validation.message);
|
|
141
|
+
const { name } = req;
|
|
142
|
+
const { filePath, configFile } = getSSHPaths(name);
|
|
143
|
+
if (!(await exists(filePath))) {
|
|
144
|
+
throw new hdbError_js_1.ClientError(`SSH key '${name}' does not exist.`);
|
|
145
|
+
}
|
|
146
|
+
harper_logger_js_1.default?.trace(`getting ssh key`, name, filePath);
|
|
147
|
+
const key = await (0, promises_1.readFile)(filePath, 'utf8');
|
|
148
|
+
const result = { name, key };
|
|
149
|
+
if (await exists(configFile)) {
|
|
150
|
+
const configContents = await (0, promises_1.readFile)(configFile, 'utf8');
|
|
151
|
+
const { host, hostname } = extractMatchingHostAndHostname(configContents, name);
|
|
152
|
+
if (host)
|
|
153
|
+
result.host = host;
|
|
154
|
+
if (hostname)
|
|
155
|
+
result.hostname = hostname;
|
|
156
|
+
}
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Updates an existing SSH key by overwriting the key file with new contents.
|
|
161
|
+
*
|
|
162
|
+
* @param req - The request object containing the updated key details.
|
|
163
|
+
* @param req.name - The name of the SSH key to update.
|
|
164
|
+
* @param req.key - The new SSH key contents to write to disk.
|
|
165
|
+
* @returns An object containing a success message and optional replication results.
|
|
166
|
+
*/
|
|
167
|
+
async function updateSSHKey(req) {
|
|
168
|
+
const validation = (0, validationWrapper_js_1.validateBySchema)(req, updateSSHKeyValidationSchema);
|
|
169
|
+
if (validation)
|
|
170
|
+
throw new hdbError_js_1.ClientError(validation.message);
|
|
171
|
+
const { name, key } = req;
|
|
172
|
+
harper_logger_js_1.default?.trace(`updating ssh key`, name);
|
|
173
|
+
const { filePath } = getSSHPaths(name);
|
|
174
|
+
if (!(await exists(filePath))) {
|
|
175
|
+
throw new hdbError_js_1.ClientError(`SSH key '${name}' does not exist. Use add_ssh_key to create it.`);
|
|
176
|
+
}
|
|
177
|
+
await writeFileEnsureDir(filePath, key);
|
|
178
|
+
await (0, promises_1.chmod)(filePath, 0o600);
|
|
179
|
+
const response = await (0, replicator_ts_1.replicateOperation)(req);
|
|
180
|
+
response.message = `Updated ssh key: ${name}`;
|
|
181
|
+
return response;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Deletes an existing SSH key and removes its associated config block from
|
|
185
|
+
* the SSH config file.
|
|
186
|
+
*
|
|
187
|
+
* @param req - The request object containing the key name.
|
|
188
|
+
* @param req.name - The name of the SSH key to delete.
|
|
189
|
+
* @returns An object containing a success message and optional replication results.
|
|
190
|
+
*/
|
|
191
|
+
async function deleteSSHKey(req) {
|
|
192
|
+
const validation = (0, validationWrapper_js_1.validateBySchema)(req, deleteSSHKeyValidationSchema);
|
|
193
|
+
if (validation)
|
|
194
|
+
throw new hdbError_js_1.ClientError(validation.message);
|
|
195
|
+
const { name } = req;
|
|
196
|
+
harper_logger_js_1.default?.trace(`deleting ssh key`, name);
|
|
197
|
+
const { filePath, configFile } = getSSHPaths(name);
|
|
198
|
+
if (!(await exists(filePath))) {
|
|
199
|
+
throw new hdbError_js_1.ClientError(`SSH key '${name}' does not exist.`);
|
|
200
|
+
}
|
|
201
|
+
if (await exists(configFile)) {
|
|
202
|
+
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
203
|
+
const configBlockRegex = new RegExp(`#${escapedName}[\\S\\s]*?IdentitiesOnly yes`, 'g');
|
|
204
|
+
const fileContents = (await (0, promises_1.readFile)(configFile, 'utf8')).replace(configBlockRegex, '').trim();
|
|
205
|
+
await writeFileEnsureDir(configFile, fileContents);
|
|
206
|
+
}
|
|
207
|
+
await (0, promises_1.unlink)(filePath);
|
|
208
|
+
const response = await (0, replicator_ts_1.replicateOperation)(req);
|
|
209
|
+
response.message = `Deleted ssh key: ${name}`;
|
|
210
|
+
return response;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Lists all SSH keys along with their associated Host and HostName
|
|
214
|
+
* configuration from the SSH config file.
|
|
215
|
+
*
|
|
216
|
+
* @returns An array of objects containing the key name and optionally
|
|
217
|
+
* the Host and HostName from the SSH config file.
|
|
218
|
+
*/
|
|
219
|
+
async function listSSHKeys() {
|
|
220
|
+
const { sshDir, configFile } = getSSHPaths(undefined);
|
|
221
|
+
if (!(await exists(sshDir)))
|
|
222
|
+
return [];
|
|
223
|
+
const EXCLUDED_FILES = new Set(['known_hosts', 'config']);
|
|
224
|
+
const configContents = (await exists(configFile)) ? await (0, promises_1.readFile)(configFile, 'utf8') : null;
|
|
225
|
+
const files = await (0, promises_1.readdir)(sshDir);
|
|
226
|
+
return files
|
|
227
|
+
.filter((file) => !EXCLUDED_FILES.has(file))
|
|
228
|
+
.map((file) => {
|
|
229
|
+
const name = (0, node_path_1.basename)(file, '.key');
|
|
230
|
+
const result = { name };
|
|
231
|
+
if (configContents) {
|
|
232
|
+
const { host, hostname } = extractMatchingHostAndHostname(configContents, name);
|
|
233
|
+
if (host)
|
|
234
|
+
result.host = host;
|
|
235
|
+
if (hostname)
|
|
236
|
+
result.hostname = hostname;
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Extracts the Host and HostName values from an SSH config block matching
|
|
243
|
+
* the given key name. Config blocks are identified by a leading comment
|
|
244
|
+
* in the format `#keyName`.
|
|
245
|
+
*
|
|
246
|
+
* @param configContents - The full contents of the SSH config file.
|
|
247
|
+
* @param name - The name of the SSH key whose config block to extract from.
|
|
248
|
+
* @returns An object containing the optional Host and HostName values from
|
|
249
|
+
* the matching config block, or an empty object if no match is found.
|
|
250
|
+
*/
|
|
251
|
+
function extractMatchingHostAndHostname(configContents, name) {
|
|
252
|
+
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
253
|
+
const configBlockRegex = new RegExp(`#${escapedName}[\\S\\s]*?IdentitiesOnly yes`, 'g');
|
|
254
|
+
const match = configContents.match(configBlockRegex);
|
|
255
|
+
if (!match?.[0])
|
|
256
|
+
return {};
|
|
257
|
+
const configBlock = match[0];
|
|
258
|
+
const host = configBlock.match(/^Host\s+(.+)$/m)?.[1]?.trim();
|
|
259
|
+
const hostname = configBlock.match(/^\s*HostName\s+(.+)$/m)?.[1]?.trim();
|
|
260
|
+
return {
|
|
261
|
+
...(host && { host }),
|
|
262
|
+
...(hostname && { hostname }),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Overwrites the SSH known_hosts file with the provided entries.
|
|
267
|
+
*
|
|
268
|
+
* @param req - The request object containing the known_hosts entries.
|
|
269
|
+
* @param req.known_hosts - The known_hosts entries to write to the file.
|
|
270
|
+
* @returns An object containing a success message and optional replication results.
|
|
271
|
+
*/
|
|
272
|
+
async function setSSHKnownHosts(req) {
|
|
273
|
+
const validation = (0, validationWrapper_js_1.validateBySchema)(req, setSSHKnownHostsValidationSchema);
|
|
274
|
+
if (validation)
|
|
275
|
+
throw new hdbError_js_1.ClientError(validation.message);
|
|
276
|
+
const { known_hosts } = req;
|
|
277
|
+
harper_logger_js_1.default?.trace(`setting ssh known hosts`);
|
|
278
|
+
const { knownHostsFile } = getSSHPaths(undefined);
|
|
279
|
+
await writeFileEnsureDir(knownHostsFile, known_hosts);
|
|
280
|
+
await (0, promises_1.chmod)(knownHostsFile, 0o600);
|
|
281
|
+
const response = await (0, replicator_ts_1.replicateOperation)(req);
|
|
282
|
+
response.message = `Known hosts successfully set`;
|
|
283
|
+
return response;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Retrieves the contents of the SSH known_hosts file.
|
|
287
|
+
*
|
|
288
|
+
* @returns An object containing the known_hosts file contents,
|
|
289
|
+
* or `null` if the file does not exist.
|
|
290
|
+
*/
|
|
291
|
+
async function getSSHKnownHosts() {
|
|
292
|
+
harper_logger_js_1.default?.trace(`getting ssh known hosts`);
|
|
293
|
+
const { knownHostsFile } = getSSHPaths(undefined);
|
|
294
|
+
if (!(await exists(knownHostsFile))) {
|
|
295
|
+
return { known_hosts: null };
|
|
296
|
+
}
|
|
297
|
+
return { known_hosts: await (0, promises_1.readFile)(knownHostsFile, 'utf8') };
|
|
298
|
+
}
|
|
299
|
+
// These will register the operations for the operations API. For now the method and schema are ignored,
|
|
300
|
+
// they are there for when build the REST interface for operations API
|
|
301
|
+
server.registerOperation?.({
|
|
302
|
+
name: 'add_ssh_key',
|
|
303
|
+
execute: addSSHKey,
|
|
304
|
+
httpMethod: 'PUT',
|
|
305
|
+
parametersSchema: [{ name: 'hostname', in: 'path', schema: { type: 'string' } }],
|
|
306
|
+
});
|
|
307
|
+
server.registerOperation?.({
|
|
308
|
+
name: 'get_ssh_key',
|
|
309
|
+
execute: getSSHKey,
|
|
310
|
+
httpMethod: 'GET',
|
|
311
|
+
parametersSchema: [{ name: 'hostname', in: 'path', schema: { type: 'string' } }],
|
|
312
|
+
});
|
|
313
|
+
server.registerOperation?.({
|
|
314
|
+
name: 'update_ssh_key',
|
|
315
|
+
execute: updateSSHKey,
|
|
316
|
+
httpMethod: 'PATCH',
|
|
317
|
+
parametersSchema: [{ name: 'hostname', in: 'path', schema: { type: 'string' } }],
|
|
318
|
+
});
|
|
319
|
+
server.registerOperation?.({
|
|
320
|
+
name: 'delete_ssh_key',
|
|
321
|
+
execute: deleteSSHKey,
|
|
322
|
+
httpMethod: 'DELETE',
|
|
323
|
+
parametersSchema: [{ name: 'hostname', in: 'path', schema: { type: 'string' } }],
|
|
324
|
+
});
|
|
325
|
+
server.registerOperation?.({
|
|
326
|
+
name: 'list_ssh_keys',
|
|
327
|
+
execute: listSSHKeys,
|
|
328
|
+
httpMethod: 'GET',
|
|
329
|
+
parametersSchema: [{ name: 'hostname', in: 'path', schema: { type: 'string' } }],
|
|
330
|
+
});
|
|
331
|
+
server.registerOperation?.({
|
|
332
|
+
name: 'set_ssh_known_hosts',
|
|
333
|
+
execute: setSSHKnownHosts,
|
|
334
|
+
httpMethod: 'PUT',
|
|
335
|
+
parametersSchema: [{ name: 'hostname', in: 'path', schema: { type: 'string' } }],
|
|
336
|
+
});
|
|
337
|
+
server.registerOperation?.({
|
|
338
|
+
name: 'get_ssh_known_hosts',
|
|
339
|
+
execute: getSSHKnownHosts,
|
|
340
|
+
httpMethod: 'GET',
|
|
341
|
+
parametersSchema: [{ name: 'hostname', in: 'path', schema: { type: 'string' } }],
|
|
342
|
+
});
|
|
343
|
+
//# sourceMappingURL=sshKeyOperations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sshKeyOperations.js","sourceRoot":"","sources":["../../security/sshKeyOperations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { type ValidatedLicense, validateLicense, initPublicKey } from './validation.ts';
|
|
2
|
+
import { ClientError } from '../core/utility/errors/hdbError.js';
|
|
3
|
+
import { onAnalyticsAggregate } from '../core/resources/analytics/write.ts';
|
|
4
|
+
import { transaction } from '../core/resources/transaction.ts';
|
|
5
|
+
import { databases } from '../core/resources/databases.ts';
|
|
6
|
+
import { watch } from 'chokidar';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import * as fs from 'node:fs/promises';
|
|
9
|
+
import type { Stats } from 'node:fs';
|
|
10
|
+
import * as configUtils from '../core/config/configUtils.js';
|
|
11
|
+
import * as terms from '../core/utility/hdbTerms.ts';
|
|
12
|
+
import type { Server } from '../core/server/Server.ts';
|
|
13
|
+
import type { Scope } from "../core/components/Scope.ts";
|
|
14
|
+
|
|
15
|
+
// eslint-disable-next-line no-unused-vars
|
|
16
|
+
export const suppressHandleApplicationWarning = true;
|
|
17
|
+
|
|
18
|
+
let logger: any;
|
|
19
|
+
|
|
20
|
+
class ExistingLicenseError extends Error {}
|
|
21
|
+
|
|
22
|
+
interface InstallLicenseRequest {
|
|
23
|
+
operation: 'install_usage_license';
|
|
24
|
+
license: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface UsageLicense extends ValidatedLicense {
|
|
28
|
+
usedReads?: number;
|
|
29
|
+
usedReadBytes?: number;
|
|
30
|
+
usedWrites?: number;
|
|
31
|
+
usedWriteBytes?: number;
|
|
32
|
+
usedRealTimeMessages?: number;
|
|
33
|
+
usedRealTimeBytes?: number;
|
|
34
|
+
usedCpuTime?: number;
|
|
35
|
+
usedStorage?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface GetUsageLicenseParams {
|
|
39
|
+
region?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface GetUsageLicensesReq extends GetUsageLicenseParams {
|
|
43
|
+
operation: 'get_usage_licenses';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let licenseRegion: string | undefined;
|
|
47
|
+
let licenseConsoleErrorPrinted = false;
|
|
48
|
+
let licenseWarningIntervalId: NodeJS.Timeout;
|
|
49
|
+
const LICENSE_NAG_PERIOD = 600000; // ten minutes
|
|
50
|
+
|
|
51
|
+
export function handleApplication({ server, logger, options }: Scope) {
|
|
52
|
+
const region = options.get(terms.CONFIG_PARAMS.LICENSE_REGION.split('_')) as string;
|
|
53
|
+
const mode = options.get(terms.CONFIG_PARAMS.LICENSE_MODE.split('_')) as string;
|
|
54
|
+
initUsageLicensing({ server, logger, license: { region, mode } });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface LicenseParams {
|
|
58
|
+
region: string,
|
|
59
|
+
mode: string,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface UsageLicensingInitParams {
|
|
63
|
+
server: Server,
|
|
64
|
+
logger: any,
|
|
65
|
+
license: LicenseParams,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function initUsageLicensing(params: UsageLicensingInitParams) {
|
|
69
|
+
logger = params.logger;
|
|
70
|
+
|
|
71
|
+
initPublicKey(params.license.mode);
|
|
72
|
+
|
|
73
|
+
licenseRegion = params.license.region;
|
|
74
|
+
|
|
75
|
+
const { server } = params;
|
|
76
|
+
|
|
77
|
+
// TODO: This only injects this header on resource API requests.
|
|
78
|
+
// It might be good to add a way for internal plugins to
|
|
79
|
+
// add headers to operations API requests as well.
|
|
80
|
+
server.http(async (request, nextHandler) => {
|
|
81
|
+
const response = nextHandler(request);
|
|
82
|
+
if (response) {
|
|
83
|
+
if (!response.headers?.set) {
|
|
84
|
+
(response as any).headers = new Headers(response.headers);
|
|
85
|
+
}
|
|
86
|
+
if (await isLicensed()) {
|
|
87
|
+
response.headers.set('Server', 'Harper Pro');
|
|
88
|
+
} else {
|
|
89
|
+
response.headers.set(
|
|
90
|
+
'Server',
|
|
91
|
+
'Unlicensed Harper Pro, this should only be used for educational and development purposes'
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return response;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
onAnalyticsAggregate(recordUsage);
|
|
99
|
+
|
|
100
|
+
server.registerOperation?.({
|
|
101
|
+
name: 'install_usage_license',
|
|
102
|
+
execute: installUsageLicenseOp,
|
|
103
|
+
httpMethod: 'POST',
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
server.registerOperation?.({
|
|
107
|
+
name: 'get_usage_licenses',
|
|
108
|
+
execute: getUsageLicensesOp,
|
|
109
|
+
httpMethod: 'GET',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const licensesPath = path.join(path.dirname(configUtils.getConfigFilePath()), 'licenses');
|
|
113
|
+
const watchOptions = {
|
|
114
|
+
persistent: false,
|
|
115
|
+
ignoreInitial: false,
|
|
116
|
+
depth: 0,
|
|
117
|
+
ignored: (file: string, stats: Stats) => stats?.isFile() && !file.endsWith('.txt'),
|
|
118
|
+
};
|
|
119
|
+
watch(licensesPath, watchOptions).on('add', loadLicenseFile);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function installUsageLicenseOp(req: InstallLicenseRequest): Promise<string> {
|
|
123
|
+
const license = req.license;
|
|
124
|
+
try {
|
|
125
|
+
await installUsageLicense(license);
|
|
126
|
+
} catch (cause) {
|
|
127
|
+
const error = new ClientError('Failed to install usage license; ' + cause.message);
|
|
128
|
+
error.cause = cause;
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
return 'Successfully installed usage license';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function installUsageLicense(license: string): Promise<void> {
|
|
135
|
+
const validatedLicense = validateLicense(license);
|
|
136
|
+
const { id } = validatedLicense;
|
|
137
|
+
const existingLicense = await databases.system.hdb_license.get(id);
|
|
138
|
+
if (existingLicense) {
|
|
139
|
+
throw new ExistingLicenseError(`A usage license with ${id} already exists`);
|
|
140
|
+
}
|
|
141
|
+
logger.info?.('Installing usage license:', validatedLicense);
|
|
142
|
+
return databases.system.hdb_license.put(id, validatedLicense);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function isActiveLicense(license: UsageLicense): boolean {
|
|
146
|
+
return (
|
|
147
|
+
(license.reads === -1 || (license.usedReads ?? 0) < license.reads) &&
|
|
148
|
+
(license.readBytes === -1 || (license.usedReadBytes ?? 0) < license.readBytes) &&
|
|
149
|
+
(license.writes === -1 || (license.usedWrites ?? 0) < license.writes) &&
|
|
150
|
+
(license.writeBytes === -1 || (license.usedWriteBytes ?? 0) < license.writeBytes) &&
|
|
151
|
+
(license.realTimeMessages === -1 || (license.usedRealTimeMessages ?? 0) < license.realTimeMessages) &&
|
|
152
|
+
(license.realTimeBytes === -1 || (license.usedRealTimeBytes ?? 0) < license.realTimeBytes) &&
|
|
153
|
+
(license.cpuTime === -1 || (license.usedCpuTime ?? 0) < license.cpuTime) &&
|
|
154
|
+
(license.storage === -1 || (license.usedStorage ?? 0) < license.storage)
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function getActiveLicense(): Promise<UsageLicense | undefined> {
|
|
159
|
+
const licenseQuery = {
|
|
160
|
+
sort: { attribute: '__createdtime__' },
|
|
161
|
+
conditions: [{ attribute: 'expiration', comparator: 'greater_than', value: new Date().toISOString() }],
|
|
162
|
+
};
|
|
163
|
+
if (licenseRegion !== undefined) {
|
|
164
|
+
licenseQuery.conditions.push({ attribute: 'region', comparator: 'equals', value: licenseRegion });
|
|
165
|
+
}
|
|
166
|
+
const results = databases.system.hdb_license?.search(licenseQuery as any);
|
|
167
|
+
for await (const license of results ?? []) {
|
|
168
|
+
if (isActiveLicense(license)) {
|
|
169
|
+
return license;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function isLicensed(): Promise<boolean> {
|
|
176
|
+
const activeLicense = await getActiveLicense();
|
|
177
|
+
return activeLicense !== undefined;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function recordUsage(analytics: any) {
|
|
181
|
+
logger.trace?.('Recording usage into license from analytics');
|
|
182
|
+
const activeLicenseId = (await getActiveLicense())?.id;
|
|
183
|
+
if (activeLicenseId) {
|
|
184
|
+
logger.trace?.('Found license to record usage into:', activeLicenseId);
|
|
185
|
+
const context: any = {};
|
|
186
|
+
transaction(context, () => {
|
|
187
|
+
const updatableActiveLicense = databases.system.hdb_license.update(activeLicenseId, context);
|
|
188
|
+
for (const analyticsRecord of analytics) {
|
|
189
|
+
logger.trace?.('Processing analytics record:', analyticsRecord);
|
|
190
|
+
switch (analyticsRecord.metric) {
|
|
191
|
+
case 'db-read':
|
|
192
|
+
logger.trace?.('Recording read usage into license');
|
|
193
|
+
updatableActiveLicense.addTo('usedReads', analyticsRecord.count);
|
|
194
|
+
updatableActiveLicense.addTo('usedReadBytes', analyticsRecord.mean * analyticsRecord.count);
|
|
195
|
+
break;
|
|
196
|
+
case 'db-write':
|
|
197
|
+
logger.trace?.('Recording write usage into license');
|
|
198
|
+
updatableActiveLicense.addTo('usedWrites', analyticsRecord.count);
|
|
199
|
+
updatableActiveLicense.addTo('usedWriteBytes', analyticsRecord.mean * analyticsRecord.count);
|
|
200
|
+
break;
|
|
201
|
+
case 'db-message':
|
|
202
|
+
logger.trace?.('Recording message usage into license');
|
|
203
|
+
updatableActiveLicense.addTo('usedRealTimeMessages', analyticsRecord.count);
|
|
204
|
+
updatableActiveLicense.addTo('usedRealTimeBytes', analyticsRecord.mean * analyticsRecord.count);
|
|
205
|
+
break;
|
|
206
|
+
case 'cpu-usage':
|
|
207
|
+
if (analyticsRecord.path === 'user') {
|
|
208
|
+
logger.trace?.('Recording CPU usage into license');
|
|
209
|
+
updatableActiveLicense.addTo('usedCpuTime', (analyticsRecord.mean * analyticsRecord.count) / 3600);
|
|
210
|
+
}
|
|
211
|
+
break;
|
|
212
|
+
default:
|
|
213
|
+
logger.trace?.('Skipping metric:', analyticsRecord.metric);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
} else if (!process.env.DEV_MODE) {
|
|
218
|
+
const msg =
|
|
219
|
+
'This server does not have valid usage licenses, this should only be used for educational and development purposes.';
|
|
220
|
+
if (!licenseConsoleErrorPrinted) {
|
|
221
|
+
console.error(msg);
|
|
222
|
+
licenseConsoleErrorPrinted = true;
|
|
223
|
+
}
|
|
224
|
+
if (licenseWarningIntervalId === undefined) {
|
|
225
|
+
licenseWarningIntervalId = setInterval(() => {
|
|
226
|
+
logger.notify?.(msg);
|
|
227
|
+
}, LICENSE_NAG_PERIOD).unref();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function getUsageLicensesOp(req: GetUsageLicensesReq): AsyncIterable<UsageLicense> {
|
|
233
|
+
const params: GetUsageLicenseParams = {};
|
|
234
|
+
if (req.region) {
|
|
235
|
+
params.region = req.region;
|
|
236
|
+
}
|
|
237
|
+
return getUsageLicenses(params);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function getUsageLicenses(params?: GetUsageLicenseParams): AsyncIterable<UsageLicense> {
|
|
241
|
+
const conditions = [];
|
|
242
|
+
const attrs = typeof params === 'object' ? Object.keys(params) : [];
|
|
243
|
+
if (attrs.length > 0) {
|
|
244
|
+
attrs.forEach((attribute) => {
|
|
245
|
+
conditions.push({ attribute, comparator: 'equals', value: params[attribute] });
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
return databases.system.hdb_license.search({
|
|
249
|
+
sort: { attribute: '__createdtime__' },
|
|
250
|
+
conditions,
|
|
251
|
+
} as any);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function loadLicenseFile(filePath: string) {
|
|
255
|
+
logger.trace?.('Loading usage license from file:', filePath);
|
|
256
|
+
const encodedLicense = await fs.readFile(filePath, { encoding: 'utf-8' });
|
|
257
|
+
try {
|
|
258
|
+
await installUsageLicense(encodedLicense);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
logger.error?.('Failed to install usage license from file:', filePath, err);
|
|
261
|
+
}
|
|
262
|
+
}
|