Compare commits

..

106 Commits

Author SHA1 Message Date
82b1d68576 2.0.35 2019-01-17 22:50:22 +01:00
e04b23aceb fix(core): update 2019-01-17 22:50:21 +01:00
8e255938b5 2.0.34 2019-01-17 22:47:58 +01:00
f2eb9666a7 fix(core): update 2019-01-17 22:47:58 +01:00
cbdb0c8b08 2.0.33 2019-01-17 22:13:10 +01:00
f821f4d9cc fix(core): update 2019-01-17 22:13:10 +01:00
6cfcf21d95 2.0.32 2019-01-17 01:15:22 +01:00
a33090bb5e fix(core): update 2019-01-17 01:15:22 +01:00
3151829f85 2.0.31 2019-01-16 22:34:38 +01:00
eca63e588c fix(core): update 2019-01-16 22:34:38 +01:00
9d23e205d8 2.0.30 2019-01-16 02:34:48 +01:00
5ecdf7c9fd fix(core): update 2019-01-16 02:34:47 +01:00
2817a65e21 2.0.29 2019-01-15 23:59:21 +01:00
09a8bc5cb5 fix(core): update 2019-01-15 23:59:21 +01:00
a1134cf227 2.0.28 2019-01-15 23:39:31 +01:00
4ee1c4b08c fix(core): update 2019-01-15 23:39:31 +01:00
08c3eaa65f 2.0.27 2019-01-14 02:46:36 +01:00
2717f08476 fix(core): update 2019-01-14 02:46:36 +01:00
f16dbeea32 2.0.26 2019-01-13 21:40:40 +01:00
a0c0230419 fix(core): update 2019-01-13 21:40:40 +01:00
0d1ebf2d1a 2.0.25 2019-01-13 19:40:32 +01:00
6edbf3cb46 fix(core): update 2019-01-13 19:40:32 +01:00
b26f7ac3e9 2.0.24 2019-01-13 19:15:04 +01:00
5129c5d601 fix(core): update 2019-01-13 19:15:03 +01:00
d09b3fd1bc 2.0.23 2019-01-13 02:11:56 +01:00
14fccd40d8 fix(core): update 2019-01-13 02:11:56 +01:00
c0f45a10e0 2.0.22 2019-01-13 02:10:00 +01:00
f9db3d28fe fix(core): update 2019-01-13 02:10:00 +01:00
c3fd8750b2 2.0.21 2019-01-13 00:50:44 +01:00
2b3c28c7a1 fix(core): update 2019-01-13 00:50:43 +01:00
d6b1f942b3 2.0.20 2019-01-13 00:24:40 +01:00
7eff6ea36a fix(core): update 2019-01-13 00:24:39 +01:00
1ef3615a49 2.0.19 2019-01-13 00:06:00 +01:00
3653cdc797 fix(core): update 2019-01-13 00:06:00 +01:00
c0271648fc 2.0.18 2019-01-12 21:06:29 +01:00
5546fa5f49 fix(core): update 2019-01-12 21:06:29 +01:00
54fe89860e 2.0.17 2019-01-12 19:12:53 +01:00
d1edf75f6f fix(core): update 2019-01-12 19:12:52 +01:00
6f9c644221 2.0.16 2019-01-12 19:11:39 +01:00
0b26054687 fix(core): update 2019-01-12 19:11:39 +01:00
e3323ed4ef 2.0.15 2019-01-12 13:52:21 +01:00
24f692636c fix(core): update 2019-01-12 13:52:21 +01:00
a9f709ee7b 2.0.14 2019-01-12 13:44:18 +01:00
1b11b637a5 fix(core): update 2019-01-12 13:44:18 +01:00
ad54bf41ea 2.0.13 2019-01-09 00:01:02 +01:00
060ebf1b29 fix(core): update 2019-01-09 00:01:01 +01:00
a87c6acb8a 2.0.12 2019-01-08 20:45:36 +01:00
62d27619f4 fix(core): update 2019-01-08 20:45:35 +01:00
0faebf2a79 2.0.11 2019-01-07 12:29:11 +01:00
29ea50796c fix(core): update 2019-01-07 12:29:10 +01:00
26d1b7cbf0 2.0.10 2019-01-07 01:08:50 +01:00
c0c97835ea fix(core): update 2019-01-07 01:08:50 +01:00
d4d50b7dcf 2.0.9 2019-01-07 01:00:58 +01:00
2492fd4de2 fix(core): update 2019-01-07 01:00:58 +01:00
bef54799b6 2.0.8 2019-01-07 00:36:51 +01:00
dbe09f320a fix(core): update 2019-01-07 00:36:51 +01:00
18045dadaf 2.0.7 2019-01-06 23:54:47 +01:00
ee300c3e12 fix(core): update 2019-01-06 23:54:46 +01:00
ed4ba0cb61 2.0.6 2019-01-06 23:30:39 +01:00
a8ab27045d fix(core): update 2019-01-06 23:30:38 +01:00
975c3ed190 2.0.5 2019-01-06 20:41:43 +01:00
a99dea549b fix(core): update 2019-01-06 20:41:42 +01:00
f8b78c433a 2.0.4 2019-01-06 20:41:22 +01:00
6c33111074 fix(core): update 2019-01-06 20:41:21 +01:00
280335f6f6 2.0.3 2019-01-04 23:30:37 +01:00
b90092c043 fix(core): update 2019-01-04 23:30:37 +01:00
9e1c73febf 2.0.2 2018-10-07 21:06:28 +02:00
dcf1915816 2.0.1 2018-10-07 21:05:46 +02:00
748c911168 fix(core): update 2018-10-07 21:05:45 +02:00
3a48cb4ea8 2.0.0 2018-10-07 21:02:18 +02:00
a035c5c0b0 BREAKING CHANGE(scope): change to @pushrocks 2018-10-07 21:02:17 +02:00
f9c521b7b3 1.1.4 2018-08-12 21:45:21 +02:00
19cfe8bdc5 fix(core): update 2018-08-12 21:45:21 +02:00
601d6b30d3 1.1.3 2018-08-12 20:59:56 +02:00
57ffc82c43 1.1.2 2018-08-12 20:59:00 +02:00
312d3c01cd fix(npm publishing): update 2018-08-12 20:58:59 +02:00
8814c1fc62 1.1.1 2018-08-12 01:35:14 +02:00
223a47c997 fix(core): now creating certs all right 2018-08-12 01:35:14 +02:00
651ef6d281 1.1.0 2018-08-12 00:29:02 +02:00
9eda0da9a7 feat(swaitch to acme-v2): switch to letsencrypt v2 2018-08-12 00:29:02 +02:00
3e350dfed5 1.0.11 2017-04-28 19:01:04 +02:00
6fc280e168 add updated ci config 2017-04-28 19:00:56 +02:00
a9efae65d6 1.0.10 2017-04-28 18:59:45 +02:00
0f09bdaf9f update to latest standards 2017-04-28 18:59:43 +02:00
84177cd575 update 2017-04-28 18:56:55 +02:00
7d16ada760 1.0.9 2017-01-27 01:09:48 +01:00
b4de8cc2be basic functionality 2017-01-27 01:09:38 +01:00
68e570c32a 1.0.8 2017-01-25 02:45:52 +01:00
20ea599f9d now getting a valid response 2017-01-25 02:45:48 +01:00
5fa530456b update validation 2017-01-22 21:50:04 +01:00
2cd1794e7e improve README 2017-01-15 23:19:48 +01:00
1f38e12bd3 1.0.7 2017-01-15 23:11:54 +01:00
1c777f6f05 improve README 2017-01-15 23:11:51 +01:00
aad113a8ea add async checkDNS 2017-01-15 22:59:58 +01:00
fff63839d1 1.0.6 2017-01-15 22:30:43 +01:00
c8d2cfd4ce update to new standards 2017-01-15 22:30:33 +01:00
dfd7edd330 now has working requestValidation method 2017-01-15 13:33:55 +01:00
4dadcf227c fix som things 2017-01-15 12:21:29 +01:00
fce25c60ed start better segregation of concerns 2017-01-14 18:36:33 +01:00
98cc70dbfb start with certificate signing process 2017-01-14 14:14:50 +01:00
89d628bd37 1.0.5 2017-01-02 00:18:57 +01:00
0056c8508c now getting certificates 2017-01-02 00:18:51 +01:00
96e0c4f905 can now agree to TOS 2017-01-01 21:20:12 +01:00
2f844dd78d remove test keys 2017-01-01 18:15:48 +01:00
ab82ac0c83 1.0.4 2017-01-01 18:09:48 +01:00
5b925e3d1b add npmextra.json 2017-01-01 18:09:45 +01:00
38 changed files with 3343 additions and 344 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ node_modules/
coverage/
public/
pages/
.nogit/

