@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,191 @@
|
|
|
1
|
+
import { createPublicKey, verify } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export class PublicKey {
|
|
4
|
+
pem: string;
|
|
5
|
+
|
|
6
|
+
constructor(mode?: string) {
|
|
7
|
+
if (mode && (mode === 'test' || mode === 'development')) {
|
|
8
|
+
this.pem = `-----BEGIN PUBLIC KEY-----
|
|
9
|
+
MCowBQYDK2VwAyEAO301jvpO12znGdK/Izrre518pgmQNk9hSMXf4wDMucM=
|
|
10
|
+
-----END PUBLIC KEY-----
|
|
11
|
+
`;
|
|
12
|
+
} else {
|
|
13
|
+
this.pem = `-----BEGIN PUBLIC KEY-----
|
|
14
|
+
MCowBQYDK2VwAyEAMtpzMn9YfS0fGaDLcAmYQx2OH8kVevwbNyQ1RIj5cvw=
|
|
15
|
+
-----END PUBLIC KEY-----
|
|
16
|
+
`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getKey() {
|
|
21
|
+
return createPublicKey(this.pem);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
toString() {
|
|
25
|
+
return this.pem;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface DecodedLicense {
|
|
30
|
+
header: string;
|
|
31
|
+
payload: string;
|
|
32
|
+
signature: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type HarperLicenseTyp = 'Harper-License';
|
|
36
|
+
type HarperLicenseAlg = 'EdDSA';
|
|
37
|
+
|
|
38
|
+
export interface LicenseHeader {
|
|
39
|
+
typ: HarperLicenseTyp;
|
|
40
|
+
alg: HarperLicenseAlg;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface LicensePayload {
|
|
44
|
+
id: string;
|
|
45
|
+
level: number;
|
|
46
|
+
region?: string;
|
|
47
|
+
reads: number;
|
|
48
|
+
writes: number;
|
|
49
|
+
readBytes: number;
|
|
50
|
+
writeBytes: number;
|
|
51
|
+
realTimeMessages: number;
|
|
52
|
+
realTimeBytes: number;
|
|
53
|
+
cpuTime: number;
|
|
54
|
+
storage: number;
|
|
55
|
+
expiration: string;
|
|
56
|
+
autoRenew?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type ValidatedLicense = LicensePayload;
|
|
60
|
+
|
|
61
|
+
export class LicenseEncodingError extends TypeError {}
|
|
62
|
+
|
|
63
|
+
export class InvalidLicenseError extends TypeError {}
|
|
64
|
+
|
|
65
|
+
export class InvalidLicenseSignatureError extends InvalidLicenseError {}
|
|
66
|
+
|
|
67
|
+
export class InvalidHeaderError extends InvalidLicenseError {}
|
|
68
|
+
|
|
69
|
+
export class InvalidPayloadError extends InvalidLicenseError {}
|
|
70
|
+
|
|
71
|
+
let publicKey: PublicKey;
|
|
72
|
+
|
|
73
|
+
export function initPublicKey(mode?: string) {
|
|
74
|
+
publicKey = new PublicKey(mode);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getPublicKey(): PublicKey {
|
|
78
|
+
if (!publicKey) {
|
|
79
|
+
publicKey = new PublicKey();
|
|
80
|
+
}
|
|
81
|
+
return publicKey;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function validateLicenseSignature(encodedLicense: string): DecodedLicense {
|
|
85
|
+
if (typeof encodedLicense !== 'string') {
|
|
86
|
+
throw new LicenseEncodingError(`License must be a string; received ${typeof encodedLicense}: ${encodedLicense}`);
|
|
87
|
+
}
|
|
88
|
+
let licenseComponents: string[];
|
|
89
|
+
try {
|
|
90
|
+
licenseComponents = encodedLicense.split('.');
|
|
91
|
+
} catch (cause) {
|
|
92
|
+
const error = new LicenseEncodingError(
|
|
93
|
+
`Unable to split license into components; license must be a string with three dot-separated parts; got: ${encodedLicense}`
|
|
94
|
+
);
|
|
95
|
+
error.cause = cause;
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (licenseComponents.length !== 3) {
|
|
100
|
+
throw new InvalidLicenseError(`License must have three dot-separated parts; got ${licenseComponents.length}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const [header, payload, signature] = licenseComponents;
|
|
104
|
+
|
|
105
|
+
const pubKey = getPublicKey().getKey();
|
|
106
|
+
const valid = verify(null, Buffer.from(header + '.' + payload, 'utf8'), pubKey, Buffer.from(signature, 'base64url'));
|
|
107
|
+
if (!valid) {
|
|
108
|
+
throw new InvalidLicenseSignatureError('License signature is invalid');
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
header: toJSON(header),
|
|
112
|
+
payload: toJSON(payload),
|
|
113
|
+
signature: toJSON(signature),
|
|
114
|
+
};
|
|
115
|
+
function toJSON(str: string): string {
|
|
116
|
+
return Buffer.from(str, 'base64url').toString('utf8');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function validateLicenseHeader(header: LicenseHeader): void {
|
|
121
|
+
if (header?.typ !== 'Harper-License') {
|
|
122
|
+
throw new InvalidHeaderError(`Invalid license header; typ must be 'Harper-License'; got: ${header?.typ}`);
|
|
123
|
+
}
|
|
124
|
+
if (header?.alg !== 'EdDSA') {
|
|
125
|
+
throw new InvalidHeaderError(`Invalid license header; alg must be 'EdDSA'; got: ${header?.alg}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
type AttrSchema = { required: boolean; type: string };
|
|
130
|
+
|
|
131
|
+
function valid(schema: AttrSchema, value: any) {
|
|
132
|
+
if (schema.required) {
|
|
133
|
+
return typeof value === schema.type;
|
|
134
|
+
}
|
|
135
|
+
return typeof value === 'undefined' || typeof value === schema.type;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function validateLicensePayload(payload: LicensePayload): void {
|
|
139
|
+
const attrs = {
|
|
140
|
+
id: { required: true, type: 'string' },
|
|
141
|
+
region: { required: false, type: 'string' },
|
|
142
|
+
expiration: { required: true, type: 'string' },
|
|
143
|
+
level: { required: true, type: 'number' },
|
|
144
|
+
reads: { required: true, type: 'number' },
|
|
145
|
+
writes: { required: true, type: 'number' },
|
|
146
|
+
readBytes: { required: true, type: 'number' },
|
|
147
|
+
writeBytes: { required: true, type: 'number' },
|
|
148
|
+
realTimeMessages: { required: true, type: 'number' },
|
|
149
|
+
realTimeBytes: { required: true, type: 'number' },
|
|
150
|
+
cpuTime: { required: true, type: 'number' },
|
|
151
|
+
storage: { required: true, type: 'number' },
|
|
152
|
+
autoRenew: { required: false, type: 'boolean' },
|
|
153
|
+
};
|
|
154
|
+
for (const attr in attrs) {
|
|
155
|
+
const { required, type } = attrs[attr];
|
|
156
|
+
const attrDesc = required ? `required attribute '${attr}'` : `optional attribute '${attr}', when present,`;
|
|
157
|
+
if (!valid(attrs[attr], payload[attr])) {
|
|
158
|
+
throw new InvalidPayloadError(
|
|
159
|
+
`Invalid license payload; ${attrDesc} must be a ${type}; got: ${typeof payload[attr]}`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function validateLicense(encodedLicense: string): ValidatedLicense {
|
|
166
|
+
const { header: headerJSON, payload: payloadJSON } = validateLicenseSignature(encodedLicense);
|
|
167
|
+
|
|
168
|
+
let header: LicenseHeader;
|
|
169
|
+
try {
|
|
170
|
+
header = JSON.parse(headerJSON);
|
|
171
|
+
} catch (cause) {
|
|
172
|
+
const error = new InvalidHeaderError();
|
|
173
|
+
error.cause = cause;
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
validateLicenseHeader(header);
|
|
178
|
+
|
|
179
|
+
let payload: LicensePayload;
|
|
180
|
+
try {
|
|
181
|
+
payload = JSON.parse(payloadJSON);
|
|
182
|
+
} catch (cause) {
|
|
183
|
+
const error = new InvalidPayloadError();
|
|
184
|
+
error.cause = cause;
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
validateLicensePayload(payload);
|
|
189
|
+
|
|
190
|
+
return payload;
|
|
191
|
+
}
|