node-opcua-server-configuration 2.97.0 → 2.98.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.
- package/package.json +24 -24
- package/dist/clientTools/index.d.ts +0 -1
- package/dist/clientTools/index.js +0 -18
- package/dist/clientTools/index.js.map +0 -1
- package/dist/clientTools/push_certificate_management_client.d.ts +0 -176
- package/dist/clientTools/push_certificate_management_client.js +0 -464
- package/dist/clientTools/push_certificate_management_client.js.map +0 -1
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -28
- package/dist/index.js.map +0 -1
- package/dist/push_certificate_manager.d.ts +0 -141
- package/dist/push_certificate_manager.js +0 -3
- package/dist/push_certificate_manager.js.map +0 -1
- package/dist/server/install_certificate_file_watcher.d.ts +0 -5
- package/dist/server/install_certificate_file_watcher.js +0 -24
- package/dist/server/install_certificate_file_watcher.js.map +0 -1
- package/dist/server/install_push_certitifate_management.d.ts +0 -19
- package/dist/server/install_push_certitifate_management.js +0 -216
- package/dist/server/install_push_certitifate_management.js.map +0 -1
- package/dist/server/promote_trust_list.d.ts +0 -6
- package/dist/server/promote_trust_list.js +0 -176
- package/dist/server/promote_trust_list.js.map +0 -1
- package/dist/server/push_certificate_manager_helpers.d.ts +0 -4
- package/dist/server/push_certificate_manager_helpers.js +0 -412
- package/dist/server/push_certificate_manager_helpers.js.map +0 -1
- package/dist/server/push_certificate_manager_server_impl.d.ts +0 -47
- package/dist/server/push_certificate_manager_server_impl.js +0 -526
- package/dist/server/push_certificate_manager_server_impl.js.map +0 -1
- package/dist/server/roles_and_permissions.d.ts +0 -3
- package/dist/server/roles_and_permissions.js +0 -39
- package/dist/server/roles_and_permissions.js.map +0 -1
- package/dist/server/tools.d.ts +0 -3
- package/dist/server/tools.js +0 -20
- package/dist/server/tools.js.map +0 -1
- package/dist/server/trust_list_server.d.ts +0 -13
- package/dist/server/trust_list_server.js +0 -90
- package/dist/server/trust_list_server.js.map +0 -1
- package/dist/standard_certificate_types.d.ts +0 -6
- package/dist/standard_certificate_types.js +0 -14
- package/dist/standard_certificate_types.js.map +0 -1
- package/dist/trust_list.d.ts +0 -79
- package/dist/trust_list.js +0 -3
- package/dist/trust_list.js.map +0 -1
- package/dist/trust_list_impl.d.ts +0 -0
- package/dist/trust_list_impl.js +0 -26
- package/dist/trust_list_impl.js.map +0 -1
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.writeTrustList = exports.buildTrustList = exports.TrustListMasks = void 0;
|
|
13
|
-
const fs = require("fs");
|
|
14
|
-
const path = require("path");
|
|
15
|
-
const node_opcua_types_1 = require("node-opcua-types");
|
|
16
|
-
const node_opcua_binary_stream_1 = require("node-opcua-binary-stream");
|
|
17
|
-
const node_opcua_crypto_1 = require("node-opcua-crypto");
|
|
18
|
-
function readAll(folder) {
|
|
19
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
-
const results = [];
|
|
21
|
-
const files = yield fs.promises.readdir(folder);
|
|
22
|
-
for (const f of files) {
|
|
23
|
-
const file = path.join(folder, f);
|
|
24
|
-
const ext = path.extname(file);
|
|
25
|
-
if (ext === ".der" || ext === ".pem") {
|
|
26
|
-
const buf = yield (0, node_opcua_crypto_1.readCertificate)(file);
|
|
27
|
-
results.push(buf);
|
|
28
|
-
}
|
|
29
|
-
else if (ext === ".crl") {
|
|
30
|
-
const buf = yield (0, node_opcua_crypto_1.readCertificateRevocationList)(file);
|
|
31
|
-
results.push(buf);
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
console.log(" unknown extesnion on file ", f);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return results;
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
var TrustListMasks;
|
|
41
|
-
(function (TrustListMasks) {
|
|
42
|
-
TrustListMasks[TrustListMasks["None"] = 0] = "None";
|
|
43
|
-
TrustListMasks[TrustListMasks["TrustedCertificates"] = 1] = "TrustedCertificates";
|
|
44
|
-
TrustListMasks[TrustListMasks["TrustedCrls"] = 2] = "TrustedCrls";
|
|
45
|
-
TrustListMasks[TrustListMasks["IssuerCertificates"] = 4] = "IssuerCertificates";
|
|
46
|
-
TrustListMasks[TrustListMasks["IssuerCrls"] = 8] = "IssuerCrls";
|
|
47
|
-
TrustListMasks[TrustListMasks["All"] = 15] = "All";
|
|
48
|
-
})(TrustListMasks = exports.TrustListMasks || (exports.TrustListMasks = {}));
|
|
49
|
-
;
|
|
50
|
-
function buildTrustList(certificateManager, trustListFlag) {
|
|
51
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
-
const trustList = new node_opcua_types_1.TrustListDataType({
|
|
53
|
-
specifiedLists: trustListFlag,
|
|
54
|
-
issuerCertificates: undefined,
|
|
55
|
-
issuerCrls: undefined,
|
|
56
|
-
trustedCertificates: undefined,
|
|
57
|
-
trustedCrls: undefined,
|
|
58
|
-
});
|
|
59
|
-
if ((trustListFlag & TrustListMasks.TrustedCertificates) === TrustListMasks.TrustedCertificates) {
|
|
60
|
-
trustList.trustedCertificates = yield readAll(certificateManager.trustedFolder);
|
|
61
|
-
}
|
|
62
|
-
if ((trustListFlag & TrustListMasks.TrustedCrls) === TrustListMasks.TrustedCrls) {
|
|
63
|
-
trustList.trustedCrls = yield readAll(certificateManager.crlFolder);
|
|
64
|
-
}
|
|
65
|
-
if ((trustListFlag & TrustListMasks.IssuerCertificates) === TrustListMasks.IssuerCertificates) {
|
|
66
|
-
trustList.issuerCertificates = yield readAll(certificateManager.issuersCertFolder);
|
|
67
|
-
}
|
|
68
|
-
if ((trustListFlag & TrustListMasks.IssuerCrls) === TrustListMasks.IssuerCrls) {
|
|
69
|
-
trustList.issuerCrls = yield readAll(certificateManager.issuersCrlFolder);
|
|
70
|
-
}
|
|
71
|
-
return trustList;
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
exports.buildTrustList = buildTrustList;
|
|
75
|
-
function writeTrustList(fs, filename, trustListFlag, certificateManager) {
|
|
76
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
-
const trustList = yield buildTrustList(certificateManager, trustListFlag);
|
|
78
|
-
const stream = new node_opcua_binary_stream_1.BinaryStream(trustList.binaryStoreSize());
|
|
79
|
-
trustList.encode(stream);
|
|
80
|
-
yield new Promise((resolve, reject) => {
|
|
81
|
-
fs.writeFile(filename, stream.buffer, "binary", (err) => {
|
|
82
|
-
if (err)
|
|
83
|
-
return reject(err);
|
|
84
|
-
resolve();
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
exports.writeTrustList = writeTrustList;
|
|
90
|
-
//# sourceMappingURL=trust_list_server.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"trust_list_server.js","sourceRoot":"","sources":["../../source/server/trust_list_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CertificateType = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* @module node-opcua-server-configuration
|
|
6
|
-
*/
|
|
7
|
-
const node_opcua_nodeid_1 = require("node-opcua-nodeid");
|
|
8
|
-
exports.CertificateType = {
|
|
9
|
-
Application: (0, node_opcua_nodeid_1.resolveNodeId)("ApplicationCertificateType"),
|
|
10
|
-
Https: (0, node_opcua_nodeid_1.resolveNodeId)("HttpsCertificateType"),
|
|
11
|
-
RsaMinApplication: (0, node_opcua_nodeid_1.resolveNodeId)("RsaMinApplicationCertificateType"),
|
|
12
|
-
RsaSha256Application: (0, node_opcua_nodeid_1.resolveNodeId)("RsaSha256ApplicationCertificateType"),
|
|
13
|
-
};
|
|
14
|
-
//# sourceMappingURL=standard_certificate_types.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"standard_certificate_types.js","sourceRoot":"","sources":["../source/standard_certificate_types.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,yDAAkD;AAErC,QAAA,eAAe,GAAI;IAC5B,WAAW,EAAE,IAAA,iCAAa,EAAC,4BAA4B,CAAC;IACxD,KAAK,EAAE,IAAA,iCAAa,EAAC,sBAAsB,CAAC;IAC5C,iBAAiB,EAAE,IAAA,iCAAa,EAAC,kCAAkC,CAAC;IACpE,oBAAoB,EAAE,IAAA,iCAAa,EAAC,qCAAqC,CAAC;CAC7E,CAAC"}
|
package/dist/trust_list.d.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { StatusCode } from "node-opcua-basic-types";
|
|
3
|
-
/**
|
|
4
|
-
* @module node-opcua-server-configuration
|
|
5
|
-
*/
|
|
6
|
-
export interface ITrustList {
|
|
7
|
-
/**
|
|
8
|
-
* The CloseAndUpdate Method closes the file and applies the changes to the Trust List. It can
|
|
9
|
-
* only be called if the file was opened for writing. If the Close Method is called any cached data
|
|
10
|
-
* is discarded and the Trust List is not changed.
|
|
11
|
-
*
|
|
12
|
-
* The Server shall verify that every Certificate in the new Trust List is valid according to the
|
|
13
|
-
* mandatory rules defined in Part 4. If an invalid Certificate is found the Server shall return an
|
|
14
|
-
* error and shall not update the Trust List. If only part of the Trust List is being updated the
|
|
15
|
-
* Server creates a temporary Trust List that includes the existing Trust List plus any updates
|
|
16
|
-
* and validates the temporary Trust List.
|
|
17
|
-
*
|
|
18
|
-
* If the file cannot be processed this Method still closes the file and discards the data before
|
|
19
|
-
* returning an error. This Method is required if the Server supports updates to the Trust List.
|
|
20
|
-
* The structure uploaded includes a mask (see 7.5.8) which specifies which fields are updated.
|
|
21
|
-
* If a bit is not set then the associated field is not changed.
|
|
22
|
-
*
|
|
23
|
-
* @param fileHandle UInt32 - The handle of the previously opened file
|
|
24
|
-
* @return applyChangesRequired - A flag indicating whether the ApplyChanges Method (see 7.7.5) shall be called
|
|
25
|
-
* before the new Trust List will be used by the Server.
|
|
26
|
-
* **Result Code**
|
|
27
|
-
* - BadUserAccessDenied The current user does not have the rights required.
|
|
28
|
-
* - BadCertificateInvalid The Server could not validate all Certificates in the Trust List.
|
|
29
|
-
* The DiagnosticInfo shall specify which Certificate(s) are invalid and the specific
|
|
30
|
-
* error.
|
|
31
|
-
*/
|
|
32
|
-
closeAndUpdate(applyChangesRequired: boolean): Promise<boolean>;
|
|
33
|
-
/**
|
|
34
|
-
* The AddCertificate Method allows a Client to add a single Certificate to the Trust List.
|
|
35
|
-
*
|
|
36
|
-
* The Server shall verify that the Certificate is valid according to the rules defined in Part 4.
|
|
37
|
-
*
|
|
38
|
-
* If an invalid Certificate is found the Server shall return an error and shall not update the Trust List.
|
|
39
|
-
*
|
|
40
|
-
* If the Certificate is issued by a CA then the Client shall provide the entire chain in the
|
|
41
|
-
* certificate argument (see Part 6).
|
|
42
|
-
*
|
|
43
|
-
* After validating the Certificate, the Server shall add the CA Certificates to the Issuers list in the Trust List.
|
|
44
|
-
*
|
|
45
|
-
* The leaf Certificate is added to the list specified by the isTrustedCertificate argument.
|
|
46
|
-
*
|
|
47
|
-
* This method cannot be called if the file object is open
|
|
48
|
-
* @param certificate - The DER encoded Certificate to add as a ByteStrng
|
|
49
|
-
* @param isTrustedCertificate - If TRUE the Certificate is added to the Trusted Certificates List. If FALSE the Certificate is added to the Issuer Certificates List.
|
|
50
|
-
*
|
|
51
|
-
* **Result Code**
|
|
52
|
-
* - BadUserAccessDenied: The current user does not have the rights required.
|
|
53
|
-
* - BadCertificateInvalid: The certificate to add is invalid.
|
|
54
|
-
* - BadInvalidState: The object is opened.
|
|
55
|
-
*
|
|
56
|
-
*/
|
|
57
|
-
addCertificate(certificate: Buffer, isTrustedCertificate: boolean): Promise<StatusCode>;
|
|
58
|
-
/**
|
|
59
|
-
* The RemoveCertificate Method allows a Client to remove a single Certificate from the Trust List.
|
|
60
|
-
*
|
|
61
|
-
* It returns BadInvalidArgument if the thumbprint does not match a Certificate in the Trust List.
|
|
62
|
-
*
|
|
63
|
-
* If the Certificate is a CA Certificate with associated CRLs then all CRLs are removed as well.
|
|
64
|
-
*
|
|
65
|
-
* This method cannot be called if the file object is open.
|
|
66
|
-
*
|
|
67
|
-
* @param thumbprint - The SHA1 hash of the Certificate to remove
|
|
68
|
-
* @param isTrustedCertificate - If TRUE the Certificate is removed from the Trusted Certificates List.
|
|
69
|
-
* If FALSE the Certificate is removed from the Issuer Certificates List.
|
|
70
|
-
*
|
|
71
|
-
* **Result Code**
|
|
72
|
-
* -BadUserAccessDenied: The current user does not have the rights required.
|
|
73
|
-
* -BadInvalidArgument: The certificate to remove was not found.
|
|
74
|
-
* -BadInvalidState: The object is opened.
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*/
|
|
78
|
-
removeCertificate(thumbprint: string, isTrustedCertificate: boolean): Promise<StatusCode>;
|
|
79
|
-
}
|
package/dist/trust_list.js
DELETED
package/dist/trust_list.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"trust_list.js","sourceRoot":"","sources":["../source/trust_list.ts"],"names":[],"mappings":""}
|
|
File without changes
|
package/dist/trust_list_impl.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// /**
|
|
3
|
-
// * @module node-opcua-server-configuration
|
|
4
|
-
// */
|
|
5
|
-
// import { StatusCode, StatusCodes } from "node-opcua-status-code";
|
|
6
|
-
// import { ITrustList } from "./trust_list";
|
|
7
|
-
// export class TrustList implements ITrustList {
|
|
8
|
-
// public async closeAndUpdate(
|
|
9
|
-
// applyChangesRequired: boolean
|
|
10
|
-
// ): Promise<boolean> {
|
|
11
|
-
// return false;
|
|
12
|
-
// }
|
|
13
|
-
// public async addCertificate(
|
|
14
|
-
// certificate: Buffer,
|
|
15
|
-
// isTrustedCertificate: boolean
|
|
16
|
-
// ): Promise<StatusCode> {
|
|
17
|
-
// return StatusCodes.BadNotImplemented;
|
|
18
|
-
// }
|
|
19
|
-
// public async removeCertificate(
|
|
20
|
-
// thumbprint: string,
|
|
21
|
-
// isTrustedCertificate: boolean
|
|
22
|
-
// ): Promise<StatusCode> {
|
|
23
|
-
// return StatusCodes.BadNotImplemented;
|
|
24
|
-
// }
|
|
25
|
-
// }
|
|
26
|
-
//# sourceMappingURL=trust_list_impl.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"trust_list_impl.js","sourceRoot":"","sources":["../source/trust_list_impl.ts"],"names":[],"mappings":";AAAA,MAAM;AACN,6CAA6C;AAC7C,MAAM;AACN,oEAAoE;AACpE,6CAA6C;AAE7C,iDAAiD;AAEjD,iCAAiC;AACjC,oCAAoC;AACpC,0BAA0B;AAE1B,oBAAoB;AACpB,MAAM;AAEN,iCAAiC;AACjC,2BAA2B;AAC3B,oCAAoC;AACpC,6BAA6B;AAC7B,4CAA4C;AAC5C,MAAM;AAEN,oCAAoC;AACpC,0BAA0B;AAC1B,oCAAoC;AACpC,6BAA6B;AAC7B,4CAA4C;AAC5C,MAAM;AACN,IAAI"}
|