View File

@ -1,59 +1,150 @@
image: hosttoday/ht-docker-node:npmts
# gitzone standard
image: hosttoday/ht-docker-node:npmci
cache:
paths:
- .npmci_cache/
key: "$CI_BUILD_STAGE"
stages:
- security
- test
- release
- trigger
- pages
- metadata
testLEGACY:
stage: test
# ====================
# security stage
# ====================
mirror:
stage: security
script:
- npmci test legacy
- npmci git mirror
tags:
- docker
- docker
- notpriv
snyk:
stage: security
script:
- npmci npm prepare
- npmci command npm install -g snyk
- npmci command npm install --ignore-scripts
- npmci command snyk test
tags:
- docker
- notpriv
sast:
stage: security
image: registry.gitlab.com/hosttoday/ht-docker-dbase:npmci
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
script:
- npmci npm prepare
- npmci npm install
- npmci command npm run build
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run
--env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}"
--volume "$PWD:/code"
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
artifacts:
reports:
sast: gl-sast-report.json
tags:
- docker
- priv
# ====================
# test stage
# ====================
testLTS:
stage: test
script:
- npmci test lts
- npmci npm prepare
- npmci node install lts
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- docker
- notpriv
testSTABLE:
stage: test
script:
- npmci test stable
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- docker
- notpriv
release:
stage: release
script:
- npmci publish
- npmci node install stable
- npmci npm publish
only:
- tags
- tags
tags:
- docker
- docker
- notpriv
# ====================
# metadata stage
# ====================
codequality:
stage: metadata
image: docker:stable
allow_failure: true
services:
- docker:stable-dind
script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run
--env SOURCE_CODE="$PWD"
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
paths: [codeclimate.json]
tags:
- docker
- priv
trigger:
stage: trigger
stage: metadata
script:
- npmci trigger
- npmci trigger
only:
- tags
- tags
tags:
- docker
- docker
- notpriv
pages:
image: hosttoday/ht-docker-node:npmpage
stage: pages
image: hosttoday/ht-docker-node:npmci
stage: metadata
script:
- npmci command npmpage --host gitlab
- npmci command npm install -g typedoc typescript
- npmci npm prepare
- npmci npm install
- npmci command typedoc --module "commonjs" --target "ES2016" --out public/ ts/
tags:
- docker
- notpriv
only:
- tags
artifacts:
expire_in: 1 week
paths:
- public
- public
allow_failure: true

4
.npmignore Normal file
View File

@ -0,0 +1,4 @@
node_modules/
coverage/
public/
pages/

12
.snyk Normal file
View File

@ -0,0 +1,12 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.12.0
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
ignore:
'npm:node-forge:20180226':
- rsa-compat > node-forge:
reason: None given
expires: '2018-09-11T19:17:24.148Z'
- acme-v2 > rsa-compat > node-forge:
reason: None given
expires: '2018-09-11T19:17:24.148Z'
patch: {}

View File

