fix(core): update
This commit is contained in:
		
							
								
								
									
										13
									
								
								test/test.ts
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								test/test.ts
									
									
									
									
									
								
							@@ -11,20 +11,23 @@ tap.test('should create a valid instance of SmartAcme', async () => {
 | 
			
		||||
  smartAcmeInstance = new smartacme.SmartAcme({
 | 
			
		||||
    accountEmail: 'domains@lossless.org',
 | 
			
		||||
    accountPrivateKey: null,
 | 
			
		||||
    mongoDescriptor: {
 | 
			
		||||
      mongoDbName: testQenv.getEnvVarOnDemand('MONGODB_DATABASE'),
 | 
			
		||||
      mongoDbPass: testQenv.getEnvVarOnDemand('MONGODB_PASSWORD'),
 | 
			
		||||
      mongoDbUrl: testQenv.getEnvVarOnDemand('MONGODB_URL')
 | 
			
		||||
    },
 | 
			
		||||
    removeChallenge: async (...args) => {
 | 
			
		||||
      console.log(args);
 | 
			
		||||
    },
 | 
			
		||||
    setChallenge: async (...args) => {
 | 
			
		||||
      console.log(args);
 | 
			
		||||
    },
 | 
			
		||||
    mongoDescriptor: {
 | 
			
		||||
      mongoDbName: testQenv.getEnvVarOnDemand('MONGODB_DATABASE'),
 | 
			
		||||
      mongoDbPass: testQenv.getEnvVarOnDemand('MONGODB_PASSWORD'),
 | 
			
		||||
      mongoDbUrl: testQenv.getEnvVarOnDemand('MONGODB_URL')
 | 
			
		||||
    validateRemoteRequest: async () => {
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  await smartAcmeInstance.init();
 | 
			
		||||
  await smartAcmeInstance.getCertificateForDomain('bleu.de');
 | 
			
		||||
  // await smartAcmeInstance.getCertificateForDomain('bleu.de');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
tap.start();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								ts/interfaces/cert.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								ts/interfaces/cert.ts
									
									
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								ts/interfaces/certremote.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ts/interfaces/certremote.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
import { ICert, TCertStatus } from './cert';
 | 
			
		||||
 | 
			
		||||
export interface ICertRemoteRequest {
 | 
			
		||||
  secret: string;
 | 
			
		||||
  domainName: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ICertRemoteResponse {
 | 
			
		||||
  status: TCertStatus;
 | 
			
		||||
  certificate?: ICert;
 | 
			
		||||
}
 | 
			
		||||
@@ -1 +1,3 @@
 | 
			
		||||
export * from './accountdata';
 | 
			
		||||
export * from './cert';
 | 
			
		||||
export * from './certremote';
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,7 @@
 | 
			
		||||
import * as plugins from './smartacme.plugins';
 | 
			
		||||
 | 
			
		||||
import * as interfaces from './interfaces';
 | 
			
		||||
 | 
			
		||||
import { CertManager } from './smartacme.classes.certmanager';
 | 
			
		||||
 | 
			
		||||
import { Collection, svDb, unI } from '@pushrocks/smartdata';
 | 
			
		||||
@@ -6,24 +9,24 @@ import { Collection, svDb, unI } from '@pushrocks/smartdata';
 | 
			
		||||
@plugins.smartdata.Collection(() => {
 | 
			
		||||
  return CertManager.activeDB;
 | 
			
		||||
})
 | 
			
		||||
export class Cert extends plugins.smartdata.SmartDataDbDoc<Cert> {
 | 
			
		||||
export class Cert extends plugins.smartdata.SmartDataDbDoc<Cert> implements interfaces.ICert {
 | 
			
		||||
  @unI()
 | 
			
		||||
  public index: string;
 | 
			
		||||
 | 
			
		||||
  @svDb()
 | 
			
		||||
  domainName: string;
 | 
			
		||||
  public domainName: string;
 | 
			
		||||
 | 
			
		||||
  @svDb()
 | 
			
		||||
  created: number;
 | 
			
		||||
  public created: number;
 | 
			
		||||
 | 
			
		||||
  @svDb()
 | 
			
		||||
  privateKey: string;
 | 
			
		||||
  public privateKey: string;
 | 
			
		||||
  
 | 
			
		||||
  @svDb()
 | 
			
		||||
  publicKey: string;
 | 
			
		||||
  public publicKey: string;
 | 
			
		||||
  
 | 
			
		||||
  @svDb()
 | 
			
		||||
  csr: string;
 | 
			
		||||
  public csr: string;
 | 
			
		||||
 | 
			
		||||
  constructor(privateKeyArg: string, publicKeyArg: string, csrArg: string) {
 | 
			
		||||
    super();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,8 @@
 | 
			
		||||
import * as plugins from './smartacme.plugins';
 | 
			
		||||
import { Cert } from './smartacme.classes.cert';
 | 
			
		||||
import { SmartAcme } from './smartacme.classes.smartacme';
 | 
			
		||||
 | 
			
		||||
import * as interfaces from './interfaces';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export class CertManager {
 | 
			
		||||
@@ -15,16 +18,22 @@ export class CertManager {
 | 
			
		||||
  private mongoDescriptor: plugins.smartdata.IMongoDescriptor;
 | 
			
		||||
  public smartdataDb: plugins.smartdata.SmartdataDb;
 | 
			
		||||
 | 
			
		||||
  constructor(optionsArg: {
 | 
			
		||||
  public pendingMap: plugins.lik.Stringmap;
 | 
			
		||||
 | 
			
		||||
  constructor(smartAcmeArg: SmartAcme,optionsArg: {
 | 
			
		||||
    mongoDescriptor: plugins.smartdata.IMongoDescriptor;
 | 
			
		||||
  }) {
 | 
			
		||||
    this.mongoDescriptor = optionsArg.mongoDescriptor;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async init () {
 | 
			
		||||
    // Smartdata DB
 | 
			
		||||
    this.smartdataDb = new plugins.smartdata.SmartdataDb(this.mongoDescriptor);
 | 
			
		||||
    await this.smartdataDb.init();
 | 
			
		||||
    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
 | 
			
		||||
   */
 | 
			
		||||
  public async retrieveCertificate(domainName: string): Promise<Cert> {
 | 
			
		||||
    await this.checkCerts();
 | 
			
		||||
    const existingCertificate: Cert = await Cert.getInstance({
 | 
			
		||||
      name: domainName
 | 
			
		||||
    });
 | 
			
		||||
@@ -56,12 +66,27 @@ export class CertManager {
 | 
			
		||||
    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
 | 
			
		||||
   */
 | 
			
		||||
  checkCerts() {}
 | 
			
		||||
  private async checkCerts() {};
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,47 @@
 | 
			
		||||
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 {
 | 
			
		||||
  private remoteUrl: string;
 | 
			
		||||
  private secret: string;
 | 
			
		||||
 | 
			
		||||
  constructor(optionsArg: {
 | 
			
		||||
    remoteUrl: 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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
import * as plugins from './smartacme.plugins';
 | 
			
		||||
 | 
			
		||||
export class CertRemoteHandler {}
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
import * as plugins from './smartacme.plugins';
 | 
			
		||||
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 {
 | 
			
		||||
  accountPrivateKey?: string;
 | 
			
		||||
  accountEmail: string;
 | 
			
		||||
  mongoDescriptor: plugins.smartdata.IMongoDescriptor;
 | 
			
		||||
  setChallenge: (domainName: string, keyAuthorization: string) => Promise<any>;
 | 
			
		||||
  removeChallenge: (domainName: string) => Promise<any>;
 | 
			
		||||
  mongoDescriptor: plugins.smartdata.IMongoDescriptor;
 | 
			
		||||
  validateRemoteRequest: () => Promise<boolean>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SmartAcme {
 | 
			
		||||
@@ -26,26 +29,64 @@ export class SmartAcme {
 | 
			
		||||
  // challenge fullfillment
 | 
			
		||||
  private setChallenge: (domainName: string, keyAuthorization: string) => Promise<any>;
 | 
			
		||||
  private removeChallenge: (domainName: string) => Promise<any>;
 | 
			
		||||
  private validateRemoteRequest: () => Promise<boolean>;
 | 
			
		||||
 | 
			
		||||
  // certmanager
 | 
			
		||||
  private certmanager: CertManager;
 | 
			
		||||
  private certremoteHandler: CertRemoteHandler;
 | 
			
		||||
  private certremoteHandler: plugins.smartexpress.Handler;
 | 
			
		||||
 | 
			
		||||
  constructor(optionsArg: ISmartAcmeOptions) {
 | 
			
		||||
    this.options = optionsArg;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * inits the instance
 | 
			
		||||
   */
 | 
			
		||||
  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.removeChallenge = this.options.removeChallenge;
 | 
			
		||||
 | 
			
		||||
    this.certmanager = new CertManager({
 | 
			
		||||
    // CertMangaer
 | 
			
		||||
    this.certmanager = new CertManager(this, {
 | 
			
		||||
      mongoDescriptor: this.options.mongoDescriptor
 | 
			
		||||
    });
 | 
			
		||||
    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({
 | 
			
		||||
      directoryUrl: plugins.acme.directory.letsencrypt.staging,
 | 
			
		||||
      accountKey: this.privateKey
 | 
			
		||||
@@ -63,7 +104,7 @@ export class SmartAcme {
 | 
			
		||||
 | 
			
		||||
    const retrievedCertificate = await this.certmanager.retrieveCertificate(domain);
 | 
			
		||||
 | 
			
		||||
    if(retrievedCertificate) {
 | 
			
		||||
    if (retrievedCertificate) {
 | 
			
		||||
      return retrievedCertificate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -123,6 +164,4 @@ export class SmartAcme {
 | 
			
		||||
 | 
			
		||||
    this.certmanager.storeCertificate(key.toString(), cert.toString(), csr.toString());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toStorageObject() {}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user