Compare commits

..

22 Commits

Author SHA1 Message Date
AJ ONeal 2fd9678ab5 v2.1.4: update links and urls 2018-11-20 14:39:54 -07:00
AJ ONeal 7a6c2ae573 v2.1.3 2018-05-12 16:54:19 -06:00
AJ ONeal 4758dc2bd2 remove unused package 2018-05-12 16:53:44 -06:00
AJ ONeal d28d82130c Update 'README.md' 2018-04-18 16:16:50 +00:00
AJ ONeal 3a41c3006c update rsa-compat 2018-03-21 15:21:53 -06:00
AJ ONeal bfe1737b9b Update 'README.md' 2018-01-12 02:43:04 +00:00
Tim Caswell 9172d4c98e Bump version to 2.1.1 2017-06-28 12:52:22 -05:00
Tim Caswell 530b25f691 Merge branch 'remove-url-warning' into 'master'
Remove warning about unknown keys in le urls

See merge request !6
2017-06-28 09:59:08 -06:00
Tim Caswell d85f4070f3 Remove warning about unknown keys in le urls 2017-06-27 12:34:52 -05:00
tigerbot 51bcc1f20a v2.1.0 2017-04-26 18:50:21 -06:00
tigerbot f79c62032c Merge remote-tracking branch 'MaitreyaBuddha/master' into master 2017-04-26 16:55:55 -06:00
tigerbot 3ed2d45d3d Merge remote-tracking branch 'MaitreyaBuddha/userAgent' into master 2017-04-14 13:16:30 -06:00
tigerbot d0e20a44cd renamed httpsOptions to tlsOptions 2017-04-10 14:21:09 -06:00
Kelly Johnson 10978ab99a https://git.daplie.com/Daplie/le-acme-core/issues/17
Add User-Agent header to request object used by le-acme-core.
Expose options to add/remove information from header
Add mocha tests
2017-03-11 13:49:12 -08:00
Kelly Johnson 72fb7b7c07 https://git.daplie.com/Daplie/le-acme-core/issues/20
example/letsencrypt.js now “works” up until the point of not having a server to give proper challenge response
2017-03-11 11:36:07 -08:00
Drew Warren 72646ced80 v2.0.9 2017-01-17 13:50:35 -07:00
Drew Warren 9f01021948 Change GitHub to GitLab 2017-01-17 13:49:59 -07:00
Drew Warren f63070ce54 v2.0.8 2017-01-17 12:49:45 -07:00
Drew Warren 6126222e8f Merge branch 'dev' into 'master'
Separate handling errors

Closes #16

See merge request !2
2017-01-17 19:09:40 +00:00
Drew Warren 7288d14fac Separate handling errors
Prevent res.body from being called if res is not defined.
Close #16
2017-01-17 12:01:02 -07:00
AJ ONeal 681c0edc71 Merge branch 'master' into 'master'
Add key-change URL support

See merge request !1
2017-01-16 23:02:15 +00:00
Rodrigo López Dato f350ae44c1
Add key-change URL support
Fixes warning when retrieving LE URLs on staging or prod
2017-01-12 19:37:34 -03:00
16 changed files with 537 additions and 58 deletions

2
.gitignore vendored
View File

@ -29,3 +29,5 @@ build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
.idea
.DS_Store

View File

@ -1,3 +1,3 @@
ISRG
Anatol Sommer <anatol@anatol.at>
AJ ONeal <aj@daplie.com> (https://daplie.com/)
AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)

334
README.md
View File

