fix(core): update

This commit is contained in:
Philipp Kunz 2019-01-09 00:01:01 +01:00
parent a87c6acb8a
commit 060ebf1b29
9 changed files with 154 additions and 28 deletions

View File

@ -11,20 +11,23 @@ tap.test('should create a valid instance of SmartAcme', async () => {
smartAcmeInstance = new smartacme.SmartAcme({ smartAcmeInstance = new smartacme.SmartAcme({
accountEmail: 'domains@lossless.org', accountEmail: 'domains@lossless.org',
accountPrivateKey: null, accountPrivateKey: null,
mongoDescriptor: {
mongoDbName: testQenv.getEnvVarOnDemand('MONGODB_DATABASE'),
mongoDbPass: testQenv.getEnvVarOnDemand('MONGODB_PASSWORD'),
mongoDbUrl: testQenv.getEnvVarOnDemand('MONGODB_URL')
},
removeChallenge: async (...args) => { removeChallenge: async (...args) => {
console.log(args); console.log(args);
}, },
setChallenge: async (...args) => { setChallenge: async (...args) => {
console.log(args); console.log(args);
}, },
mongoDescriptor: { validateRemoteRequest: async () => {
mongoDbName: testQenv.getEnvVarOnDemand('MONGODB_DATABASE'), return true;
mongoDbPass: testQenv.getEnvVarOnDemand('MONGODB_PASSWORD'),
mongoDbUrl: testQenv.getEnvVarOnDemand('MONGODB_URL')
} }
}); });
await smartAcmeInstance.init(); await smartAcmeInstance.init();
await smartAcmeInstance.getCertificateForDomain('bleu.de'); // await smartAcmeInstance.getCertificateForDomain('bleu.de');
}); });
tap.start(); tap.start();

9
ts/interfaces/cert.ts Normal file
View File

@ -0,0 +1,9 @@
export type TCertStatus = 'existing' | 'nonexisting' | 'pending' | 'failed';
export interface ICert {
domainName: string;
created: number;
privateKey: string;
publicKey: string;
csr: string;
}

View File

@ -0,0 +1,11 @@
import { ICert, TCertStatus } from './cert';
export interface ICertRemoteRequest {
secret: string;
domainName: string;
}
export interface ICertRemoteResponse {
status: TCertStatus;
certificate?: ICert;
}

View File

@ -1 +1,3 @@
export * from './accountdata'; export * from './accountdata';
export * from './cert';
export * from './certremote';

View File

