@push.rocks/smartacme 4.0.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/LICENSE +19 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -0
- package/dist_ts/interfaces/accountdata.d.ts +13 -0
- package/dist_ts/interfaces/accountdata.js +2 -0
- package/dist_ts/interfaces/index.d.ts +1 -0
- package/dist_ts/interfaces/index.js +2 -0
- package/dist_ts/smartacme.classes.cert.d.ts +14 -0
- package/dist_ts/smartacme.classes.cert.js +74 -0
- package/dist_ts/smartacme.classes.certmanager.d.ts +25 -0
- package/dist_ts/smartacme.classes.certmanager.js +53 -0
- package/dist_ts/smartacme.classes.certmatcher.d.ts +11 -0
- package/dist_ts/smartacme.classes.certmatcher.js +19 -0
- package/dist_ts/smartacme.classes.smartacme.d.ts +58 -0
- package/dist_ts/smartacme.classes.smartacme.js +164 -0
- package/dist_ts/smartacme.plugins.d.ts +16 -0
- package/dist_ts/smartacme.plugins.js +20 -0
- package/npmextra.json +18 -0
- package/package.json +67 -0
- package/readme.md +66 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/index.ts +1 -0
- package/ts/interfaces/accountdata.ts +8 -0
- package/ts/interfaces/index.ts +1 -0
- package/ts/smartacme.classes.cert.ts +62 -0
- package/ts/smartacme.classes.certmanager.ts +77 -0
- package/ts/smartacme.classes.certmatcher.ts +19 -0
- package/ts/smartacme.classes.smartacme.ts +212 -0
- package/ts/smartacme.plugins.ts +36 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (C) 2016, Lossless GmbH
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
8
|
+
so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* autocreated commitinfo by @pushrocks/commitinfo
|
|
3
|
+
*/
|
|
4
|
+
export const commitinfo = {
|
|
5
|
+
name: '@pushrocks/smartacme',
|
|
6
|
+
version: '4.0.3',
|
|
7
|
+
description: 'acme with an easy yet powerful interface in TypeScript'
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxzQkFBc0I7SUFDNUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHdEQUF3RDtDQUN0RSxDQUFBIn0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './smartacme.classes.smartacme.js';
|
package/dist_ts/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './accountdata.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as plugins from './smartacme.plugins.js';
|
|
2
|
+
export declare class Cert extends plugins.smartdata.SmartDataDbDoc<Cert, plugins.tsclass.network.ICert> implements plugins.tsclass.network.ICert {
|
|
3
|
+
id: string;
|
|
4
|
+
domainName: string;
|
|
5
|
+
created: number;
|
|
6
|
+
privateKey: string;
|
|
7
|
+
publicKey: string;
|
|
8
|
+
csr: string;
|
|
9
|
+
validUntil: number;
|
|
10
|
+
isStillValid(): boolean;
|
|
11
|
+
shouldBeRenewed(): boolean;
|
|
12
|
+
update(certDataArg: plugins.tsclass.network.ICert): void;
|
|
13
|
+
constructor(optionsArg: plugins.tsclass.network.ICert);
|
|
14
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import * as plugins from './smartacme.plugins.js';
|
|
11
|
+
import './interfaces/index.js';
|
|
12
|
+
import { CertManager } from './smartacme.classes.certmanager.js';
|
|
13
|
+
import { svDb, unI } from '@pushrocks/smartdata';
|
|
14
|
+
let Cert = class Cert extends plugins.smartdata.SmartDataDbDoc {
|
|
15
|
+
isStillValid() {
|
|
16
|
+
return this.validUntil >= Date.now();
|
|
17
|
+
}
|
|
18
|
+
shouldBeRenewed() {
|
|
19
|
+
const shouldBeValidAtLeastUntil = Date.now() +
|
|
20
|
+
plugins.smarttime.getMilliSecondsFromUnits({
|
|
21
|
+
days: 10,
|
|
22
|
+
});
|
|
23
|
+
return !(this.validUntil >= shouldBeValidAtLeastUntil);
|
|
24
|
+
}
|
|
25
|
+
update(certDataArg) {
|
|
26
|
+
Object.keys(certDataArg).forEach((key) => {
|
|
27
|
+
this[key] = certDataArg[key];
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
constructor(optionsArg) {
|
|
31
|
+
super();
|
|
32
|
+
if (optionsArg) {
|
|
33
|
+
Object.keys(optionsArg).forEach((key) => {
|
|
34
|
+
this[key] = optionsArg[key];
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
__decorate([
|
|
40
|
+
unI(),
|
|
41
|
+
__metadata("design:type", String)
|
|
42
|
+
], Cert.prototype, "id", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
svDb(),
|
|
45
|
+
__metadata("design:type", String)
|
|
46
|
+
], Cert.prototype, "domainName", void 0);
|
|
47
|
+
__decorate([
|
|
48
|
+
svDb(),
|
|
49
|
+
__metadata("design:type", Number)
|
|
50
|
+
], Cert.prototype, "created", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
svDb(),
|
|
53
|
+
__metadata("design:type", String)
|
|
54
|
+
], Cert.prototype, "privateKey", void 0);
|
|
55
|
+
__decorate([
|
|
56
|
+
svDb(),
|
|
57
|
+
__metadata("design:type", String)
|
|
58
|
+
], Cert.prototype, "publicKey", void 0);
|
|
59
|
+
__decorate([
|
|
60
|
+
svDb(),
|
|
61
|
+
__metadata("design:type", String)
|
|
62
|
+
], Cert.prototype, "csr", void 0);
|
|
63
|
+
__decorate([
|
|
64
|
+
svDb(),
|
|
65
|
+
__metadata("design:type", Number)
|
|
66
|
+
], Cert.prototype, "validUntil", void 0);
|
|
67
|
+
Cert = __decorate([
|
|
68
|
+
plugins.smartdata.Collection(() => {
|
|
69
|
+
return CertManager.activeDB;
|
|
70
|
+
}),
|
|
71
|
+
__metadata("design:paramtypes", [Object])
|
|
72
|
+
], Cert);
|
|
73
|
+
export { Cert };
|
|
74
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuY2VydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0YWNtZS5jbGFzc2VzLmNlcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSx3QkFBd0IsQ0FBQztBQUVsRCxPQUE0Qix1QkFBdUIsQ0FBQztBQUVwRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFFakUsT0FBTyxFQUFjLElBQUksRUFBRSxHQUFHLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUt0RCxJQUFNLElBQUksR0FBVixNQUFNLElBQUssU0FBUSxPQUFPLENBQUMsU0FBUyxDQUFDLGNBQW1EO0lBdUJ0RixZQUFZO1FBQ2pCLE9BQU8sSUFBSSxDQUFDLFVBQVUsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDdkMsQ0FBQztJQUVNLGVBQWU7UUFDcEIsTUFBTSx5QkFBeUIsR0FDN0IsSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUNWLE9BQU8sQ0FBQyxTQUFTLENBQUMsd0JBQXdCLENBQUM7Z0JBQ3pDLElBQUksRUFBRSxFQUFFO2FBQ1QsQ0FBQyxDQUFDO1FBQ0wsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLFVBQVUsSUFBSSx5QkFBeUIsQ0FBQyxDQUFDO0lBQ3pELENBQUM7SUFFTSxNQUFNLENBQUMsV0FBMEM7UUFDdEQsTUFBTSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRTtZQUN2QyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQy9CLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELFlBQVksVUFBeUM7UUFDbkQsS0FBSyxFQUFFLENBQUM7UUFDUixJQUFJLFVBQVUsRUFBRTtZQUNkLE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUU7Z0JBQ3RDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDOUIsQ0FBQyxDQUFDLENBQUM7U0FDSjtJQUNILENBQUM7Q0FDRixDQUFBO0FBaERDO0lBQUMsR0FBRyxFQUFFOztnQ0FDWTtBQUVsQjtJQUFDLElBQUksRUFBRTs7d0NBQ21CO0FBRTFCO0lBQUMsSUFBSSxFQUFFOztxQ0FDZ0I7QUFFdkI7SUFBQyxJQUFJLEVBQUU7O3dDQUNtQjtBQUUxQjtJQUFDLElBQUksRUFBRTs7dUNBQ2tCO0FBRXpCO0lBQUMsSUFBSSxFQUFFOztpQ0FDWTtBQUVuQjtJQUFDLElBQUksRUFBRTs7d0NBQ21CO0FBckJmLElBQUk7SUFIaEIsT0FBTyxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsR0FBRyxFQUFFO1FBQ2pDLE9BQU8sV0FBVyxDQUFDLFFBQVEsQ0FBQztJQUM5QixDQUFDLENBQUM7O0dBQ1csSUFBSSxDQWtEaEI7U0FsRFksSUFBSSJ9
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as plugins from './smartacme.plugins.js';
|
|
2
|
+
import { Cert } from './smartacme.classes.cert.js';
|
|
3
|
+
import { SmartAcme } from './smartacme.classes.smartacme.js';
|
|
4
|
+
export declare class CertManager {
|
|
5
|
+
static activeDB: plugins.smartdata.SmartdataDb;
|
|
6
|
+
private mongoDescriptor;
|
|
7
|
+
smartdataDb: plugins.smartdata.SmartdataDb;
|
|
8
|
+
interestMap: plugins.lik.InterestMap<string, Cert>;
|
|
9
|
+
constructor(smartAcmeArg: SmartAcme, optionsArg: {
|
|
10
|
+
mongoDescriptor: plugins.smartdata.IMongoDescriptor;
|
|
11
|
+
});
|
|
12
|
+
init(): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* retrieves a certificate
|
|
15
|
+
* @returns the Cert class or null
|
|
16
|
+
* @param certDomainNameArg the domain Name to retrieve the vcertificate for
|
|
17
|
+
*/
|
|
18
|
+
retrieveCertificate(certDomainNameArg: string): Promise<Cert>;
|
|
19
|
+
/**
|
|
20
|
+
* stores the certificate
|
|
21
|
+
* @param optionsArg
|
|
22
|
+
*/
|
|
23
|
+
storeCertificate(optionsArg: plugins.tsclass.network.ICert): Promise<void>;
|
|
24
|
+
deleteCertificate(certDomainNameArg: string): Promise<void>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as plugins from './smartacme.plugins.js';
|
|
2
|
+
import { Cert } from './smartacme.classes.cert.js';
|
|
3
|
+
import './smartacme.classes.smartacme.js';
|
|
4
|
+
import './interfaces/index.js';
|
|
5
|
+
export class CertManager {
|
|
6
|
+
constructor(smartAcmeArg, optionsArg) {
|
|
7
|
+
this.mongoDescriptor = optionsArg.mongoDescriptor;
|
|
8
|
+
}
|
|
9
|
+
async init() {
|
|
10
|
+
// Smartdata DB
|
|
11
|
+
this.smartdataDb = new plugins.smartdata.SmartdataDb(this.mongoDescriptor);
|
|
12
|
+
await this.smartdataDb.init();
|
|
13
|
+
CertManager.activeDB = this.smartdataDb;
|
|
14
|
+
// Pending Map
|
|
15
|
+
this.interestMap = new plugins.lik.InterestMap((certName) => certName);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* retrieves a certificate
|
|
19
|
+
* @returns the Cert class or null
|
|
20
|
+
* @param certDomainNameArg the domain Name to retrieve the vcertificate for
|
|
21
|
+
*/
|
|
22
|
+
async retrieveCertificate(certDomainNameArg) {
|
|
23
|
+
const existingCertificate = await Cert.getInstance({
|
|
24
|
+
domainName: certDomainNameArg,
|
|
25
|
+
});
|
|
26
|
+
if (existingCertificate) {
|
|
27
|
+
return existingCertificate;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* stores the certificate
|
|
35
|
+
* @param optionsArg
|
|
36
|
+
*/
|
|
37
|
+
async storeCertificate(optionsArg) {
|
|
38
|
+
const cert = new Cert(optionsArg);
|
|
39
|
+
await cert.save();
|
|
40
|
+
const interest = this.interestMap.findInterest(cert.domainName);
|
|
41
|
+
if (interest) {
|
|
42
|
+
interest.fullfillInterest(cert);
|
|
43
|
+
interest.markLost();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async deleteCertificate(certDomainNameArg) {
|
|
47
|
+
const cert = await Cert.getInstance({
|
|
48
|
+
domainName: certDomainNameArg,
|
|
49
|
+
});
|
|
50
|
+
await cert.delete();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuY2VydG1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGFjbWUuY2xhc3Nlcy5jZXJ0bWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHdCQUF3QixDQUFDO0FBQ2xELE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUNuRCxPQUEwQixrQ0FBa0MsQ0FBQztBQUU3RCxPQUE0Qix1QkFBdUIsQ0FBQztBQUVwRCxNQUFNLE9BQU8sV0FBVztJQWN0QixZQUNFLFlBQXVCLEVBQ3ZCLFVBRUM7UUFFRCxJQUFJLENBQUMsZUFBZSxHQUFHLFVBQVUsQ0FBQyxlQUFlLENBQUM7SUFDcEQsQ0FBQztJQUVNLEtBQUssQ0FBQyxJQUFJO1FBQ2YsZUFBZTtRQUNmLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxPQUFPLENBQUMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDM0UsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzlCLFdBQVcsQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQztRQUV4QyxjQUFjO1FBQ2QsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUN6RSxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxpQkFBeUI7UUFDeEQsTUFBTSxtQkFBbUIsR0FBUyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQU87WUFDN0QsVUFBVSxFQUFFLGlCQUFpQjtTQUM5QixDQUFDLENBQUM7UUFFSCxJQUFJLG1CQUFtQixFQUFFO1lBQ3ZCLE9BQU8sbUJBQW1CLENBQUM7U0FDNUI7YUFBTTtZQUNMLE9BQU8sSUFBSSxDQUFDO1NBQ2I7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLGdCQUFnQixDQUFDLFVBQXlDO1FBQ3JFLE1BQU0sSUFBSSxHQUFHLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ2xDLE1BQU0sSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ2xCLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNoRSxJQUFJLFFBQVEsRUFBRTtZQUNaLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNoQyxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUM7U0FDckI7SUFDSCxDQUFDO0lBRU0sS0FBSyxDQUFDLGlCQUFpQixDQUFDLGlCQUF5QjtRQUN0RCxNQUFNLElBQUksR0FBUyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQU87WUFDOUMsVUFBVSxFQUFFLGlCQUFpQjtTQUM5QixDQUFDLENBQUM7UUFDSCxNQUFNLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUN0QixDQUFDO0NBQ0YifQ==
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* certmatcher is responsible for matching certificates
|
|
3
|
+
*/
|
|
4
|
+
export declare class CertMatcher {
|
|
5
|
+
/**
|
|
6
|
+
* creates a domainName for the certificate that will include the broadest scope
|
|
7
|
+
* for wild card certificates
|
|
8
|
+
* @param domainNameArg the domainNameArg to create the scope from
|
|
9
|
+
*/
|
|
10
|
+
getCertificateDomainNameByDomainName(domainNameArg: string): string;
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as plugins from './smartacme.plugins.js';
|
|
2
|
+
import './interfaces/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* certmatcher is responsible for matching certificates
|
|
5
|
+
*/
|
|
6
|
+
export class CertMatcher {
|
|
7
|
+
/**
|
|
8
|
+
* creates a domainName for the certificate that will include the broadest scope
|
|
9
|
+
* for wild card certificates
|
|
10
|
+
* @param domainNameArg the domainNameArg to create the scope from
|
|
11
|
+
*/
|
|
12
|
+
getCertificateDomainNameByDomainName(domainNameArg) {
|
|
13
|
+
const originalDomain = new plugins.smartstring.Domain(domainNameArg);
|
|
14
|
+
if (!originalDomain.level4) {
|
|
15
|
+
return `${originalDomain.level2}.${originalDomain.level1}`;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuY2VydG1hdGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGFjbWUuY2xhc3Nlcy5jZXJ0bWF0Y2hlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHdCQUF3QixDQUFDO0FBQ2xELE9BQTRCLHVCQUF1QixDQUFDO0FBRXBEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLFdBQVc7SUFDdEI7Ozs7T0FJRztJQUNJLG9DQUFvQyxDQUFDLGFBQXFCO1FBQy9ELE1BQU0sY0FBYyxHQUFHLElBQUksT0FBTyxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDckUsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUU7WUFDMUIsT0FBTyxHQUFHLGNBQWMsQ0FBQyxNQUFNLElBQUksY0FBYyxDQUFDLE1BQU0sRUFBRSxDQUFDO1NBQzVEO0lBQ0gsQ0FBQztDQUNGIn0=
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as plugins from './smartacme.plugins.js';
|
|
2
|
+
import { Cert } from './smartacme.classes.cert.js';
|
|
3
|
+
/**
|
|
4
|
+
* the options for the class @see SmartAcme
|
|
5
|
+
*/
|
|
6
|
+
export interface ISmartAcmeOptions {
|
|
7
|
+
accountPrivateKey?: string;
|
|
8
|
+
accountEmail: string;
|
|
9
|
+
mongoDescriptor: plugins.smartdata.IMongoDescriptor;
|
|
10
|
+
setChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise<any>;
|
|
11
|
+
removeChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise<any>;
|
|
12
|
+
environment: 'production' | 'integration';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* class SmartAcme
|
|
16
|
+
* can be used for setting up communication with an ACME authority
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* const mySmartAcmeInstance = new SmartAcme({
|
|
20
|
+
* // see ISmartAcmeOptions for options
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare class SmartAcme {
|
|
25
|
+
private options;
|
|
26
|
+
private client;
|
|
27
|
+
private smartdns;
|
|
28
|
+
logger: plugins.smartlog.ConsoleLog;
|
|
29
|
+
private privateKey;
|
|
30
|
+
private setChallenge;
|
|
31
|
+
private removeChallenge;
|
|
32
|
+
private certmanager;
|
|
33
|
+
private certmatcher;
|
|
34
|
+
constructor(optionsArg: ISmartAcmeOptions);
|
|
35
|
+
/**
|
|
36
|
+
* inits the instance
|
|
37
|
+
* ```ts
|
|
38
|
+
* await myCloudlyInstance.init() // does not support options
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
init(): Promise<void>;
|
|
42
|
+
stop(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* gets a certificate
|
|
45
|
+
* it runs through the following steps
|
|
46
|
+
*
|
|
47
|
+
* * look in the database
|
|
48
|
+
* * if in the database and still valid return it
|
|
49
|
+
* * if not in the database announce it
|
|
50
|
+
* * then get it from letsencrypt
|
|
51
|
+
* * store it
|
|
52
|
+
* * remove it from the pending map (which it go onto by announcing it)
|
|
53
|
+
* * retrieve it from the databse and return it
|
|
54
|
+
*
|
|
55
|
+
* @param domainArg
|
|
56
|
+
*/
|
|
57
|
+
getCertificateForDomain(domainArg: string): Promise<Cert>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import * as plugins from './smartacme.plugins.js';
|
|
2
|
+
import './smartacme.classes.cert.js';
|
|
3
|
+
import { CertManager } from './smartacme.classes.certmanager.js';
|
|
4
|
+
import { CertMatcher } from './smartacme.classes.certmatcher.js';
|
|
5
|
+
/**
|
|
6
|
+
* class SmartAcme
|
|
7
|
+
* can be used for setting up communication with an ACME authority
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const mySmartAcmeInstance = new SmartAcme({
|
|
11
|
+
* // see ISmartAcmeOptions for options
|
|
12
|
+
* })
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export class SmartAcme {
|
|
16
|
+
constructor(optionsArg) {
|
|
17
|
+
this.smartdns = new plugins.smartdns.Smartdns({});
|
|
18
|
+
this.options = optionsArg;
|
|
19
|
+
this.logger = new plugins.smartlog.ConsoleLog();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* inits the instance
|
|
23
|
+
* ```ts
|
|
24
|
+
* await myCloudlyInstance.init() // does not support options
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
async init() {
|
|
28
|
+
this.privateKey =
|
|
29
|
+
this.options.accountPrivateKey || (await plugins.acme.forge.createPrivateKey()).toString();
|
|
30
|
+
this.setChallenge = this.options.setChallenge;
|
|
31
|
+
this.removeChallenge = this.options.removeChallenge;
|
|
32
|
+
// CertMangaer
|
|
33
|
+
this.certmanager = new CertManager(this, {
|
|
34
|
+
mongoDescriptor: this.options.mongoDescriptor,
|
|
35
|
+
});
|
|
36
|
+
await this.certmanager.init();
|
|
37
|
+
// CertMatcher
|
|
38
|
+
this.certmatcher = new CertMatcher();
|
|
39
|
+
// ACME Client
|
|
40
|
+
this.client = new plugins.acme.Client({
|
|
41
|
+
directoryUrl: (() => {
|
|
42
|
+
if (this.options.environment === 'production') {
|
|
43
|
+
return plugins.acme.directory.letsencrypt.production;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
return plugins.acme.directory.letsencrypt.staging;
|
|
47
|
+
}
|
|
48
|
+
})(),
|
|
49
|
+
accountKey: this.privateKey,
|
|
50
|
+
});
|
|
51
|
+
/* Register account */
|
|
52
|
+
await this.client.createAccount({
|
|
53
|
+
termsOfServiceAgreed: true,
|
|
54
|
+
contact: [`mailto:${this.options.accountEmail}`],
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async stop() {
|
|
58
|
+
await this.certmanager.smartdataDb.close();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* gets a certificate
|
|
62
|
+
* it runs through the following steps
|
|
63
|
+
*
|
|
64
|
+
* * look in the database
|
|
65
|
+
* * if in the database and still valid return it
|
|
66
|
+
* * if not in the database announce it
|
|
67
|
+
* * then get it from letsencrypt
|
|
68
|
+
* * store it
|
|
69
|
+
* * remove it from the pending map (which it go onto by announcing it)
|
|
70
|
+
* * retrieve it from the databse and return it
|
|
71
|
+
*
|
|
72
|
+
* @param domainArg
|
|
73
|
+
*/
|
|
74
|
+
async getCertificateForDomain(domainArg) {
|
|
75
|
+
const certDomainName = this.certmatcher.getCertificateDomainNameByDomainName(domainArg);
|
|
76
|
+
const retrievedCertificate = await this.certmanager.retrieveCertificate(certDomainName);
|
|
77
|
+
if (!retrievedCertificate &&
|
|
78
|
+
(await this.certmanager.interestMap.checkInterest(certDomainName))) {
|
|
79
|
+
const existingCertificateInterest = this.certmanager.interestMap.findInterest(certDomainName);
|
|
80
|
+
const certificate = existingCertificateInterest.interestFullfilled;
|
|
81
|
+
return certificate;
|
|
82
|
+
}
|
|
83
|
+
else if (retrievedCertificate && !retrievedCertificate.shouldBeRenewed()) {
|
|
84
|
+
return retrievedCertificate;
|
|
85
|
+
}
|
|
86
|
+
else if (retrievedCertificate && retrievedCertificate.shouldBeRenewed()) {
|
|
87
|
+
await retrievedCertificate.delete();
|
|
88
|
+
}
|
|
89
|
+
// lets make sure others get the same interest
|
|
90
|
+
const currentDomainInterst = await this.certmanager.interestMap.addInterest(certDomainName);
|
|
91
|
+
/* Place new order */
|
|
92
|
+
const order = await this.client.createOrder({
|
|
93
|
+
identifiers: [
|
|
94
|
+
{ type: 'dns', value: certDomainName },
|
|
95
|
+
{ type: 'dns', value: `*.${certDomainName}` },
|
|
96
|
+
],
|
|
97
|
+
});
|
|
98
|
+
/* Get authorizations and select challenges */
|
|
99
|
+
const authorizations = await this.client.getAuthorizations(order);
|
|
100
|
+
for (const authz of authorizations) {
|
|
101
|
+
console.log(authz);
|
|
102
|
+
const fullHostName = `_acme-challenge.${authz.identifier.value}`;
|
|
103
|
+
const dnsChallenge = authz.challenges.find((challengeArg) => {
|
|
104
|
+
return challengeArg.type === 'dns-01';
|
|
105
|
+
});
|
|
106
|
+
// process.exit(1);
|
|
107
|
+
const keyAuthorization = await this.client.getChallengeKeyAuthorization(dnsChallenge);
|
|
108
|
+
try {
|
|
109
|
+
/* Satisfy challenge */
|
|
110
|
+
await this.setChallenge({
|
|
111
|
+
hostName: fullHostName,
|
|
112
|
+
challenge: keyAuthorization,
|
|
113
|
+
});
|
|
114
|
+
await plugins.smartdelay.delayFor(30000);
|
|
115
|
+
await this.smartdns.checkUntilAvailable(fullHostName, 'TXT', keyAuthorization, 100, 5000);
|
|
116
|
+
console.log('Cool down an extra 60 second for region availability');
|
|
117
|
+
await plugins.smartdelay.delayFor(60000);
|
|
118
|
+
/* Verify that challenge is satisfied */
|
|
119
|
+
await this.client.verifyChallenge(authz, dnsChallenge);
|
|
120
|
+
/* Notify ACME provider that challenge is satisfied */
|
|
121
|
+
await this.client.completeChallenge(dnsChallenge);
|
|
122
|
+
/* Wait for ACME provider to respond with valid status */
|
|
123
|
+
await this.client.waitForValidStatus(dnsChallenge);
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
/* Clean up challenge response */
|
|
127
|
+
try {
|
|
128
|
+
await this.removeChallenge({
|
|
129
|
+
hostName: fullHostName,
|
|
130
|
+
challenge: keyAuthorization,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
console.log(e);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/* Finalize order */
|
|
139
|
+
const [key, csr] = await plugins.acme.forge.createCsr({
|
|
140
|
+
commonName: `*.${certDomainName}`,
|
|
141
|
+
altNames: [certDomainName],
|
|
142
|
+
});
|
|
143
|
+
await this.client.finalizeOrder(order, csr);
|
|
144
|
+
const cert = await this.client.getCertificate(order);
|
|
145
|
+
/* Done */
|
|
146
|
+
await this.certmanager.storeCertificate({
|
|
147
|
+
id: plugins.smartunique.shortId(),
|
|
148
|
+
domainName: certDomainName,
|
|
149
|
+
privateKey: key.toString(),
|
|
150
|
+
publicKey: cert.toString(),
|
|
151
|
+
csr: csr.toString(),
|
|
152
|
+
created: Date.now(),
|
|
153
|
+
validUntil: Date.now() +
|
|
154
|
+
plugins.smarttime.getMilliSecondsFromUnits({
|
|
155
|
+
days: 90,
|
|
156
|
+
}),
|
|
157
|
+
});
|
|
158
|
+
const newCertificate = await this.certmanager.retrieveCertificate(certDomainName);
|
|
159
|
+
currentDomainInterst.fullfillInterest(newCertificate);
|
|
160
|
+
currentDomainInterst.destroy();
|
|
161
|
+
return newCertificate;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sd0JBQXdCLENBQUM7QUFDbEQsT0FBcUIsNkJBQTZCLENBQUM7QUFDbkQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQWNqRTs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLE9BQU8sU0FBUztJQW1CcEIsWUFBWSxVQUE2QjtRQWRqQyxhQUFRLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQWVuRCxJQUFJLENBQUMsT0FBTyxHQUFHLFVBQVUsQ0FBQztRQUMxQixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUNsRCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxLQUFLLENBQUMsSUFBSTtRQUNmLElBQUksQ0FBQyxVQUFVO1lBQ2IsSUFBSSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsSUFBSSxDQUFDLE1BQU0sT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQzdGLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUM7UUFDOUMsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQztRQUVwRCxjQUFjO1FBQ2QsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLFdBQVcsQ0FBQyxJQUFJLEVBQUU7WUFDdkMsZUFBZSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsZUFBZTtTQUM5QyxDQUFDLENBQUM7UUFDSCxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxFQUFFLENBQUM7UUFFOUIsY0FBYztRQUNkLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxXQUFXLEVBQUUsQ0FBQztRQUVyQyxjQUFjO1FBQ2QsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO1lBQ3BDLFlBQVksRUFBRSxDQUFDLEdBQUcsRUFBRTtnQkFDbEIsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsS0FBSyxZQUFZLEVBQUU7b0JBQzdDLE9BQU8sT0FBTyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsV0FBVyxDQUFDLFVBQVUsQ0FBQztpQkFDdEQ7cUJBQU07b0JBQ0wsT0FBTyxPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDO2lCQUNuRDtZQUNILENBQUMsQ0FBQyxFQUFFO1lBQ0osVUFBVSxFQUFFLElBQUksQ0FBQyxVQUFVO1NBQzVCLENBQUMsQ0FBQztRQUVILHNCQUFzQjtRQUN0QixNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDO1lBQzlCLG9CQUFvQixFQUFFLElBQUk7WUFDMUIsT0FBTyxFQUFFLENBQUMsVUFBVSxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVksRUFBRSxDQUFDO1NBQ2pELENBQUMsQ0FBQztJQUNMLENBQUM7SUFFTSxLQUFLLENBQUMsSUFBSTtRQUNmLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDN0MsQ0FBQztJQUVEOzs7Ozs7Ozs7Ozs7O09BYUc7SUFDSSxLQUFLLENBQUMsdUJBQXVCLENBQUMsU0FBaUI7UUFDcEQsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxvQ0FBb0MsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN4RixNQUFNLG9CQUFvQixHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxtQkFBbUIsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUV4RixJQUNFLENBQUMsb0JBQW9CO1lBQ3JCLENBQUMsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsY0FBYyxDQUFDLENBQUMsRUFDbEU7WUFDQSxNQUFNLDJCQUEyQixHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDLFlBQVksQ0FBQyxjQUFjLENBQUMsQ0FBQztZQUM5RixNQUFNLFdBQVcsR0FBRywyQkFBMkIsQ0FBQyxrQkFBa0IsQ0FBQztZQUNuRSxPQUFPLFdBQVcsQ0FBQztTQUNwQjthQUFNLElBQUksb0JBQW9CLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxlQUFlLEVBQUUsRUFBRTtZQUMxRSxPQUFPLG9CQUFvQixDQUFDO1NBQzdCO2FBQU0sSUFBSSxvQkFBb0IsSUFBSSxvQkFBb0IsQ0FBQyxlQUFlLEVBQUUsRUFBRTtZQUN6RSxNQUFNLG9CQUFvQixDQUFDLE1BQU0sRUFBRSxDQUFDO1NBQ3JDO1FBRUQsOENBQThDO1FBQzlDLE1BQU0sb0JBQW9CLEdBQUcsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLFdBQVcsQ0FBQyxXQUFXLENBQUMsY0FBYyxDQUFDLENBQUM7UUFFNUYscUJBQXFCO1FBQ3JCLE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUM7WUFDMUMsV0FBVyxFQUFFO2dCQUNYLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsY0FBYyxFQUFFO2dCQUN0QyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssY0FBYyxFQUFFLEVBQUU7YUFDOUM7U0FDRixDQUFDLENBQUM7UUFFSCw4Q0FBOEM7UUFDOUMsTUFBTSxjQUFjLEdBQUcsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLGlCQUFpQixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBRWxFLEtBQUssTUFBTSxLQUFLLElBQUksY0FBYyxFQUFFO1lBQ2xDLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDbkIsTUFBTSxZQUFZLEdBQVcsbUJBQW1CLEtBQUssQ0FBQyxVQUFVLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDekUsTUFBTSxZQUFZLEdBQVcsS0FBSyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxZQUFZLEVBQUUsRUFBRTtnQkFDbEUsT0FBTyxZQUFZLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQztZQUN4QyxDQUFDLENBQUMsQ0FBQztZQUNILG1CQUFtQjtZQUNuQixNQUFNLGdCQUFnQixHQUFXLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyw0QkFBNEIsQ0FBQyxZQUFZLENBQUMsQ0FBQztZQUU5RixJQUFJO2dCQUNGLHVCQUF1QjtnQkFDdkIsTUFBTSxJQUFJLENBQUMsWUFBWSxDQUFDO29CQUN0QixRQUFRLEVBQUUsWUFBWTtvQkFDdEIsU0FBUyxFQUFFLGdCQUFnQjtpQkFDNUIsQ0FBQyxDQUFDO2dCQUNILE1BQU0sT0FBTyxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQ3pDLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBQyxZQUFZLEVBQUUsS0FBSyxFQUFFLGdCQUFnQixFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQztnQkFDMUYsT0FBTyxDQUFDLEdBQUcsQ0FBQyxzREFBc0QsQ0FBQyxDQUFDO2dCQUNwRSxNQUFNLE9BQU8sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUV6Qyx3Q0FBd0M7Z0JBQ3hDLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsS0FBSyxFQUFFLFlBQVksQ0FBQyxDQUFDO2dCQUV2RCxzREFBc0Q7Z0JBQ3RELE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyxZQUFZLENBQUMsQ0FBQztnQkFFbEQseURBQXlEO2dCQUN6RCxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsa0JBQWtCLENBQUMsWUFBWSxDQUFDLENBQUM7YUFDcEQ7b0JBQVM7Z0JBQ1IsaUNBQWlDO2dCQUNqQyxJQUFJO29CQUNGLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FBQzt3QkFDekIsUUFBUSxFQUFFLFlBQVk7d0JBQ3RCLFNBQVMsRUFBRSxnQkFBZ0I7cUJBQzVCLENBQUMsQ0FBQztpQkFDSjtnQkFBQyxPQUFPLENBQUMsRUFBRTtvQkFDVixPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO2lCQUNoQjthQUNGO1NBQ0Y7UUFFRCxvQkFBb0I7UUFDcEIsTUFBTSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsR0FBRyxNQUFNLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQztZQUNwRCxVQUFVLEVBQUUsS0FBSyxjQUFjLEVBQUU7WUFDakMsUUFBUSxFQUFFLENBQUMsY0FBYyxDQUFDO1NBQzNCLENBQUMsQ0FBQztRQUVILE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFckQsVUFBVTtRQUVWLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxnQkFBZ0IsQ0FBQztZQUN0QyxFQUFFLEVBQUUsT0FBTyxDQUFDLFdBQVcsQ0FBQyxPQUFPLEVBQUU7WUFDakMsVUFBVSxFQUFFLGNBQWM7WUFDMUIsVUFBVSxFQUFFLEdBQUcsQ0FBQyxRQUFRLEVBQUU7WUFDMUIsU0FBUyxFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUU7WUFDMUIsR0FBRyxFQUFFLEdBQUcsQ0FBQyxRQUFRLEVBQUU7WUFDbkIsT0FBTyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDbkIsVUFBVSxFQUNSLElBQUksQ0FBQyxHQUFHLEVBQUU7Z0JBQ1YsT0FBTyxDQUFDLFNBQVMsQ0FBQyx3QkFBd0IsQ0FBQztvQkFDekMsSUFBSSxFQUFFLEVBQUU7aUJBQ1QsQ0FBQztTQUNMLENBQUMsQ0FBQztRQUVILE1BQU0sY0FBYyxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxtQkFBbUIsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUNsRixvQkFBb0IsQ0FBQyxnQkFBZ0IsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUN0RCxvQkFBb0IsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUMvQixPQUFPLGNBQWMsQ0FBQztJQUN4QixDQUFDO0NBQ0YifQ==
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as lik from '@pushrocks/lik';
|
|
2
|
+
import * as smartdata from '@pushrocks/smartdata';
|
|
3
|
+
import * as smartdelay from '@pushrocks/smartdelay';
|
|
4
|
+
import * as smartdns from '@pushrocks/smartdns';
|
|
5
|
+
import * as smartexpress from '@pushrocks/smartexpress';
|
|
6
|
+
import * as smartlog from '@pushrocks/smartlog';
|
|
7
|
+
import * as smartpromise from '@pushrocks/smartpromise';
|
|
8
|
+
import * as smartrequest from '@pushrocks/smartrequest';
|
|
9
|
+
import * as smartunique from '@pushrocks/smartunique';
|
|
10
|
+
import * as smartstring from '@pushrocks/smartstring';
|
|
11
|
+
import * as smarttime from '@pushrocks/smarttime';
|
|
12
|
+
export { lik, smartdata, smartdelay, smartdns, smartexpress, smartlog, smartpromise, smartrequest, smartunique, smartstring, smarttime, };
|
|
13
|
+
import * as tsclass from '@tsclass/tsclass';
|
|
14
|
+
export { tsclass };
|
|
15
|
+
import * as acme from 'acme-client';
|
|
16
|
+
export { acme };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// @pushrocks scope
|
|
2
|
+
import * as lik from '@pushrocks/lik';
|
|
3
|
+
import * as smartdata from '@pushrocks/smartdata';
|
|
4
|
+
import * as smartdelay from '@pushrocks/smartdelay';
|
|
5
|
+
import * as smartdns from '@pushrocks/smartdns';
|
|
6
|
+
import * as smartexpress from '@pushrocks/smartexpress';
|
|
7
|
+
import * as smartlog from '@pushrocks/smartlog';
|
|
8
|
+
import * as smartpromise from '@pushrocks/smartpromise';
|
|
9
|
+
import * as smartrequest from '@pushrocks/smartrequest';
|
|
10
|
+
import * as smartunique from '@pushrocks/smartunique';
|
|
11
|
+
import * as smartstring from '@pushrocks/smartstring';
|
|
12
|
+
import * as smarttime from '@pushrocks/smarttime';
|
|
13
|
+
export { lik, smartdata, smartdelay, smartdns, smartexpress, smartlog, smartpromise, smartrequest, smartunique, smartstring, smarttime, };
|
|
14
|
+
// @tsclass scope
|
|
15
|
+
import * as tsclass from '@tsclass/tsclass';
|
|
16
|
+
export { tsclass };
|
|
17
|
+
// third party scope
|
|
18
|
+
import * as acme from 'acme-client';
|
|
19
|
+
export { acme };
|
|
20
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLnBsdWdpbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGFjbWUucGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxtQkFBbUI7QUFDbkIsT0FBTyxLQUFLLEdBQUcsTUFBTSxnQkFBZ0IsQ0FBQztBQUN0QyxPQUFPLEtBQUssU0FBUyxNQUFNLHNCQUFzQixDQUFDO0FBQ2xELE9BQU8sS0FBSyxVQUFVLE1BQU0sdUJBQXVCLENBQUM7QUFDcEQsT0FBTyxLQUFLLFFBQVEsTUFBTSxxQkFBcUIsQ0FBQztBQUNoRCxPQUFPLEtBQUssWUFBWSxNQUFNLHlCQUF5QixDQUFDO0FBQ3hELE9BQU8sS0FBSyxRQUFRLE1BQU0scUJBQXFCLENBQUM7QUFDaEQsT0FBTyxLQUFLLFlBQVksTUFBTSx5QkFBeUIsQ0FBQztBQUN4RCxPQUFPLEtBQUssWUFBWSxNQUFNLHlCQUF5QixDQUFDO0FBQ3hELE9BQU8sS0FBSyxXQUFXLE1BQU0sd0JBQXdCLENBQUM7QUFDdEQsT0FBTyxLQUFLLFdBQVcsTUFBTSx3QkFBd0IsQ0FBQztBQUN0RCxPQUFPLEtBQUssU0FBUyxNQUFNLHNCQUFzQixDQUFDO0FBRWxELE9BQU8sRUFDTCxHQUFHLEVBQ0gsU0FBUyxFQUNULFVBQVUsRUFDVixRQUFRLEVBQ1IsWUFBWSxFQUNaLFFBQVEsRUFDUixZQUFZLEVBQ1osWUFBWSxFQUNaLFdBQVcsRUFDWCxXQUFXLEVBQ1gsU0FBUyxHQUNWLENBQUM7QUFFRixpQkFBaUI7QUFDakIsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUU1QyxPQUFPLEVBQUUsT0FBTyxFQUFFLENBQUM7QUFFbkIsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxJQUFJLE1BQU0sYUFBYSxDQUFDO0FBRXBDLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQyJ9
|
package/npmextra.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"gitzone": {
|
|
3
|
+
"projectType": "npm",
|
|
4
|
+
"module": {
|
|
5
|
+
"githost": "gitlab.com",
|
|
6
|
+
"gitscope": "pushrocks",
|
|
7
|
+
"gitrepo": "smartacme",
|
|
8
|
+
"shortDescription": "acme with an easy yet powerful interface in TypeScript",
|
|
9
|
+
"npmPackagename": "@pushrocks/smartacme",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"projectDomain": "push.rocks"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"npmci": {
|
|
15
|
+
"npmGlobalTools": [],
|
|
16
|
+
"npmAccessLevel": "public"
|
|
17
|
+
}
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@push.rocks/smartacme",
|
|
3
|
+
"version": "4.0.3",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "acme with an easy yet powerful interface in TypeScript",
|
|
6
|
+
"main": "dist_ts/index.js",
|
|
7
|
+
"typings": "dist_ts/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "(tstest test/)",
|
|
11
|
+
"build": "(tsbuild --web --allowimplicitany)"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+ssh://git@gitlab.com/umbrellazone/smartacme.git"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"TypeScript",
|
|
19
|
+
"acme",
|
|
20
|
+
"letsencrypt"
|
|
21
|
+
],
|
|
22
|
+
"author": "Lossless GmbH",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://gitlab.com/umbrellazone/smartacme/issues"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://gitlab.com/umbrellazone/smartacme#README",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@pushrocks/lik": "^6.0.0",
|
|
30
|
+
"@pushrocks/smartdata": "^5.0.8",
|
|
31
|
+
"@pushrocks/smartdelay": "^2.0.13",
|
|
32
|
+
"@pushrocks/smartdns": "^5.0.2",
|
|
33
|
+
"@pushrocks/smartexpress": "^4.0.21",
|
|
34
|
+
"@pushrocks/smartlog": "^3.0.1",
|
|
35
|
+
"@pushrocks/smartpromise": "^3.1.7",
|
|
36
|
+
"@pushrocks/smartrequest": "^2.0.11",
|
|
37
|
+
"@pushrocks/smartstring": "^4.0.5",
|
|
38
|
+
"@pushrocks/smarttime": "^4.0.0",
|
|
39
|
+
"@pushrocks/smartunique": "^3.0.3",
|
|
40
|
+
"@tsclass/tsclass": "^4.0.21",
|
|
41
|
+
"acme-client": "^4.2.5"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@gitzone/tsbuild": "^2.1.65",
|
|
45
|
+
"@gitzone/tsrun": "^1.2.37",
|
|
46
|
+
"@gitzone/tstest": "^1.0.73",
|
|
47
|
+
"@mojoio/cloudflare": "^6.0.1",
|
|
48
|
+
"@pushrocks/qenv": "^5.0.2",
|
|
49
|
+
"@pushrocks/tapbundle": "^5.0.4",
|
|
50
|
+
"@types/node": "^18.7.23"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"ts/**/*",
|
|
54
|
+
"ts_web/**/*",
|
|
55
|
+
"dist/**/*",
|
|
56
|
+
"dist_*/**/*",
|
|
57
|
+
"dist_ts/**/*",
|
|
58
|
+
"dist_ts_web/**/*",
|
|
59
|
+
"assets/**/*",
|
|
60
|
+
"cli.js",
|
|
61
|
+
"npmextra.json",
|
|
62
|
+
"readme.md"
|
|
63
|
+
],
|
|
64
|
+
"browserslist": [
|
|
65
|
+
"last 1 chrome versions"
|
|
66
|
+
]
|
|
67
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @pushrocks/smartacme
|
|
2
|
+
acme with an easy yet powerful interface in TypeScript
|
|
3
|
+
|
|
4
|
+
## Availabililty and Links
|
|
5
|
+
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartacme)
|
|
6
|
+
* [gitlab.com (source)](https://gitlab.com/pushrocks/smartacme)
|
|
7
|
+
* [github.com (source mirror)](https://github.com/pushrocks/smartacme)
|
|
8
|
+
* [docs (typedoc)](https://pushrocks.gitlab.io/smartacme/)
|
|
9
|
+
|
|
10
|
+
## Status for master
|
|
11
|
+
|
|
12
|
+
Status Category | Status Badge
|
|
13
|
+
-- | --
|
|
14
|
+
GitLab Pipelines | [](https://lossless.cloud)
|
|
15
|
+
GitLab Pipline Test Coverage | [](https://lossless.cloud)
|
|
16
|
+
npm | [](https://lossless.cloud)
|
|
17
|
+
Snyk | [](https://lossless.cloud)
|
|
18
|
+
TypeScript Support | [](https://lossless.cloud)
|
|
19
|
+
node Support | [](https://nodejs.org/dist/latest-v10.x/docs/api/)
|
|
20
|
+
Code Style | [](https://lossless.cloud)
|
|
21
|
+
PackagePhobia (total standalone install weight) | [](https://lossless.cloud)
|
|
22
|
+
PackagePhobia (package size on registry) | [](https://lossless.cloud)
|
|
23
|
+
BundlePhobia (total size when bundled) | [](https://lossless.cloud)
|
|
24
|
+
Platform support | [](https://lossless.cloud) [](https://lossless.cloud)
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
Use TypeScript for best in class instellisense.
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
import { SmartAcme } from 'smartacme';
|
|
32
|
+
|
|
33
|
+
const run = async () => {
|
|
34
|
+
smartAcmeInstance = new smartacme.SmartAcme({
|
|
35
|
+
accountEmail: 'domains@lossless.org',
|
|
36
|
+
accountPrivateKey: null,
|
|
37
|
+
mongoDescriptor: {
|
|
38
|
+
mongoDbName: testQenv.getEnvVarRequired('MONGODB_DATABASE'),
|
|
39
|
+
mongoDbPass: testQenv.getEnvVarRequired('MONGODB_PASSWORD'),
|
|
40
|
+
mongoDbUrl: testQenv.getEnvVarRequired('MONGODB_URL'),
|
|
41
|
+
},
|
|
42
|
+
removeChallenge: async (dnsChallenge) => {
|
|
43
|
+
// somehow provide a function that is able to remove the dns challenge
|
|
44
|
+
},
|
|
45
|
+
setChallenge: async (dnsChallenge) => {
|
|
46
|
+
// somehow provide a function that is able to the dns challenge
|
|
47
|
+
},
|
|
48
|
+
environment: 'integration',
|
|
49
|
+
});
|
|
50
|
+
await smartAcmeInstance.init();
|
|
51
|
+
|
|
52
|
+
// myCert has properties for public/private keys and csr ;)
|
|
53
|
+
const myCert = await smartAcmeInstance.getCertificateForDomain('bleu.de');
|
|
54
|
+
};
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Contribution
|
|
58
|
+
|
|
59
|
+
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
|
|
60
|
+
|
|
61
|
+
For further information read the linked docs at the top of this readme.
|
|
62
|
+
|
|
63
|
+
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
|
|
64
|
+
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
|
|
65
|
+
|
|
66
|
+
[](https://maintainedby.lossless.com)
|
package/ts/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './smartacme.classes.smartacme.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './accountdata.js';
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as plugins from './smartacme.plugins.js';
|
|
2
|
+
|
|
3
|
+
import * as interfaces from './interfaces/index.js';
|
|
4
|
+
|
|
5
|
+
import { CertManager } from './smartacme.classes.certmanager.js';
|
|
6
|
+
|
|
7
|
+
import { Collection, svDb, unI } from '@pushrocks/smartdata';
|
|
8
|
+
|
|
9
|
+
@plugins.smartdata.Collection(() => {
|
|
10
|
+
return CertManager.activeDB;
|
|
11
|
+
})
|
|
12
|
+
export class Cert extends plugins.smartdata.SmartDataDbDoc<Cert, plugins.tsclass.network.ICert>
|
|
13
|
+
implements plugins.tsclass.network.ICert {
|
|
14
|
+
@unI()
|
|
15
|
+
public id: string;
|
|
16
|
+
|
|
17
|
+
@svDb()
|
|
18
|
+
public domainName: string;
|
|
19
|
+
|
|
20
|
+
@svDb()
|
|
21
|
+
public created: number;
|
|
22
|
+
|
|
23
|
+
@svDb()
|
|
24
|
+
public privateKey: string;
|
|
25
|
+
|
|
26
|
+
@svDb()
|
|
27
|
+
public publicKey: string;
|
|
28
|
+
|
|
29
|
+
@svDb()
|
|
30
|
+
public csr: string;
|
|
31
|
+
|
|
32
|
+
@svDb()
|
|
33
|
+
public validUntil: number;
|
|
34
|
+
|
|
35
|
+
public isStillValid(): boolean {
|
|
36
|
+
return this.validUntil >= Date.now();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public shouldBeRenewed(): boolean {
|
|
40
|
+
const shouldBeValidAtLeastUntil =
|
|
41
|
+
Date.now() +
|
|
42
|
+
plugins.smarttime.getMilliSecondsFromUnits({
|
|
43
|
+
days: 10,
|
|
44
|
+
});
|
|
45
|
+
return !(this.validUntil >= shouldBeValidAtLeastUntil);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public update(certDataArg: plugins.tsclass.network.ICert) {
|
|
49
|
+
Object.keys(certDataArg).forEach((key) => {
|
|
50
|
+
this[key] = certDataArg[key];
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
constructor(optionsArg: plugins.tsclass.network.ICert) {
|
|
55
|
+
super();
|
|
56
|
+
if (optionsArg) {
|
|
57
|
+
Object.keys(optionsArg).forEach((key) => {
|
|
58
|
+
this[key] = optionsArg[key];
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as plugins from './smartacme.plugins.js';
|
|
2
|
+
import { Cert } from './smartacme.classes.cert.js';
|
|
3
|
+
import { SmartAcme } from './smartacme.classes.smartacme.js';
|
|
4
|
+
|
|
5
|
+
import * as interfaces from './interfaces/index.js';
|
|
6
|
+
|
|
7
|
+
export class CertManager {
|
|
8
|
+
// =========
|
|
9
|
+
// STATIC
|
|
10
|
+
// =========
|
|
11
|
+
public static activeDB: plugins.smartdata.SmartdataDb;
|
|
12
|
+
|
|
13
|
+
// =========
|
|
14
|
+
// INSTANCE
|
|
15
|
+
// =========
|
|
16
|
+
private mongoDescriptor: plugins.smartdata.IMongoDescriptor;
|
|
17
|
+
public smartdataDb: plugins.smartdata.SmartdataDb;
|
|
18
|
+
|
|
19
|
+
public interestMap: plugins.lik.InterestMap<string, Cert>;
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
smartAcmeArg: SmartAcme,
|
|
23
|
+
optionsArg: {
|
|
24
|
+
mongoDescriptor: plugins.smartdata.IMongoDescriptor;
|
|
25
|
+
}
|
|
26
|
+
) {
|
|
27
|
+
this.mongoDescriptor = optionsArg.mongoDescriptor;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public async init() {
|
|
31
|
+
// Smartdata DB
|
|
32
|
+
this.smartdataDb = new plugins.smartdata.SmartdataDb(this.mongoDescriptor);
|
|
33
|
+
await this.smartdataDb.init();
|
|
34
|
+
CertManager.activeDB = this.smartdataDb;
|
|
35
|
+
|
|
36
|
+
// Pending Map
|
|
37
|
+
this.interestMap = new plugins.lik.InterestMap((certName) => certName);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* retrieves a certificate
|
|
42
|
+
* @returns the Cert class or null
|
|
43
|
+
* @param certDomainNameArg the domain Name to retrieve the vcertificate for
|
|
44
|
+
*/
|
|
45
|
+
public async retrieveCertificate(certDomainNameArg: string): Promise<Cert> {
|
|
46
|
+
const existingCertificate: Cert = await Cert.getInstance<Cert>({
|
|
47
|
+
domainName: certDomainNameArg,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (existingCertificate) {
|
|
51
|
+
return existingCertificate;
|
|
52
|
+
} else {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* stores the certificate
|
|
59
|
+
* @param optionsArg
|
|
60
|
+
*/
|
|
61
|
+
public async storeCertificate(optionsArg: plugins.tsclass.network.ICert) {
|
|
62
|
+
const cert = new Cert(optionsArg);
|
|
63
|
+
await cert.save();
|
|
64
|
+
const interest = this.interestMap.findInterest(cert.domainName);
|
|
65
|
+
if (interest) {
|
|
66
|
+
interest.fullfillInterest(cert);
|
|
67
|
+
interest.markLost();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public async deleteCertificate(certDomainNameArg: string) {
|
|
72
|
+
const cert: Cert = await Cert.getInstance<Cert>({
|
|
73
|
+
domainName: certDomainNameArg,
|
|
74
|
+
});
|
|
75
|
+
await cert.delete();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as plugins from './smartacme.plugins.js';
|
|
2
|
+
import * as interfaces from './interfaces/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* certmatcher is responsible for matching certificates
|
|
6
|
+
*/
|
|
7
|
+
export class CertMatcher {
|
|
8
|
+
/**
|
|
9
|
+
* creates a domainName for the certificate that will include the broadest scope
|
|
10
|
+
* for wild card certificates
|
|
11
|
+
* @param domainNameArg the domainNameArg to create the scope from
|
|
12
|
+
*/
|
|
13
|
+
public getCertificateDomainNameByDomainName(domainNameArg: string): string {
|
|
14
|
+
const originalDomain = new plugins.smartstring.Domain(domainNameArg);
|
|
15
|
+
if (!originalDomain.level4) {
|
|
16
|
+
return `${originalDomain.level2}.${originalDomain.level1}`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import * as plugins from './smartacme.plugins.js';
|
|
2
|
+
import { Cert } from './smartacme.classes.cert.js';
|
|
3
|
+
import { CertManager } from './smartacme.classes.certmanager.js';
|
|
4
|
+
import { CertMatcher } from './smartacme.classes.certmatcher.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* the options for the class @see SmartAcme
|
|
8
|
+
*/
|
|
9
|
+
export interface ISmartAcmeOptions {
|
|
10
|
+
accountPrivateKey?: string;
|
|
11
|
+
accountEmail: string;
|
|
12
|
+
mongoDescriptor: plugins.smartdata.IMongoDescriptor;
|
|
13
|
+
setChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise<any>;
|
|
14
|
+
removeChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise<any>;
|
|
15
|
+
environment: 'production' | 'integration';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* class SmartAcme
|
|
20
|
+
* can be used for setting up communication with an ACME authority
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* const mySmartAcmeInstance = new SmartAcme({
|
|
24
|
+
* // see ISmartAcmeOptions for options
|
|
25
|
+
* })
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class SmartAcme {
|
|
29
|
+
private options: ISmartAcmeOptions;
|
|
30
|
+
|
|
31
|
+
// the acme client
|
|
32
|
+
private client: any;
|
|
33
|
+
private smartdns = new plugins.smartdns.Smartdns({});
|
|
34
|
+
public logger: plugins.smartlog.ConsoleLog;
|
|
35
|
+
|
|
36
|
+
// the account private key
|
|
37
|
+
private privateKey: string;
|
|
38
|
+
|
|
39
|
+
// challenge fullfillment
|
|
40
|
+
private setChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise<any>;
|
|
41
|
+
private removeChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise<any>;
|
|
42
|
+
|
|
43
|
+
// certmanager
|
|
44
|
+
private certmanager: CertManager;
|
|
45
|
+
private certmatcher: CertMatcher;
|
|
46
|
+
|
|
47
|
+
constructor(optionsArg: ISmartAcmeOptions) {
|
|
48
|
+
this.options = optionsArg;
|
|
49
|
+
this.logger = new plugins.smartlog.ConsoleLog();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* inits the instance
|
|
54
|
+
* ```ts
|
|
55
|
+
* await myCloudlyInstance.init() // does not support options
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
public async init() {
|
|
59
|
+
this.privateKey =
|
|
60
|
+
this.options.accountPrivateKey || (await plugins.acme.forge.createPrivateKey()).toString();
|
|
61
|
+
this.setChallenge = this.options.setChallenge;
|
|
62
|
+
this.removeChallenge = this.options.removeChallenge;
|
|
63
|
+
|
|
64
|
+
// CertMangaer
|
|
65
|
+
this.certmanager = new CertManager(this, {
|
|
66
|
+
mongoDescriptor: this.options.mongoDescriptor,
|
|
67
|
+
});
|
|
68
|
+
await this.certmanager.init();
|
|
69
|
+
|
|
70
|
+
// CertMatcher
|
|
71
|
+
this.certmatcher = new CertMatcher();
|
|
72
|
+
|
|
73
|
+
// ACME Client
|
|
74
|
+
this.client = new plugins.acme.Client({
|
|
75
|
+
directoryUrl: (() => {
|
|
76
|
+
if (this.options.environment === 'production') {
|
|
77
|
+
return plugins.acme.directory.letsencrypt.production;
|
|
78
|
+
} else {
|
|
79
|
+
return plugins.acme.directory.letsencrypt.staging;
|
|
80
|
+
}
|
|
81
|
+
})(),
|
|
82
|
+
accountKey: this.privateKey,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
/* Register account */
|
|
86
|
+
await this.client.createAccount({
|
|
87
|
+
termsOfServiceAgreed: true,
|
|
88
|
+
contact: [`mailto:${this.options.accountEmail}`],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public async stop() {
|
|
93
|
+
await this.certmanager.smartdataDb.close();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* gets a certificate
|
|
98
|
+
* it runs through the following steps
|
|
99
|
+
*
|
|
100
|
+
* * look in the database
|
|
101
|
+
* * if in the database and still valid return it
|
|
102
|
+
* * if not in the database announce it
|
|
103
|
+
* * then get it from letsencrypt
|
|
104
|
+
* * store it
|
|
105
|
+
* * remove it from the pending map (which it go onto by announcing it)
|
|
106
|
+
* * retrieve it from the databse and return it
|
|
107
|
+
*
|
|
108
|
+
* @param domainArg
|
|
109
|
+
*/
|
|
110
|
+
public async getCertificateForDomain(domainArg: string): Promise<Cert> {
|
|
111
|
+
const certDomainName = this.certmatcher.getCertificateDomainNameByDomainName(domainArg);
|
|
112
|
+
const retrievedCertificate = await this.certmanager.retrieveCertificate(certDomainName);
|
|
113
|
+
|
|
114
|
+
if (
|
|
115
|
+
!retrievedCertificate &&
|
|
116
|
+
(await this.certmanager.interestMap.checkInterest(certDomainName))
|
|
117
|
+
) {
|
|
118
|
+
const existingCertificateInterest = this.certmanager.interestMap.findInterest(certDomainName);
|
|
119
|
+
const certificate = existingCertificateInterest.interestFullfilled;
|
|
120
|
+
return certificate;
|
|
121
|
+
} else if (retrievedCertificate && !retrievedCertificate.shouldBeRenewed()) {
|
|
122
|
+
return retrievedCertificate;
|
|
123
|
+
} else if (retrievedCertificate && retrievedCertificate.shouldBeRenewed()) {
|
|
124
|
+
await retrievedCertificate.delete();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// lets make sure others get the same interest
|
|
128
|
+
const currentDomainInterst = await this.certmanager.interestMap.addInterest(certDomainName);
|
|
129
|
+
|
|
130
|
+
/* Place new order */
|
|
131
|
+
const order = await this.client.createOrder({
|
|
132
|
+
identifiers: [
|
|
133
|
+
{ type: 'dns', value: certDomainName },
|
|
134
|
+
{ type: 'dns', value: `*.${certDomainName}` },
|
|
135
|
+
],
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
/* Get authorizations and select challenges */
|
|
139
|
+
const authorizations = await this.client.getAuthorizations(order);
|
|
140
|
+
|
|
141
|
+
for (const authz of authorizations) {
|
|
142
|
+
console.log(authz);
|
|
143
|
+
const fullHostName: string = `_acme-challenge.${authz.identifier.value}`;
|
|
144
|
+
const dnsChallenge: string = authz.challenges.find((challengeArg) => {
|
|
145
|
+
return challengeArg.type === 'dns-01';
|
|
146
|
+
});
|
|
147
|
+
// process.exit(1);
|
|
148
|
+
const keyAuthorization: string = await this.client.getChallengeKeyAuthorization(dnsChallenge);
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
/* Satisfy challenge */
|
|
152
|
+
await this.setChallenge({
|
|
153
|
+
hostName: fullHostName,
|
|
154
|
+
challenge: keyAuthorization,
|
|
155
|
+
});
|
|
156
|
+
await plugins.smartdelay.delayFor(30000);
|
|
157
|
+
await this.smartdns.checkUntilAvailable(fullHostName, 'TXT', keyAuthorization, 100, 5000);
|
|
158
|
+
console.log('Cool down an extra 60 second for region availability');
|
|
159
|
+
await plugins.smartdelay.delayFor(60000);
|
|
160
|
+
|
|
161
|
+
/* Verify that challenge is satisfied */
|
|
162
|
+
await this.client.verifyChallenge(authz, dnsChallenge);
|
|
163
|
+
|
|
164
|
+
/* Notify ACME provider that challenge is satisfied */
|
|
165
|
+
await this.client.completeChallenge(dnsChallenge);
|
|
166
|
+
|
|
167
|
+
/* Wait for ACME provider to respond with valid status */
|
|
168
|
+
await this.client.waitForValidStatus(dnsChallenge);
|
|
169
|
+
} finally {
|
|
170
|
+
/* Clean up challenge response */
|
|
171
|
+
try {
|
|
172
|
+
await this.removeChallenge({
|
|
173
|
+
hostName: fullHostName,
|
|
174
|
+
challenge: keyAuthorization,
|
|
175
|
+
});
|
|
176
|
+
} catch (e) {
|
|
177
|
+
console.log(e);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Finalize order */
|
|
183
|
+
const [key, csr] = await plugins.acme.forge.createCsr({
|
|
184
|
+
commonName: `*.${certDomainName}`,
|
|
185
|
+
altNames: [certDomainName],
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
await this.client.finalizeOrder(order, csr);
|
|
189
|
+
const cert = await this.client.getCertificate(order);
|
|
190
|
+
|
|
191
|
+
/* Done */
|
|
192
|
+
|
|
193
|
+
await this.certmanager.storeCertificate({
|
|
194
|
+
id: plugins.smartunique.shortId(),
|
|
195
|
+
domainName: certDomainName,
|
|
196
|
+
privateKey: key.toString(),
|
|
197
|
+
publicKey: cert.toString(),
|
|
198
|
+
csr: csr.toString(),
|
|
199
|
+
created: Date.now(),
|
|
200
|
+
validUntil:
|
|
201
|
+
Date.now() +
|
|
202
|
+
plugins.smarttime.getMilliSecondsFromUnits({
|
|
203
|
+
days: 90,
|
|
204
|
+
}),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const newCertificate = await this.certmanager.retrieveCertificate(certDomainName);
|
|
208
|
+
currentDomainInterst.fullfillInterest(newCertificate);
|
|
209
|
+
currentDomainInterst.destroy();
|
|
210
|
+
return newCertificate;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// @pushrocks scope
|
|
2
|
+
import * as lik from '@pushrocks/lik';
|
|
3
|
+
import * as smartdata from '@pushrocks/smartdata';
|
|
4
|
+
import * as smartdelay from '@pushrocks/smartdelay';
|
|
5
|
+
import * as smartdns from '@pushrocks/smartdns';
|
|
6
|
+
import * as smartexpress from '@pushrocks/smartexpress';
|
|
7
|
+
import * as smartlog from '@pushrocks/smartlog';
|
|
8
|
+
import * as smartpromise from '@pushrocks/smartpromise';
|
|
9
|
+
import * as smartrequest from '@pushrocks/smartrequest';
|
|
10
|
+
import * as smartunique from '@pushrocks/smartunique';
|
|
11
|
+
import * as smartstring from '@pushrocks/smartstring';
|
|
12
|
+
import * as smarttime from '@pushrocks/smarttime';
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
lik,
|
|
16
|
+
smartdata,
|
|
17
|
+
smartdelay,
|
|
18
|
+
smartdns,
|
|
19
|
+
smartexpress,
|
|
20
|
+
smartlog,
|
|
21
|
+
smartpromise,
|
|
22
|
+
smartrequest,
|
|
23
|
+
smartunique,
|
|
24
|
+
smartstring,
|
|
25
|
+
smarttime,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// @tsclass scope
|
|
29
|
+
import * as tsclass from '@tsclass/tsclass';
|
|
30
|
+
|
|
31
|
+
export { tsclass };
|
|
32
|
+
|
|
33
|
+
// third party scope
|
|
34
|
+
import * as acme from 'acme-client';
|
|
35
|
+
|
|
36
|
+
export { acme };
|