@ -1,5 +1,333 @@
le-acme-core
=====
# le-acme-core
Moved to https://git.daplie.com/Daplie/le-acme-core
Looking for **letiny-core**? Check the [v1.x branch](https://git.coolaj86.com/coolaj86/le-acme-core.js/tree/v1.x).
<!-- rename to le-acme-core -->
A framework for building letsencrypt clients, forked from `letiny`.
Supports all of:
* node with `ursa` (works fast)
* node with `forge` (works on windows)
* browser WebCrypto (not implemented, but... Let's Encrypt over WebRTC anyone?)
* any javascript implementation
# NEW: Let's Encrypt v2 Support
Let's Encrypt v2 (aka ACME v2 or ACME draft 11) is available in [acme-v2.js](https://git.coolaj86.com/coolaj86/acme-v2.js)
### These aren't the droids you're looking for
This is a library / framework for building letsencrypt clients.
You probably want one of these pre-built clients instead:
* [`letsencrypt`](https://git.coolaj86.com/coolaj86/greenlock.js) (compatible with the official client)
* `letiny` (lightweight client cli)
* [`letsencrypt-express`](https://git.coolaj86.com/coolaj86/greenlock-express.js) (automatic https for express)
## Install & Usage:
```bash
npm install --save le-acme-core
```
To use the default dependencies:
```javascript
'use strict';
var ACME = require('le-acme-core').ACME.create();
```
For **testing** and **development**, you can also inject the dependencies you want to use:
```javascript
'use strict';
var ACME = require('le-acme-core').ACME.create({
, RSA: require('rsa-compat').RSA
});
ACME.getAcmeUrls(discoveryUrl, function (err, urls) {
console.log(urls);
});
```
You will follow these steps to obtain certificates:
* discover ACME registration urls with `getAcmeUrls`
* register a user account with `registerNewAccount`
* implement a method to agree to the terms of service as `agreeToTos`
* get certificates with `getCertificate`
* implement a method to store the challenge token as `setChallenge`
* implement a method to get the challenge token as `getChallenge`
* implement a method to remove the challenge token as `removeChallenge`
### Demo
You can see this working for yourself, but you'll need to be on an internet connected computer with a domain.
Get a temporary domain for testing
```bash
npm install -g ddns-cli
ddns --random --email user@example.com --agree
```
Note: use **YOUR EMAIL** and accept the terms of service (run `ddns --help` to see them).
<!-- TODO tutorial on ddns -->
Install le-acme-core and its dependencies. **Note**: it's okay if you're on windows
and `ursa` fails to compile. It'll still work.
```bash
git clone https://git.coolaj86.com/coolaj86/le-acme-core.js.git ~/le-acme-core
pushd ~/le-acme-core
npm install
```
Run the demo:
```bash
node examples/letsencrypt.js user@example.com example.com
```
Note: use **YOUR TEMPORARY DOMAIN** and **YOUR EMAIL**.
## API
The Goodies
```javascript
// Accounts
ACME.registerNewAccount(options, cb) // returns "regr" registration data
{ newRegUrl: '<url>' // no defaults, specify acmeUrls.newAuthz
, email: '<email>' // valid email (server checks MX records)
, accountKeypair: { // privateKeyPem or privateKeyJwt
privateKeyPem: '<ASCII PEM>'
}
, agreeToTerms: fn (tosUrl, cb) {} // must specify agree=tosUrl to continue (or falsey to end)
}
// Registration
ACME.getCertificate(options, cb) // returns (err, pems={ privkey (key), cert, chain (ca) })
{ newAuthzUrl: '<url>' // specify acmeUrls.newAuthz
, newCertUrl: '<url>' // specify acmeUrls.newCert
, domainKeypair: {
privateKeyPem: '<ASCII PEM>'
}
, accountKeypair: {
privateKeyPem: '<ASCII PEM>'
}
, domains: ['example.com']
, setChallenge: fn (hostname, key, val, cb)
, removeChallenge: fn (hostname, key, cb)
}
// Discovery URLs
ACME.getAcmeUrls(acmeDiscoveryUrl, cb) // returns (err, acmeUrls={newReg,newAuthz,newCert,revokeCert})
```
Helpers & Stuff
```javascript
// Constants
ACME.productionServerUrl // https://acme-v01.api.letsencrypt.org/directory
ACME.stagingServerUrl // https://acme-staging.api.letsencrypt.org/directory
ACME.acmeChallengePrefix // /.well-known/acme-challenge/
ACME.knownEndpoints // new-authz, new-cert, new-reg, revoke-cert
// HTTP Client Helpers
ACME.Acme // Signs requests with JWK
acme = new Acme(keypair) // 'keypair' is an object with `privateKeyPem` and/or `privateKeyJwk`
acme.post(url, body, cb) // POST with signature
acme.parseLinks(link) // (internal) parses 'link' header
acme.getNonce(url, cb) // (internal) HEAD request to get 'replay-nonce' strings
```
## Example
Below you'll find a stripped-down example. You can see the full example in the example folder.
* [example/](https://git.coolaj86.com/coolaj86/le-acme-core.js/blob/master/example/)
#### Register Account & Domain
This is how you **register an ACME account** and **get an HTTPS certificate**
```javascript
'use strict';
var ACME = require('le-acme-core').ACME.create();
var RSA = require('rsa-compat').RSA;
var email = 'user@example.com'; // CHANGE TO YOUR EMAIL
var domains = 'example.com'; // CHANGE TO YOUR DOMAIN
var acmeDiscoveryUrl = ACME.stagingServerUrl; // CHANGE to production, when ready
var accountKeypair = null; // { privateKeyPem: null, privateKeyJwk: null };
var domainKeypair = null; // same as above
var acmeUrls = null;
RSA.generateKeypair(2048, 65537, function (err, keypair) {
accountKeypair = keypair;
// ...
ACME.getAcmeUrls(acmeDiscoveryUrl, function (err, urls) {
// ...
runDemo();
});
});
function runDemo() {
ACME.registerNewAccount(
{ newRegUrl: acmeUrls.newReg
, email: email
, accountKeypair: accountKeypair
, agreeToTerms: function (tosUrl, done) {
// agree to the exact version of these terms
done(null, tosUrl);
}
}
, function (err, regr) {
ACME.getCertificate(
{ newAuthzUrl: acmeUrls.newAuthz
, newCertUrl: acmeUrls.newCert
, domainKeypair: domainKeypair
, accountKeypair: accountKeypair
, domains: domains
, setChallenge: challengeStore.set
, removeChallenge: challengeStore.remove
}
, function (err, certs) {
// Note: you should save certs to disk (or db)
certStore.set(domains[0], certs, function () {
// ...
});
}
);
}
);
}
```
**But wait**, there's more!
See [example/letsencrypt.js](https://git.coolaj86.com/coolaj86/le-acme-core.js/blob/master/example/letsencrypt.js)
#### Run a Server on 80, 443, and 5001 (https/tls)
That will fail unless you have a webserver running on 80 and 443 (or 5001)
to respond to `/.well-known/acme-challenge/xxxxxxxx` with the proper token
```javascript
var https = require('https');
var http = require('http');
var LeCore = deps.LeCore;
var tlsOptions = deps.tlsOptions;
var challengeStore = deps.challengeStore;
var certStore = deps.certStore;
//
// Challenge Handler
//
function acmeResponder(req, res) {
if (0 !== req.url.indexOf(LeCore.acmeChallengePrefix)) {
res.end('Hello World!');
return;
}
var key = req.url.slice(LeCore.acmeChallengePrefix.length);
challengeStore.get(req.hostname, key, function (err, val) {
res.end(val || 'Error');
});
}
//
// Server
//
https.createServer(tlsOptions, acmeResponder).listen(5001, function () {
console.log('Listening https on', this.address());
});
http.createServer(acmeResponder).listen(80, function () {
console.log('Listening http on', this.address());
});
```
**But wait**, there's more!
See [example/serve.js](https://git.coolaj86.com/coolaj86/le-acme-core.js/blob/master/example/serve.js)
#### Put some storage in place
Finally, you need an implementation of `challengeStore`:
```javascript
var challengeCache = {};
var challengeStore = {
set: function (hostname, key, value, cb) {
challengeCache[key] = value;
cb(null);
}
, get: function (hostname, key, cb) {
cb(null, challengeCache[key]);
}
, remove: function (hostname, key, cb) {
delete challengeCache[key];
cb(null);
}
};
var certCache = {};
var certStore = {
set: function (hostname, certs, cb) {
certCache[hostname] = certs;
cb(null);
}
, get: function (hostname, cb) {
cb(null, certCache[hostname]);
}
, remove: function (hostname, cb) {
delete certCache[hostname];
cb(null);
}
};
```
**But wait**, there's more!
See
* [example/challenge-store.js](https://git.coolaj86.com/coolaj86/le-acme-core.js/blob/master/challenge-store.js)
* [example/cert-store.js](https://git.coolaj86.com/coolaj86/le-acme-core.js/blob/master/cert-store.js)
## Authors
* ISRG
* Anatol Sommer (https://github.com/anatolsommer)
* AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com)
## Licence
MPL 2.0
All of the code is available under the MPL-2.0.
Some of the files are original work not modified from `letiny`
and are made available under MIT and Apache-2.0 as well (check file headers).

View File

@ -1,6 +1,6 @@
/*!
* letiny-core
* Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
* Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
* Apache-2.0 OR MIT (and hence also MPL 2.0)
*/
'use strict';

View File

@ -1,6 +1,6 @@
/*!
* letiny-core
* Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
* Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
* Apache-2.0 OR MIT (and hence also MPL 2.0)
*/
'use strict';

View File

@ -1,12 +1,12 @@
/*!
* letiny-core
* Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
* Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
* Apache-2.0 OR MIT (and hence also MPL 2.0)
*/
'use strict';
//var LeCore = require('letiny-core');
var LeCore = require('../');
var LeCore = require('../').ACME.create();
var email = process.argv[2] || 'user@example.com'; // CHANGE TO YOUR EMAIL
var domains = [process.argv[3] || 'example.com']; // CHANGE TO YOUR DOMAIN
@ -17,8 +17,8 @@ var certStore = require('./cert-store');
var serve = require('./serve');
var closer;
var accountPrivateKeyPem = null;
var domainPrivateKeyPem = null;
var accountKeypair = null;
var domainKeypair = null;
var acmeUrls = null;
@ -44,14 +44,14 @@ function init() {
function getPrivateKeys(cb) {
console.log('Generating Account Keypair');
console.log("(Note: if you're using forge and not ursa, this will take a long time");
LeCore.leCrypto.generateRsaKeypair(2048, 65537, function (err, pems) {
const RSA = require('rsa-compat').RSA;
RSA.generateKeypair(2048, 65537, {}, function (err, pems) {
accountPrivateKeyPem = pems.privateKeyPem;
accountKeypair = pems;
console.log('Generating Domain Keypair');
LeCore.leCrypto.generateRsaKeypair(2048, 65537, function (err, pems) {
RSA.generateKeypair(2048, 65537, {}, function (err, pems2) {
domainPrivateKeyPem = pems.privateKeyPem;
domainKeypair = pems2;
cb();
});
});
@ -62,7 +62,7 @@ function runDemo() {
LeCore.registerNewAccount(
{ newRegUrl: acmeUrls.newReg
, email: email
, accountPrivateKeyPem: accountPrivateKeyPem
, accountKeypair: accountKeypair
, agreeToTerms: function (tosUrl, done) {
// agree to the exact version of these terms
@ -82,8 +82,8 @@ function runDemo() {
{ newAuthzUrl: acmeUrls.newAuthz
, newCertUrl: acmeUrls.newCert
, domainPrivateKeyPem: domainPrivateKeyPem
, accountPrivateKeyPem: accountPrivateKeyPem
, domainKeypair: domainKeypair
, accountKeypair: accountKeypair
, domains: domains
, setChallenge: challengeStore.set
@ -99,7 +99,7 @@ function runDemo() {
closer();
});
}
);
}
@ -111,8 +111,7 @@ function runDemo() {
//
closer = serve.init({
LeCore: LeCore
// needs a default key and cert chain, anything will do
, httpsOptions: require('localhost.daplie.com-certificates')
, tlsOptions: {}
, challengeStore: challengeStore
, certStore: certStore
, certStore: certStore
});

View File

@ -1,6 +1,6 @@
/*!
* letiny-core
* Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
* Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
* Apache-2.0 OR MIT (and hence also MPL 2.0)
*/
'use strict';
@ -15,7 +15,7 @@ module.exports.init = function (deps) {
var LeCore = deps.LeCore;
var httpsOptions = deps.httpsOptions;
var tlsOptions = deps.tlsOptions || deps.httpsOptions;
var challengeStore = deps.challengeStore;
var certStore = deps.certStore;
@ -63,11 +63,11 @@ module.exports.init = function (deps) {
//
// Server
//
httpsOptions.SNICallback = certGetter;
https.createServer(httpsOptions, acmeResponder).listen(443, function () {
tlsOptions.SNICallback = certGetter;
https.createServer(tlsOptions, acmeResponder).listen(443, function () {
console.log('Listening https on', this.address());
});
https.createServer(httpsOptions, acmeResponder).listen(5001, function () {
https.createServer(tlsOptions, acmeResponder).listen(5001, function () {
console.log('Listening https on', this.address());
});
http.createServer(acmeResponder).listen(80, function () {
@ -77,6 +77,6 @@ module.exports.init = function (deps) {
return function () {
// Note: we should just keep a handle on
// the servers and close them each with server.close()
process.exit(1);
process.exit(1);
};
};

View File

@ -11,7 +11,7 @@ module.exports.create = function (deps) {
var NOOP = function () {
};
var log = NOOP;
var request = deps.request || require('request');
var acmeRequest = deps.acmeRequest;
var RSA = deps.RSA;
var generateSignature = RSA.signJws;
@ -30,7 +30,7 @@ module.exports.create = function (deps) {
Acme.prototype.getNonce=function(url, cb) {
var self=this;
request.head({
acmeRequest.create().head({
url:url,
}, function(err, res/*, body*/) {
if (err) {
@ -73,7 +73,7 @@ module.exports.create = function (deps) {
//process.exit(1);
//return;
return request.post({
return acmeRequest.create().post({
url: url
, body: signed
, encoding: null

View File

@ -1,12 +1,12 @@
/*!
* letiny-core
* Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
* Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
* Apache-2.0 OR MIT (and hence also MPL 2.0)
*/
'use strict';
module.exports.create = function (deps) {
var request = deps.request;
var acmeRequest = deps.acmeRequest;
var knownUrls = deps.LeCore.knownEndpoints;
function getAcmeUrls(acmeDiscoveryUrl, cb) {
@ -15,7 +15,7 @@ module.exports.create = function (deps) {
}
// TODO check response header on request for cache time
return request({
return acmeRequest.create()({
url: acmeDiscoveryUrl
, encoding: 'utf8'
}, function (err, resp) {
@ -39,11 +39,6 @@ module.exports.create = function (deps) {
}
}
if (4 !== Object.keys(data).length) {
console.warn("This Let's Encrypt / ACME server has been updated with urls that this client doesn't understand");
console.warn(data);
}
if (!knownUrls.every(function (url) {
return data[url];
})) {
@ -56,6 +51,7 @@ module.exports.create = function (deps) {
, newCert: data['new-cert']
, newReg: data['new-reg']
, revokeCert: data['revoke-cert']
, keyChange: data['key-change']
});
});
}

View File

@ -24,7 +24,7 @@ function certBufferToPem(cert) {
}
module.exports.create = function (deps) {
var request = deps.request;
var acmeRequest = deps.acmeRequest;
var Acme = deps.Acme;
var RSA = deps.RSA;
@ -193,7 +193,7 @@ module.exports.create = function (deps) {
if (authz.status==='pending') {
setTimeout(function() {
request({
acmeRequest.create()({
method: 'GET'
, url: state.authorizationUrl
}, function(err, res, body) {
@ -278,7 +278,7 @@ module.exports.create = function (deps) {
state.certificate=body;
certUrl=res.headers.location;
request({
acmeRequest.create()({
method: 'GET'
, url: certUrl
, encoding: null
@ -310,7 +310,7 @@ module.exports.create = function (deps) {
function downloadIssuerCert(links) {
log('Requesting issuer certificate...');
request({
acmeRequest.create()({
method: 'GET'
, url: links.up
, encoding: null

72
lib/le-acme-request.js Normal file
View File

@ -0,0 +1,72 @@
/*!
* le-acme-core
* Author: Kelly Johnson
* Copyright 2017
* Apache-2.0 OR MIT (and hence also MPL 2.0)
*/
'use strict';
const request = require('request');
const pkgJSON = require('../package.json');
const version = pkgJSON.version;
const os = require('os');
const uaDefaults = {
pkg: `Greenlock/${version}`
, os: ` (${os.type()}; ${process.arch} ${os.platform()} ${os.release()})`
, node: ` Node.js/${process.version}`
, user: ''
}
let currentUAProps;
function getUaString() {
let userAgent = '';
for (let key in currentUAProps) {
userAgent += currentUAProps[key];
}
return userAgent.trim();
}
function getRequest() {
return request.defaults({
headers: {
'User-Agent': getUaString()
}
});
}
function resetUa() {
currentUAProps = {};
for (let key in uaDefaults) {
currentUAProps[key] = uaDefaults[key];
}
}
function addUaString(string) {
currentUAProps.user += ` ${string}`;
}
function omitUaProperties(opts) {
if (opts.all) {
currentUAProps = {};
} else {
for (let key in opts) {
currentUAProps[key] = '';
}
}
}
// Set our UA to begin with
resetUa();
module.exports = {
create: function create() {
// get deps and modify here if need be
return getRequest();
}
, addUaString: addUaString
, omitUaProperties: omitUaProperties
, resetUa: resetUa
, getUaString: getUaString
};

View File

@ -8,7 +8,7 @@
'use strict';
module.exports.create = function (deps) {
var NOOP=function () {}, log=NOOP;
var request=deps.request;
var acmeRequest = deps.acmeRequest;
var RSA = deps.RSA;
var Acme = deps.Acme;
@ -24,7 +24,11 @@ module.exports.create = function (deps) {
function getTerms(err, res) {
var links;
if (err || Math.floor(res.statusCode/100)!==2) {
if (err) {
return handleErr(err, 'Registration request failed: ' + err.toString());
}
if (Math.floor(res.statusCode/100)!==2) {
return handleErr(err, 'Registration request failed: ' + res.body.toString('utf8'));
}
@ -51,7 +55,7 @@ module.exports.create = function (deps) {
state.agreeTerms = agree;
state.termsUrl=links['terms-of-service'];
log(state.termsUrl);
request.get(state.termsUrl, getAgreement);
acmeRequest.create().get(state.termsUrl, getAgreement);
});
} else {
cb(null, null);

View File

@ -1,6 +1,6 @@
/*!
* letiny-core
* Copyright(c) 2015 AJ ONeal <aj@daplie.com> https://daplie.com
* Copyright(c) 2015 AJ ONeal <coolaj86@gmail.com> https://coolaj86.com
* Apache-2.0 OR MIT (and hence also MPL 2.0)
*/
'use strict';
@ -9,7 +9,7 @@ var defaults = {
productionServerUrl: 'https://acme-v01.api.letsencrypt.org/directory'
, stagingServerUrl: 'https://acme-staging.api.letsencrypt.org/directory'
, acmeChallengePrefix: '/.well-known/acme-challenge/'
, knownEndpoints: [ 'new-authz', 'new-cert', 'new-reg', 'revoke-cert' ]
, knownEndpoints: [ 'new-authz', 'new-cert', 'new-reg', 'revoke-cert', 'key-change' ]
, challengeType: 'http-01'
, rsaKeySize: 2048
};
@ -24,10 +24,11 @@ function create(deps) {
});
deps.RSA = deps.RSA || require('rsa-compat').RSA;
deps.request = deps.request || require('request');
deps.acmeRequest = require('./lib/le-acme-request');
deps.Acme = require('./lib/acme-client').create(deps);
deps.LeCore.Acme = deps.Acme;
deps.LeCore.acmeRequest = deps.acmeRequest;
deps.LeCore.getAcmeUrls = require('./lib/get-acme-urls').create(deps);
deps.LeCore.registerNewAccount = require('./lib/register-new-account').create(deps);
deps.LeCore.getCertificate = require('./lib/get-certificate').create(deps);

View File

@ -1,6 +1,6 @@
{
"name": "le-acme-core",
"version": "2.0.7",
"version": "2.1.4",
"description": "A framework for building letsencrypt clients, forked from letiny",
"main": "node.js",
"browser": "browser.js",
@ -8,18 +8,15 @@
"example": "example",
"test": "test"
},
"scripts": {
"test": "node example/letsencrypt.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Daplie/le-acme-core.git"
"url": "git+https://git.coolaj86.com/coolaj86/le-acme-core.js.git"
},
"license": "MPL-2.0",
"bugs": {
"url": "https://github.com/Daplie/le-acme-core/issues"
"url": "https://git.coolaj86.com/coolaj86/le-acme-core.js/issues"
},
"homepage": "https://github.com/Daplie/le-acme-core#readme",
"homepage": "https://git.coolaj86.com/coolaj86/le-acme-core.js#readme",
"keywords": [
"le-acme",
"le-acme-",
@ -33,6 +30,12 @@
],
"dependencies": {
"request": "^2.74.0",
"rsa-compat": "^1.2.7"
"rsa-compat": "^1.3.2"
},
"devDependencies": {
"better-assert": "^1.0.2",
"chai": "^3.5.0",
"chai-string": "^1.3.0",
"request-debug": "^0.2.0"
}
}

74
test/test-request.js Normal file
View File

@ -0,0 +1,74 @@
/*!
* le-acme-core
* Author: Kelly Johnson
* Copyright 2017
* Apache-2.0 OR MIT (and hence also MPL 2.0)
*/
'use strict';
const acmeRequest = require('../lib/le-acme-request');
const debugRequest = require('request-debug');
const chai = require('chai');
chai.use(require('chai-string'));
const expect = chai.expect;
const productId = 'Greenlock';
const UA = 'User-Agent';
function checkRequest(req, done, tester) {
debugRequest(req, function dbg(type, data, r) {
if (type !== 'request') return; // Only interested in the request
expect(data.headers).to.have.property(UA);
let uaString = data.headers[UA];
tester(uaString);
req.stopDebugging();
done();
});
req('http://www.google.com', function (error, response, body) {
});
}
describe('le-acme-request', function () {
beforeEach(function () {
acmeRequest.resetUa();
});
it('should build User-Agent string', function () {
let uaString = acmeRequest.getUaString();
expect(uaString).to.startsWith(productId);
});
it('should have proper User-Agent in request', function (done) {
let request = acmeRequest.create();
checkRequest(request, done, function (uaString) {
expect(uaString).to.startsWith(productId);
});
});
it('should add custom string to User Agent', function (done) {
let testStr = 'check it';
acmeRequest.addUaString(testStr);
let request = acmeRequest.create();
checkRequest(request, done, function (uaString) {
// Added space to ensure str was properly appended
expect(uaString).to.endsWith(` ${testStr}`);
});
});
it('should remove all items from User Agent', function (done) {
acmeRequest.omitUaProperties({all: true});
let request = acmeRequest.create();
checkRequest(request, done, function (uaString) {
expect(uaString).to.be.empty;
});
});
it('should remove one item from User Agent', function (done) {
acmeRequest.omitUaProperties({pkg: true});
const request = acmeRequest.create();
checkRequest(request, done, function (uaString) {
expect(uaString).to.not.have.string(productId);
});
});
});

View File

@ -1,5 +1,5 @@
var forge=require('node-forge'), assert=require('better-assert'), fs=require('fs'),
letiny=require('../lib/client'), config=require('./config.json'),
letiny=require('../'), config=require('./config.json'),
res, newReg='https://acme-staging.api.letsencrypt.org/acme/new-reg';
config.newReg=config.newReg || newReg;