@ -1,4 +1,7 @@
import * as plugins from './smartacme.plugins'; import * as plugins from './smartacme.plugins';
import * as interfaces from './interfaces';
import { CertManager } from './smartacme.classes.certmanager'; import { CertManager } from './smartacme.classes.certmanager';
import { Collection, svDb, unI } from '@pushrocks/smartdata'; import { Collection, svDb, unI } from '@pushrocks/smartdata';
@ -6,24 +9,24 @@ import { Collection, svDb, unI } from '@pushrocks/smartdata';
@plugins.smartdata.Collection(() => { @plugins.smartdata.Collection(() => {
return CertManager.activeDB; return CertManager.activeDB;
}) })
export class Cert extends plugins.smartdata.SmartDataDbDoc<Cert> { export class Cert extends plugins.smartdata.SmartDataDbDoc<Cert> implements interfaces.ICert {
@unI() @unI()
public index: string; public index: string;
@svDb() @svDb()
domainName: string; public domainName: string;
@svDb() @svDb()
created: number; public created: number;
@svDb() @svDb()
privateKey: string; public privateKey: string;
@svDb() @svDb()
publicKey: string; public publicKey: string;
@svDb() @svDb()
csr: string; public csr: string;
constructor(privateKeyArg: string, publicKeyArg: string, csrArg: string) { constructor(privateKeyArg: string, publicKeyArg: string, csrArg: string) {
super(); super();

View File

@ -1,5 +1,8 @@
import * as plugins from './smartacme.plugins'; import * as plugins from './smartacme.plugins';
import { Cert } from './smartacme.classes.cert'; import { Cert } from './smartacme.classes.cert';
import { SmartAcme } from './smartacme.classes.smartacme';
import * as interfaces from './interfaces';
export class CertManager { export class CertManager {
@ -15,16 +18,22 @@ export class CertManager {
private mongoDescriptor: plugins.smartdata.IMongoDescriptor; private mongoDescriptor: plugins.smartdata.IMongoDescriptor;
public smartdataDb: plugins.smartdata.SmartdataDb; public smartdataDb: plugins.smartdata.SmartdataDb;
constructor(optionsArg: { public pendingMap: plugins.lik.Stringmap;
constructor(smartAcmeArg: SmartAcme,optionsArg: {
mongoDescriptor: plugins.smartdata.IMongoDescriptor; mongoDescriptor: plugins.smartdata.IMongoDescriptor;
}) { }) {
this.mongoDescriptor = optionsArg.mongoDescriptor; this.mongoDescriptor = optionsArg.mongoDescriptor;
} }
public async init () { public async init () {
// Smartdata DB
this.smartdataDb = new plugins.smartdata.SmartdataDb(this.mongoDescriptor); this.smartdataDb = new plugins.smartdata.SmartdataDb(this.mongoDescriptor);
await this.smartdataDb.init(); await this.smartdataDb.init();
CertManager.activeDB = this.smartdataDb; CertManager.activeDB = this.smartdataDb;
// Pending Map
this.pendingMap = new plugins.lik.Stringmap();
}; };
/** /**
@ -33,6 +42,7 @@ export class CertManager {
* @param domainName the domain Name to retrieve the vcertificate for * @param domainName the domain Name to retrieve the vcertificate for
*/ */
public async retrieveCertificate(domainName: string): Promise<Cert> { public async retrieveCertificate(domainName: string): Promise<Cert> {
await this.checkCerts();
const existingCertificate: Cert = await Cert.getInstance({ const existingCertificate: Cert = await Cert.getInstance({
name: domainName name: domainName
}); });
@ -56,12 +66,27 @@ export class CertManager {
cert.save(); cert.save();
}; };
public async deleteCertificate(domainName: string) { public async deleteCertificate(domainNameArg: string) {
}; }
public async getCertificateStatus(domainNameArg: string): Promise<interfaces.TCertStatus> {
const isPending = this.pendingMap.checkString('domainNameArg');
if (isPending) {
return 'pending';
}
// otherwise lets continue
const existingCertificate = this.retrieveCertificate(domainNameArg);
if (existingCertificate) {
return 'existing';
}
return 'nonexisting';
}
/** /**
* checks all certs for expiration * checks all certs for expiration
*/ */
checkCerts() {} private async checkCerts() {};
} }

View File

@ -1,10 +1,47 @@
import * as plugins from './smartacme.plugins'; import * as plugins from './smartacme.plugins';
import * as interfaces from './interfaces';
import { ICertRemoteResponse } from './interfaces';
// tslint:disable-next-line: max-classes-per-file
export class CertRemoteClient { export class CertRemoteClient {
private remoteUrl: string;
private secret: string;
constructor(optionsArg: { constructor(optionsArg: {
remoteUrl: string; remoteUrl: string;
secret: string; secret: string;
}) { }) {
this.remoteUrl = optionsArg.remoteUrl;
this.secret = optionsArg.secret;
}
/**
*
* @param domainNameArg
*/
async getCertificateForDomain(domainNameArg: string): Promise<interfaces.ICert> {
let certificate: interfaces.ICert;
const doRequestCycle = async (): Promise<interfaces.ICert> => {
const response: ICertRemoteResponse = (await plugins.smartrequest.postJson(this.remoteUrl, {
requestBody: <interfaces.ICertRemoteRequest>{
domainName: domainNameArg,
secret: this.secret
}
})).body;
switch(response.status) {
case 'pending':
await plugins.smartdelay.delayFor(5000);
const finalResponse = await doRequestCycle();
return finalResponse;
case 'existing':
return response.certificate;
case 'failed':
default:
console.log(`could not retrieve certificate for ${domainNameArg}`);
return null;
}
};
certificate = await doRequestCycle();
return certificate;
} }
} }

View File

@ -1,3 +0,0 @@
import * as plugins from './smartacme.plugins';
export class CertRemoteHandler {}

View File

@ -1,6 +1,8 @@
import * as plugins from './smartacme.plugins'; import * as plugins from './smartacme.plugins';
import { CertManager } from './smartacme.classes.certmanager'; import { CertManager } from './smartacme.classes.certmanager';
import { CertRemoteHandler } from './smartacme.classes.certremotehandler';
import * as interfaces from './interfaces';
import { request } from 'http';
/** /**
* *
@ -8,9 +10,10 @@ import { CertRemoteHandler } from './smartacme.classes.certremotehandler';
export interface ISmartAcmeOptions { export interface ISmartAcmeOptions {
accountPrivateKey?: string; accountPrivateKey?: string;
accountEmail: string; accountEmail: string;
mongoDescriptor: plugins.smartdata.IMongoDescriptor;
setChallenge: (domainName: string, keyAuthorization: string) => Promise<any>; setChallenge: (domainName: string, keyAuthorization: string) => Promise<any>;
removeChallenge: (domainName: string) => Promise<any>; removeChallenge: (domainName: string) => Promise<any>;
mongoDescriptor: plugins.smartdata.IMongoDescriptor; validateRemoteRequest: () => Promise<boolean>;
} }
export class SmartAcme { export class SmartAcme {
@ -26,26 +29,64 @@ export class SmartAcme {
// challenge fullfillment // challenge fullfillment
private setChallenge: (domainName: string, keyAuthorization: string) => Promise<any>; private setChallenge: (domainName: string, keyAuthorization: string) => Promise<any>;
private removeChallenge: (domainName: string) => Promise<any>; private removeChallenge: (domainName: string) => Promise<any>;
private validateRemoteRequest: () => Promise<boolean>;
// certmanager // certmanager
private certmanager: CertManager; private certmanager: CertManager;
private certremoteHandler: CertRemoteHandler; private certremoteHandler: plugins.smartexpress.Handler;
constructor(optionsArg: ISmartAcmeOptions) { constructor(optionsArg: ISmartAcmeOptions) {
this.options = optionsArg; this.options = optionsArg;
} }
/**
* inits the instance
*/
public async init() { public async init() {
this.privateKey = this.options.accountPrivateKey || (await plugins.acme.forge.createPrivateKey()); this.privateKey =
this.options.accountPrivateKey || (await plugins.acme.forge.createPrivateKey());
this.setChallenge = this.options.setChallenge; this.setChallenge = this.options.setChallenge;
this.removeChallenge = this.options.removeChallenge; this.removeChallenge = this.options.removeChallenge;
this.certmanager = new CertManager({ // CertMangaer
this.certmanager = new CertManager(this, {
mongoDescriptor: this.options.mongoDescriptor mongoDescriptor: this.options.mongoDescriptor
}); });
await this.certmanager.init(); await this.certmanager.init();
this.certremoteHandler = new CertRemoteHandler();
// CertRemoteHandler
this.certremoteHandler = new plugins.smartexpress.Handler('POST', async (req, res) => {
const requestBody: interfaces.ICertRemoteRequest = req.body;
const status: interfaces.TCertStatus = await this.certmanager.getCertificateStatus(requestBody.domainName);
const existingCertificate = await this.certmanager.retrieveCertificate(
requestBody.domainName
);
let response: interfaces.ICertRemoteResponse;
switch (status) {
case 'existing':
response = {
status,
certificate: {
created: existingCertificate.created,
csr: existingCertificate.csr,
domainName: existingCertificate.domainName,
privateKey: existingCertificate.privateKey,
publicKey: existingCertificate.publicKey
}
};
break;
default:
response = {
status
};
break;
}
res.status(200);
res.send(response);
res.end();
});
// ACME Client
this.client = new plugins.acme.Client({ this.client = new plugins.acme.Client({
directoryUrl: plugins.acme.directory.letsencrypt.staging, directoryUrl: plugins.acme.directory.letsencrypt.staging,
accountKey: this.privateKey accountKey: this.privateKey
@ -63,7 +104,7 @@ export class SmartAcme {
const retrievedCertificate = await this.certmanager.retrieveCertificate(domain); const retrievedCertificate = await this.certmanager.retrieveCertificate(domain);
if(retrievedCertificate) { if (retrievedCertificate) {
return retrievedCertificate; return retrievedCertificate;
} }
@ -123,6 +164,4 @@ export class SmartAcme {
this.certmanager.storeCertificate(key.toString(), cert.toString(), csr.toString()); this.certmanager.storeCertificate(key.toString(), cert.toString(), csr.toString());
} }
toStorageObject() {}
} }