@ -1,24 +1,63 @@
# smartacme
acme implementation in TypeScript
## Availabililty
[![npm](https://push.rocks/assets/repo-button-npm.svg)](https://www.npmjs.com/package/smartacme)
[![git](https://push.rocks/assets/repo-button-git.svg)](https://GitLab.com/pushrocks/smartacme)
[![git](https://push.rocks/assets/repo-button-mirror.svg)](https://github.com/pushrocks/smartacme)
[![docs](https://push.rocks/assets/repo-button-docs.svg)](https://pushrocks.gitlab.io/smartacme/)
[![npm](https://umbrellazone.gitlab.io/assets/repo-button-npm.svg)](https://www.npmjs.com/package/smartacme)
[![git](https://umbrellazone.gitlab.io/assets/repo-button-git.svg)](https://GitLab.com/umbrellazone/smartacme)
[![git](https://umbrellazone.gitlab.io/assets/repo-button-mirror.svg)](https://github.com/umbrellazone/smartacme)
[![docs](https://umbrellazone.gitlab.io/assets/repo-button-docs.svg)](https://umbrellazone.gitlab.io/smartacme/)
## Status for master
[![build status](https://GitLab.com/pushrocks/smartacme/badges/master/build.svg)](https://GitLab.com/pushrocks/smartacme/commits/master)
[![coverage report](https://GitLab.com/pushrocks/smartacme/badges/master/coverage.svg)](https://GitLab.com/pushrocks/smartacme/commits/master)
[![build status](https://GitLab.com/umbrellazone/smartacme/badges/master/build.svg)](https://GitLab.com/umbrellazone/smartacme/commits/master)
[![coverage report](https://GitLab.com/umbrellazone/smartacme/badges/master/coverage.svg)](https://GitLab.com/umbrellazone/smartacme/commits/master)
[![npm downloads per month](https://img.shields.io/npm/dm/smartacme.svg)](https://www.npmjs.com/package/smartacme)
[![Dependency Status](https://david-dm.org/pushrocks/smartacme.svg)](https://david-dm.org/pushrocks/smartacme)
[![bitHound Dependencies](https://www.bithound.io/github/pushrocks/smartacme/badges/dependencies.svg)](https://www.bithound.io/github/pushrocks/smartacme/master/dependencies/npm)
[![bitHound Code](https://www.bithound.io/github/pushrocks/smartacme/badges/code.svg)](https://www.bithound.io/github/pushrocks/smartacme)
[![Dependency Status](https://david-dm.org/umbrellazone/smartacme.svg)](https://david-dm.org/umbrellazone/smartacme)
[![bitHound Dependencies](https://www.bithound.io/github/umbrellazone/smartacme/badges/dependencies.svg)](https://www.bithound.io/github/umbrellazone/smartacme/master/dependencies/npm)
[![bitHound Code](https://www.bithound.io/github/umbrellazone/smartacme/badges/code.svg)](https://www.bithound.io/github/umbrellazone/smartacme)
[![TypeScript](https://img.shields.io/badge/TypeScript-2.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%206.x.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
## Usage
Use TypeScript for best in class instellisense.
[![npm](https://push.rocks/assets/repo-header.svg)](https://push.rocks)
```javascript
import { SmartAcme } from 'smartacme';
let smac = new SmartAcme()(async () => {
// learn async/await, it'll make your life easier
// optionally accepts a filePath Arg with a stored acmeaccount.json
// will create an account and
let myAccount = await smac.createAcmeAccount();
// will return a dnsHash to set in your DNS record
let myCert = await myAccount.createAcmeCert('example.com');
// gets and accepts the specified challenge
// first argument optional, defaults to dns-01 (which is the cleanest method for production use)
let myChallenge = await myCert.getChallenge('dns-01');
/* ----------
Now you need to set the challenge in your DNS
myChallenge.domainNamePrefixed is the address for the record
myChallenge.dnsKeyHash is the ready to use txt record value expected by letsencrypt
-------------*/
})();
```
## Other relevant npm modules
| module name | description |
| ----------- | ------------------------------------------------------------------- |
| cert | a higlevel production module that uses smartacme to manage certs |
| smartnginx | a highlevel production tool for docker environments to manage nginx |
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
> | By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy.html)
[![repo-footer](https://umbrellazone.gitlab.io/assets/repo-footer.svg)](https://umbrella.zone

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAyocFq3vvbiRNCsEcXtsqimIi6UM1OmxiYVQ0NoLvBtpaWk+E
TvNIjmUgh5QQaQfRyRGoWvhskp+E8p6go4GsbRtzx0TvL8uINTcC3SHHo6Qvl599
4QUUPUrioHdh+lX1oj+zIPVUOaL4dl0US1Ebs5vrZVbCfNXSm86vBaPIj6IkWEkj
4S5xGsYlVaQUI8Tvv2fbPziIivbkxS1v/EEMnfk6i5PWgCsnMupYxz58WaVp9xyu
+v/DMPB09mqo4DzchtUNF/b5eOWh3pDJoewYyRVMDDPJoQiTKkJn3kt64EaQuZK2
nUXcihlmaKIx5ayxirsgfvIvxidHnkQcluvciQIDAQABAoIBAQCTPUKz/3B8pMuW
C/syQyhUXzB+YawrA20q0Wr8Toi0dL7HdZP9SgXv8DmMF+suUM8F3V6GdKGKn4qq
UQT8mmPfFtw/fTBfkRs/hPUCC3L214D6PKvpkiW6wdytSN3kf+YKxUDXr0RCeuck
NltwvlDjbXHfxQm0dEefms3HzeEb+jwCyyLVLv+cDly7w7Qqq+67A6mduV/hb53p
92VFm36r7njr+1CYHq+ixV+oyUrEue7yW7w1SjZRkii3AY8Tbvk1f0lVw+XkyYf7
bQvmGSGJh1FmBi7Lytc2hKnqBLTn+iWx3S5pdPhcKTMwC/OD8p+r/DfyqThW/KVa
aaXdoY/5AoGBAO4uAcmHOhR+M/Jnue4srZJ82EkNOQy+zaFlg9KCU9R4qZ59/klH
fp0PkOw3bDFT4/1i12nm4XXqhI9Z7nsKdAoajOYpnifJVEAwQh9MlRBM7Lw+ZS0q
IcH7dvvP1XQ7E2U4C0cWUMcpWNpnmwV67gtqy0KZwk5i+WlFuugQzmhbAoGBANmu
JX6bPKUx0kBJLWhJeAxsk0OoHJ4uGihs1zxT6gl6s+AKQG4db9vU2w99lJ0nR3Aw
MLA4evSMFa5Od96W4KnoiMNHS4c5QiiVKsRSU1losWfwq0jyg406oyTh8rd0eOQn
LDOKP7nDTij8A6l0/t5a2MCu4bLQQXTedPrX+wPrAoGBAM/XO94Fb+xUGLaOR1SM
jkaHRSGyNTdnBP+zGy5GZirBxJo2rgB6MAWUgM1wq6v73bbOWtXiEJqaNGT3gEDE
ZXAvrQZoCMgFSszcj8bKSEW6Ktc1x4p6+oxRCIpC2aycpJcuKcE1uvWgohWsVT2a
AUHbRlXu4P0QJz7zB1/c0pGDAoGAbIvSVpfCXf3CAhx7cA1yt39Mz+f8nUQP9yiP
C54sjh2JpKZ4CnDTXqN9uPO+L79ueBsPrE/9wAQ6q3ilfXFvBkrWJ8pdd0iuHN6F
PPBwb50tGc+BGhcUUlBzGekxxxllTx/ZgrnlnRQu3XENwmp8zRQwEaUjFq+SdFyZ
qJwap5ECgYEA7UGxxRXAjfStTLnsrnr9svvr3QhwnZBg5JAjeR6FKC0cGFzdBrJ5
rV/Zy4mGbTBBVh5oU3MplB3AUHejuFv+8eCik2mJug8k3G8KQAk9mB8oV97k0cp+
bdlu9vlutIoCG9RXxCHdgRVLiLK+OkLv6p7hQOIY7fsIRaAuI+vPKSk=
-----END RSA PRIVATE KEY-----

View File

@ -1,9 +0,0 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyocFq3vvbiRNCsEcXtsq
imIi6UM1OmxiYVQ0NoLvBtpaWk+ETvNIjmUgh5QQaQfRyRGoWvhskp+E8p6go4Gs
bRtzx0TvL8uINTcC3SHHo6Qvl5994QUUPUrioHdh+lX1oj+zIPVUOaL4dl0US1Eb
s5vrZVbCfNXSm86vBaPIj6IkWEkj4S5xGsYlVaQUI8Tvv2fbPziIivbkxS1v/EEM
nfk6i5PWgCsnMupYxz58WaVp9xyu+v/DMPB09mqo4DzchtUNF/b5eOWh3pDJoewY
yRVMDDPJoQiTKkJn3kt64EaQuZK2nUXcihlmaKIx5ayxirsgfvIvxidHnkQcluvc
iQIDAQAB
-----END PUBLIC KEY-----

1
dist/index.d.ts vendored
View File

@ -1 +0,0 @@
export * from './smartacme.classes.smartacme';

6
dist/index.js vendored
View File

@ -1,6 +0,0 @@
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(require("./smartacme.classes.smartacme"));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBQUEsbURBQTZDIn0=

View File

@ -1,8 +0,0 @@
import 'typings-global';
export interface IRsaKeypair {
publicKey: string;
privateKey: string;
}
export declare class SmartacmeHelper {
createKeypair(bit?: number): IRsaKeypair;
}

View File

@ -1,14 +0,0 @@
"use strict";
require("typings-global");
let rsaKeygen = require('rsa-keygen');
class SmartacmeHelper {
createKeypair(bit = 2048) {
let result = rsaKeygen.generate(bit);
return {
publicKey: result.public_key,
privateKey: result.private_key
};
}
}
exports.SmartacmeHelper = SmartacmeHelper;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuaGVscGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLmNsYXNzZXMuaGVscGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSwwQkFBdUI7QUFDdkIsSUFBSSxTQUFTLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFBO0FBT3JDO0lBQ0ksYUFBYSxDQUFDLEdBQUcsR0FBRyxJQUFJO1FBQ3BCLElBQUksTUFBTSxHQUFHLFNBQVMsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUE7UUFDbkMsTUFBTSxDQUFDO1lBQ0osU0FBUyxFQUFHLE1BQU0sQ0FBQyxVQUFVO1lBQzdCLFVBQVUsRUFBRSxNQUFNLENBQUMsV0FBVztTQUNoQyxDQUFBO0lBQ04sQ0FBQztDQUNKO0FBUkQsMENBUUMifQ==

View File

@ -1,23 +0,0 @@
/// <reference types="q" />
import 'typings-global';
import * as q from 'q';
import { SmartacmeHelper, IRsaKeypair } from './smartacme.classes.helper';
/**
* class SmartAcme exports methods for maintaining SSL Certificates
*/
export declare class SmartAcme {
helper: SmartacmeHelper;
acmeUrl: string;
productionBool: boolean;
keyPair: IRsaKeypair;
JWK: any;
/**
* the constructor for class SmartAcme
*/
constructor(productionArg?: boolean);
/**
* creates an account if not currently present in module
* @executes ASYNC
*/
createAccount(): q.Promise<{}>;
}

View File

@ -1,60 +0,0 @@
"use strict";
require("typings-global");
const q = require("q");
let rsaKeygen = require('rsa-keygen');
let rawacme = require('rawacme');
const smartacme_classes_helper_1 = require("./smartacme.classes.helper");
/**
* class SmartAcme exports methods for maintaining SSL Certificates
*/
class SmartAcme {
/**
* the constructor for class SmartAcme
*/
constructor(productionArg = false) {
this.productionBool = productionArg;
this.helper = new smartacme_classes_helper_1.SmartacmeHelper();
this.keyPair = this.helper.createKeypair();
if (this.productionBool) {
this.acmeUrl = rawacme.LETSENCRYPT_STAGING_URL;
}
else {
this.acmeUrl = rawacme.LETSENCRYPT_URL;
}
}
/**
* creates an account if not currently present in module
* @executes ASYNC
*/
createAccount() {
let done = q.defer();
rawacme.createClient({
url: this.acmeUrl,
publicKey: this.keyPair.publicKey,
privateKey: this.keyPair.privateKey
}, (err, client) => {
if (err) {
console.error('smartacme: something went wrong:');
console.log(err);
done.reject(err);
return;
}
client.newReg({
contact: ['mailto:domains@lossless.org']
}, (err, res) => {
if (err) {
console.error('smartacme: something went wrong:');
console.log(err);
done.reject(err);
return;
}
this.JWK = res.body.key;
console.log(this.JWK);
done.resolve();
});
});
return done.promise;
}
}
exports.SmartAcme = SmartAcme;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSwwQkFBdUI7QUFDdkIsdUJBQXNCO0FBRXRCLElBQUksU0FBUyxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQTtBQUdyQyxJQUFJLE9BQU8sR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUE7QUFHaEMseUVBQXlFO0FBRXpFOztHQUVHO0FBQ0g7SUFPSTs7T0FFRztJQUNILFlBQVksZ0JBQXlCLEtBQUs7UUFDdEMsSUFBSSxDQUFDLGNBQWMsR0FBRyxhQUFhLENBQUE7UUFDbkMsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLDBDQUFlLEVBQUUsQ0FBQTtRQUNuQyxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxFQUFFLENBQUE7UUFDMUMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7WUFDdEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUMsdUJBQXVCLENBQUE7UUFDbEQsQ0FBQztRQUFDLElBQUksQ0FBQyxDQUFDO1lBQ0osSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUMsZUFBZSxDQUFBO1FBQzFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsYUFBYTtRQUNULElBQUksSUFBSSxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQTtRQUNwQixPQUFPLENBQUMsWUFBWSxDQUNoQjtZQUNJLEdBQUcsRUFBRSxJQUFJLENBQUMsT0FBTztZQUNqQixTQUFTLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTO1lBQ2pDLFVBQVUsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLFVBQVU7U0FDdEMsRUFDRCxDQUFDLEdBQUcsRUFBRSxNQUFNO1lBQ1IsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztnQkFDTixPQUFPLENBQUMsS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUE7Z0JBQ2pELE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQ2hCLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQ2hCLE1BQU0sQ0FBQTtZQUNWLENBQUM7WUFFRCxNQUFNLENBQUMsTUFBTSxDQUNUO2dCQUNJLE9BQU8sRUFBRSxDQUFDLDZCQUE2QixDQUFDO2FBQzNDLEVBQ0QsQ0FBQyxHQUFHLEVBQUUsR0FBRztnQkFDTCxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO29CQUNOLE9BQU8sQ0FBQyxLQUFLLENBQUMsa0NBQWtDLENBQUMsQ0FBQTtvQkFDakQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQTtvQkFDaEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQTtvQkFDaEIsTUFBTSxDQUFBO2dCQUNWLENBQUM7Z0JBQ0QsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQTtnQkFDdkIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQ3JCLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQTtZQUNsQixDQUFDLENBQUMsQ0FBQTtRQUVWLENBQUMsQ0FDSixDQUFBO1FBQ0QsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUE7SUFDdkIsQ0FBQztDQUNKO0FBN0RELDhCQTZEQyJ9

View File

@ -1,2 +0,0 @@
export declare let packageDir: string;
export declare let assetDir: string;

View File

@ -1,7 +0,0 @@
"use strict";
const path = require("path");
const smartfile = require("smartfile");
exports.packageDir = path.join(__dirname, '../');
exports.assetDir = path.join(exports.packageDir, 'assets/');
smartfile.fs.ensureDirSync(exports.assetDir);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLnBhdGhzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLnBhdGhzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSw2QkFBNEI7QUFDNUIsdUNBQXNDO0FBRTNCLFFBQUEsVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFDLEtBQUssQ0FBQyxDQUFBO0FBQ3ZDLFFBQUEsUUFBUSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsa0JBQVUsRUFBQyxTQUFTLENBQUMsQ0FBQTtBQUNyRCxTQUFTLENBQUMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxnQkFBUSxDQUFDLENBQUEifQ==

6
npmextra.json Normal file
View File

@ -0,0 +1,6 @@
{
"npmci": {
"npmGlobalTools": [],
"npmAccessLevel": "public"
}
}

2579
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,17 @@
{
"name": "smartacme",
"version": "1.0.3",
"name": "@pushrocks/smartacme",
"version": "2.0.35",
"private": false,
"description": "acme implementation in TypeScript",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"scripts": {
"test": "(npmts --nodocs)"
"test": "(tstest test/)",
"build": "(tsbuild)"
},
"repository": {
"type": "git",
"url": "git+ssh://git@gitlab.com/pushrocks/smartacme.git"
"url": "git+ssh://git@gitlab.com/umbrellazone/smartacme.git"
},
"keywords": [
"TypeScript",
@ -19,21 +21,32 @@
"author": "Lossless GmbH",
"license": "MIT",
"bugs": {
"url": "https://gitlab.com/pushrocks/smartacme/issues"
"url": "https://gitlab.com/umbrellazone/smartacme/issues"
},
"homepage": "https://gitlab.com/pushrocks/smartacme#README",
"homepage": "https://gitlab.com/umbrellazone/smartacme#README",
"dependencies": {
"@types/q": "0.x.x",
"q": "^1.4.1",
"rawacme": "^0.2.1",
"rsa-keygen": "^1.0.6",
"smartfile": "^4.1.0",
"smartstring": "^2.0.20",
"typings-global": "^1.0.14"
"@pushrocks/lik": "^3.0.4",
"@pushrocks/smartdata": "^3.1.13",
"@pushrocks/smartdelay": "^2.0.2",
"@pushrocks/smartdns": "^3.0.8",
"@pushrocks/smartexpress": "^3.0.6",
"@pushrocks/smartlog": "^2.0.11",
"@pushrocks/smartpromise": "^2.0.5",
"@pushrocks/smartrequest": "^1.1.14",
"@pushrocks/smartstring": "^3.0.8",
"@pushrocks/smarttime": "^3.0.5",
"@pushrocks/smartunique": "^3.0.1",
"acme-client": "2.2.2"
},
"devDependencies": {
"@types/should": "^8.1.30",
"should": "^11.1.1",
"typings-test": "^1.0.3"
"@gitzone/tsbuild": "^2.1.4",
"@gitzone/tsrun": "^1.1.17",
"@gitzone/tstest": "^1.0.18",
"@mojoio/cloudflare": "^2.0.0",
"@pushrocks/qenv": "^4.0.0",
"@pushrocks/tapbundle": "^3.0.7",
"@types/node": "^10.12.18",
"tslint": "^5.12.1",
"tslint-config-prettier": "^1.17.0"
}
}

6
qenv.yml Normal file
View File

@ -0,0 +1,6 @@
required:
- CF_EMAIL
- CF_KEY
- MONGODB_URL
- MONGODB_PASSWORD
- MONGODB_DATABASE

1
test/test.d.ts vendored
View File

@ -1 +0,0 @@
import 'typings-test';

View File

@ -1,26 +0,0 @@
"use strict";
require("typings-test");
const should = require("should");
// import the module to test
const smartacme = require("../dist/index");
describe('smartacme', function () {
let testAcme;
it('should create a valid instance', function () {
this.timeout(10000);
testAcme = new smartacme.SmartAcme();
should(testAcme).be.instanceOf(smartacme.SmartAcme);
});
it('should have created keyPair', function () {
should(testAcme.acmeUrl).be.of.type('string');
});
it('should register a new account', function (done) {
this.timeout(40000);
testAcme.createAccount().then(x => {
done();
}).catch(err => {
console.log(err);
done(err);
});
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHdCQUFxQjtBQUNyQixpQ0FBZ0M7QUFFaEMsNEJBQTRCO0FBQzVCLDJDQUEwQztBQUUxQyxRQUFRLENBQUMsV0FBVyxFQUFFO0lBQ2xCLElBQUksUUFBNkIsQ0FBQTtJQUNqQyxFQUFFLENBQUMsZ0NBQWdDLEVBQUU7UUFDakMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQTtRQUNuQixRQUFRLEdBQUcsSUFBSSxTQUFTLENBQUMsU0FBUyxFQUFFLENBQUE7UUFDcEMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFBO0lBQ3ZELENBQUMsQ0FBQyxDQUFBO0lBQ0YsRUFBRSxDQUFDLDZCQUE2QixFQUFFO1FBQzlCLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUE7SUFDakQsQ0FBQyxDQUFDLENBQUE7SUFDRixFQUFFLENBQUMsK0JBQStCLEVBQUUsVUFBVSxJQUFJO1FBQzlDLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUE7UUFDbkIsUUFBUSxDQUFDLGFBQWEsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQzNCLElBQUksRUFBRSxDQUFBO1FBQ1YsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUc7WUFDUixPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFBO1lBQ2hCLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQTtRQUNiLENBQUMsQ0FBQyxDQUFBO0lBQ04sQ0FBQyxDQUFDLENBQUE7QUFDTixDQUFDLENBQUMsQ0FBQSJ9

View File

@ -1,26 +1,42 @@
import 'typings-test'
import * as should from 'should'
import { tap, expect } from '@pushrocks/tapbundle';
import { Qenv } from '@pushrocks/qenv';
// import the module to test
import * as smartacme from '../dist/index'
const testQenv = new Qenv('./', './.nogit/');
describe('smartacme', function () {
let testAcme: smartacme.SmartAcme
it('should create a valid instance', function () {
this.timeout(10000)
testAcme = new smartacme.SmartAcme()
should(testAcme).be.instanceOf(smartacme.SmartAcme)
})
it('should have created keyPair', function () {
should(testAcme.acmeUrl).be.of.type('string')
})
it('should register a new account', function (done) {
this.timeout(40000)
testAcme.createAccount().then(x => {
done()
}).catch(err => {
console.log(err)
done(err)
})
})
})
import * as smartacme from '../ts/index';
let smartAcmeInstance: smartacme.SmartAcme;
tap.test('should create a valid instance of SmartAcme', async () => {
smartAcmeInstance = new smartacme.SmartAcme({
accountEmail: 'domains@lossless.org',
accountPrivateKey: null,
mongoDescriptor: {
mongoDbName: testQenv.getEnvVarRequired('MONGODB_DATABASE'),
mongoDbPass: testQenv.getEnvVarRequired('MONGODB_PASSWORD'),
mongoDbUrl: testQenv.getEnvVarRequired('MONGODB_URL')
},
removeChallenge: async (...args) => {
console.log(args);
},
setChallenge: async (...args) => {
console.log(args);
},
environment: "integration"
});
await smartAcmeInstance.init();
// await smartAcmeInstance.getCertificateForDomain('bleu.de');
});
tap.test('certmatcher should correctly match domains', async () => {
const certMatcherMod = await import('../ts/smartacme.classes.certmatcher');
const certMatcher = new certMatcherMod.CertMatcher();
const matchedCert = certMatcher.getCertificateDomainNameByDomainName('level3.level2.level1');
expect(matchedCert).to.equal('level2.level1');
});
tap.test('should stop correctly', async () => {
await smartAcmeInstance.stop();
});
tap.start();

View File

@ -1 +1,3 @@
export * from './smartacme.classes.smartacme'
export * from './smartacme.classes.smartacme';
export * from './smartacme.classes.certremoteclient';

View File

@ -0,0 +1,8 @@
export interface IAccountData {
id: number;
key: { kty: 'RSA'; n: string; e: string; kid: string };
contact: string[];
initialIp: string;
createdAt: string;
status: string;
}

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

@ -0,0 +1,10 @@
export type TCertStatus = 'existing' | 'nonexisting' | 'pending' | 'failed';
export interface ICert {
id: string;
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;
}

3
ts/interfaces/index.ts Normal file
View File

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

View File

@ -0,0 +1,40 @@
import * as plugins from './smartacme.plugins';
import * as interfaces from './interfaces';
import { CertManager } from './smartacme.classes.certmanager';
import { Collection, svDb, unI } from '@pushrocks/smartdata';
import { ICert } from './interfaces';
@plugins.smartdata.Collection(() => {
return CertManager.activeDB;
})
export class Cert extends plugins.smartdata.SmartDataDbDoc<Cert> implements interfaces.ICert {
@unI()
public id: string;
@svDb()
public domainName: string;
@svDb()
public created: number;
@svDb()
public privateKey: string;
@svDb()
public publicKey: string;
@svDb()
public csr: string;
constructor(optionsArg: ICert) {
super();
if (optionsArg) {
Object.keys(optionsArg).forEach(key => {
this[key] = optionsArg[key];
});
}
}
}

View File

@ -0,0 +1,104 @@
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 {
// =========
// STATIC
// =========
public static activeDB: plugins.smartdata.SmartdataDb;
// =========
// INSTANCE
// =========
private mongoDescriptor: plugins.smartdata.IMongoDescriptor;
public smartdataDb: plugins.smartdata.SmartdataDb;
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();
}
/**
* retrieves a certificate
* @returns the Cert class or null
* @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({
domainName
});
if(existingCertificate) {
return existingCertificate;
} else {
return null;
}
}
/**
* stores the certificate
* @param optionsArg
*/
public async storeCertificate(optionsArg: interfaces.ICert) {
const cert = new Cert(optionsArg);
await cert.save();
this.pendingMap.removeString(optionsArg.domainName);
}
public async deleteCertificate(domainNameArg: string) {
}
/**
* announce a certificate as being in the process of being retrieved
*/
public async announceCertificate (domainNameArg: string) {
this.pendingMap.addString(domainNameArg);
}
/**
* gets the status of a certificate by certDomain name
* @param certDomainArg
*/
public async getCertificateStatus(certDomainArg: string): Promise<interfaces.TCertStatus> {
const isPending = this.pendingMap.checkString(certDomainArg);
if (isPending) {
return 'pending';
}
// otherwise lets continue
const existingCertificate = await this.retrieveCertificate(certDomainArg);
if (existingCertificate) {
return 'existing';
}
return 'nonexisting';
}
/**
* checks all certs for expiration
*/
private async checkCerts() {
// TODO
};
}

View File

@ -0,0 +1,10 @@
import * as plugins from './smartacme.plugins';
export class CertMatcher {
public getCertificateDomainNameByDomainName(domainNameArg: string): string {
const originalDomain = new plugins.smartstring.Domain(domainNameArg);
if (!originalDomain.level4) {
return `${originalDomain.level2}.${originalDomain.level1}`;
}
}
}

View File

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

View File

@ -1,17 +0,0 @@
import 'typings-global'
let rsaKeygen = require('rsa-keygen')
export interface IRsaKeypair {
publicKey: string
privateKey: string
}
export class SmartacmeHelper {
createKeypair(bit = 2048): IRsaKeypair {
let result = rsaKeygen.generate(bit)
return {
publicKey: result.public_key,
privateKey: result.private_key
}
}
}

View File

@ -1,76 +1,231 @@
import 'typings-global'
import * as q from 'q'
import * as path from 'path'
let rsaKeygen = require('rsa-keygen')
import * as smartfile from 'smartfile'
import * as smartstring from 'smartstring'
let rawacme = require('rawacme')
import * as paths from './smartacme.paths'
import * as plugins from './smartacme.plugins';
import { Cert } from './smartacme.classes.cert';
import { CertManager } from './smartacme.classes.certmanager';
import { CertMatcher } from './smartacme.classes.certmatcher';
import { SmartacmeHelper, IRsaKeypair } from './smartacme.classes.helper'
import * as interfaces from './interfaces';
import { request } from 'http';
/**
* class SmartAcme exports methods for maintaining SSL Certificates
* the options for the class @see SmartAcme
*/
export interface ISmartAcmeOptions {
accountPrivateKey?: string;
accountEmail: string;
mongoDescriptor: plugins.smartdata.IMongoDescriptor;
setChallenge: (domainName: string, keyAuthorization: string) => Promise<any>;
removeChallenge: (domainName: string) => Promise<any>;
environment: 'production' | 'integration';
logger?: plugins.smartlog.Smartlog;
}
/**
* class SmartAcme
* can be used for setting up communication with an ACME authority
*
* ```ts
* const mySmartAcmeInstance = new SmartAcme({
* // see ISmartAcmeOptions for options
* })
* ```
*/
export class SmartAcme {
helper: SmartacmeHelper // bundles helper methods that would clutter the main SmartAcme class
acmeUrl: string // the acme url to use
productionBool: boolean // a boolean to quickly know wether we are in production or not
keyPair: IRsaKeypair // the keyPair needed for account creation
JWK
private options: ISmartAcmeOptions;
/**
* the constructor for class SmartAcme
*/
constructor(productionArg: boolean = false) {
this.productionBool = productionArg
this.helper = new SmartacmeHelper()
this.keyPair = this.helper.createKeypair()
if (this.productionBool) {
this.acmeUrl = rawacme.LETSENCRYPT_STAGING_URL
} else {
this.acmeUrl = rawacme.LETSENCRYPT_URL
// the acme client
private client: any;
private smartdns = new plugins.smartdns.Smartdns();
public logger: plugins.smartlog.Smartlog;
// the account private key
private privateKey: string;
// challenge fullfillment
private setChallenge: (domainName: string, keyAuthorization: string) => Promise<any>;
private removeChallenge: (domainName: string) => Promise<any>;
// certmanager
private certmanager: CertManager;
private certmatcher: CertMatcher;
/**
* the remote handler to hand the request and response to.
*/
public certremoteHandler = async (
req: plugins.smartexpress.Request,
res: plugins.smartexpress.Response
) => {
const requestBody: interfaces.ICertRemoteRequest = req.body;
this.logger.log('ok', `got certificate request for ${requestBody.domainName}`);
const certDomain = this.certmatcher.getCertificateDomainNameByDomainName(
requestBody.domainName
);
this.logger.log('ok', `mapping ${requestBody.domainName} to ${certDomain}`);
let status: interfaces.TCertStatus = await this.certmanager.getCertificateStatus(certDomain);
let response: interfaces.ICertRemoteResponse;
switch (status) {
case 'existing':
this.logger.log('ok', `certificate exists for ${certDomain}. Sending certificate!`);
response = {
status,
certificate: await (await this.certmanager.retrieveCertificate(
certDomain
)).createSavableObject()
};
break;
default:
if (status === 'nonexisting') {
this.getCertificateForDomain(certDomain);
status = 'pending';
}
response = {
status
};
break;
}
res.status(200);
res.send(response);
res.end();
}
constructor(optionsArg: ISmartAcmeOptions) {
this.options = optionsArg;
this.options.logger
? (this.logger = optionsArg.logger)
: (this.logger = plugins.smartlog.defaultLogger);
}
/**
* inits the instance
* ```ts
* await myCloudlyInstance.init() // does not support options
* ```
*/
public async init() {
this.privateKey =
this.options.accountPrivateKey || (await plugins.acme.forge.createPrivateKey());
this.setChallenge = this.options.setChallenge;
this.removeChallenge = this.options.removeChallenge;
// CertMangaer
this.certmanager = new CertManager(this, {
mongoDescriptor: this.options.mongoDescriptor
});
await this.certmanager.init();
// CertMatcher
this.certmatcher = new CertMatcher();
// ACME Client
this.client = new plugins.acme.Client({
directoryUrl: (() => {
if (this.options.environment === 'production') {
return plugins.acme.directory.letsencrypt.production;
} else {
return plugins.acme.directory.letsencrypt.staging;
}
})(),
accountKey: this.privateKey
});
/* Register account */
await this.client.createAccount({
termsOfServiceAgreed: true,
contact: [`mailto:${this.options.accountEmail}`]
});
}
public async stop() {
await this.certmanager.smartdataDb.close();
}
/**
* gets a certificate
* it runs through the following steps
*
* * look in the database
* * if in the database return it
* * of not in the database announce it
* * then get it from letsencrypt
* * store it
* * remove it from the pending map (which it go onto by announcing it)
* * retrieve it from the databse and return it
*
* @param domainArg
*/
public async getCertificateForDomain(domainArg: string): Promise<Cert> {
const certDomain = this.certmatcher.getCertificateDomainNameByDomainName(domainArg);
const retrievedCertificate = await this.certmanager.retrieveCertificate(certDomain);
if (retrievedCertificate) {
return retrievedCertificate;
} else {
await this.certmanager.announceCertificate(certDomain);
}
/**
* creates an account if not currently present in module
* @executes ASYNC
*/
createAccount() {
let done = q.defer()
rawacme.createClient(
{
url: this.acmeUrl,
publicKey: this.keyPair.publicKey,
privateKey: this.keyPair.privateKey
},
(err, client) => {
if (err) {
console.error('smartacme: something went wrong:')
console.log(err)
done.reject(err)
return
}
/* Place new order */
const order = await this.client.createOrder({
identifiers: [{ type: 'dns', value: certDomain }, { type: 'dns', value: `*.${certDomain}` }]
});
client.newReg(
{
contact: ['mailto:domains@lossless.org']
},
(err, res) => {
if (err) {
console.error('smartacme: something went wrong:')
console.log(err)
done.reject(err)
return
}
this.JWK = res.body.key
console.log(this.JWK)
done.resolve()
})
/* Get authorizations and select challenges */
const authorizations = await this.client.getAuthorizations(order);
}
)
return done.promise
for (const authz of authorizations) {
console.log(authz);
const domainDnsName: string = `_acme-challenge.${authz.identifier.value}`;
const dnsChallenge: string = authz.challenges.find(challengeArg => {
return challengeArg.type === 'dns-01';
});
// process.exit(1);
const keyAuthorization: string = await this.client.getChallengeKeyAuthorization(dnsChallenge);
try {
/* Satisfy challenge */
await this.setChallenge(domainDnsName, keyAuthorization);
await this.smartdns.checkUntilAvailable(domainDnsName, 'TXT', keyAuthorization, 100, 5000);
console.log('Cool down an extra 60 second for region availability');
await plugins.smartdelay.delayFor(60000);
/* Verify that challenge is satisfied */
await this.client.verifyChallenge(authz, dnsChallenge);
/* Notify ACME provider that challenge is satisfied */
await this.client.completeChallenge(dnsChallenge);
/* Wait for ACME provider to respond with valid status */
await this.client.waitForValidStatus(dnsChallenge);
} finally {
/* Clean up challenge response */
try {
await this.removeChallenge(domainDnsName);
} catch (e) {
console.log(e);
}
}
}
/* Finalize order */
const [key, csr] = await plugins.acme.forge.createCsr({
commonName: `*.${certDomain}`,
altNames: [certDomain]
});
await this.client.finalizeOrder(order, csr);
const cert = await this.client.getCertificate(order);
/* Done */
await this.certmanager.storeCertificate({
id: plugins.smartunique.shortId(),
domainName: certDomain,
privateKey: key.toString(),
publicKey: cert.toString(),
csr: csr.toString(),
created: Date.now()
});
const newCertificate = await this.certmanager.retrieveCertificate(certDomain);
return newCertificate;
}
}

View File

@ -1,6 +0,0 @@
import * as path from 'path'
import * as smartfile from 'smartfile'
export let packageDir = path.join(__dirname,'../')
export let assetDir = path.join(packageDir,'assets/')
smartfile.fs.ensureDirSync(assetDir)

19
ts/smartacme.plugins.ts Normal file
View File

@ -0,0 +1,19 @@
// @pushrocks scope
import * as lik from '@pushrocks/lik';
import * as smartdata from '@pushrocks/smartdata';
import * as smartdelay from '@pushrocks/smartdelay';
import * as smartdns from '@pushrocks/smartdns';
import * as smartexpress from '@pushrocks/smartexpress';
import * as smartlog from '@pushrocks/smartlog';
import * as smartpromise from '@pushrocks/smartpromise';
import * as smartrequest from '@pushrocks/smartrequest';
import * as smartunique from '@pushrocks/smartunique';
import * as smartstring from '@pushrocks/smartstring';
import * as smarttime from '@pushrocks/smarttime';
export { lik, smartdata, smartdelay, smartdns, smartexpress, smartlog, smartpromise, smartrequest, smartunique, smartstring, smarttime };
// thirs party scope
import * as acme from 'acme-client';
export { acme };

View File

7
tsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"target": "es2017",
"module": "commonjs"
}
}

View File

@ -1,3 +1,17 @@
{
"extends": "tslint-config-standard"
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"semicolon": [true, "always"],
"no-console": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"member-ordering": {
"options":{
"order": [
"static-method"
]
}
}
},
"defaultSeverity": "warning"
}