| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  | /*! | 
					
						
							|  |  |  |  * letiny | 
					
						
							|  |  |  |  * Copyright(c) 2015 Anatol Sommer <anatol@anatol.at> | 
					
						
							|  |  |  |  * Some code used from https://github.com/letsencrypt/boulder/tree/master/test/js
 | 
					
						
							|  |  |  |  * MPL 2.0 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  | module.exports.create = function (deps) { | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |   var NOOP=function () {}; | 
					
						
							|  |  |  |   var log=NOOP; | 
					
						
							|  |  |  |   var request=require('request'); | 
					
						
							| 
									
										
										
										
											2016-08-01 05:53:50 -04:00
										 |  |  |   var generateSignature=deps.RSA.signJws; | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |   function Acme(privateKey) { | 
					
						
							|  |  |  |     this.privateKey=privateKey; | 
					
						
							|  |  |  |     this.nonces=[]; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |   Acme.prototype.getNonce=function(url, cb) { | 
					
						
							|  |  |  |     var self=this; | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |     request.head({ | 
					
						
							|  |  |  |       url:url, | 
					
						
							|  |  |  |     }, function(err, res/*, body*/) { | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  |       if (err) { | 
					
						
							|  |  |  |         return cb(err); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |       if (res && 'replay-nonce' in res.headers) { | 
					
						
							|  |  |  |         log('Storing nonce: '+res.headers['replay-nonce']); | 
					
						
							|  |  |  |         self.nonces.push(res.headers['replay-nonce']); | 
					
						
							|  |  |  |         cb(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       cb(new Error('Failed to get nonce for request')); | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |   Acme.prototype.post=function(url, body, cb) { | 
					
						
							|  |  |  |     var self=this, payload, jws, signed; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (this.nonces.length===0) { | 
					
						
							|  |  |  |       this.getNonce(url, function(err) { | 
					
						
							|  |  |  |         if (err) { | 
					
						
							|  |  |  |           return cb(err); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         self.post(url, body, cb); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |     log('Using nonce: '+this.nonces[0]); | 
					
						
							|  |  |  |     payload=JSON.stringify(body, null, 2); | 
					
						
							|  |  |  |     jws=generateSignature( | 
					
						
							| 
									
										
										
										
											2016-08-01 05:53:50 -04:00
										 |  |  |         this.privateKey, new Buffer(payload), this.nonces.shift() | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |     ); | 
					
						
							|  |  |  |     signed=JSON.stringify(jws, null, 2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     log('Posting to '+url); | 
					
						
							|  |  |  |     log(signed.green); | 
					
						
							|  |  |  |     log('Payload:'+payload.blue); | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |     return request.post({ | 
					
						
							|  |  |  |       url:url, | 
					
						
							|  |  |  |       body:signed, | 
					
						
							|  |  |  |       encoding:null | 
					
						
							|  |  |  |     }, function(err, res, body) { | 
					
						
							|  |  |  |       var parsed; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (err) { | 
					
						
							| 
									
										
										
										
											2016-02-10 15:41:48 -05:00
										 |  |  |         console.error('[letiny-core/lib/acme-client.js] post'); | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |         console.error(err.stack); | 
					
						
							|  |  |  |         return cb(err); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (res) { | 
					
						
							|  |  |  |         log(('HTTP/1.1 '+res.statusCode).yellow); | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |       Object.keys(res.headers).forEach(function(key) { | 
					
						
							|  |  |  |         var value, upcased; | 
					
						
							|  |  |  |         value=res.headers[key]; | 
					
						
							|  |  |  |         upcased=key.charAt(0).toUpperCase()+key.slice(1); | 
					
						
							|  |  |  |         log((upcased+': '+value).yellow); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (body && !body.toString().match(/[^\x00-\x7F]/)) { | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |           parsed=JSON.parse(body); | 
					
						
							|  |  |  |           log(JSON.stringify(parsed, null, 2).cyan); | 
					
						
							|  |  |  |         } catch(err) { | 
					
						
							|  |  |  |           log(body.toString().cyan); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |       if ('replay-nonce' in res.headers) { | 
					
						
							|  |  |  |         log('Storing nonce: '+res.headers['replay-nonce']); | 
					
						
							|  |  |  |         self.nonces.push(res.headers['replay-nonce']); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |       cb(err, res, body); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Acme.parseLink = function parseLink(link) { | 
					
						
							|  |  |  |     var links; | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       links=link.split(',').map(function(link) { | 
					
						
							|  |  |  |         var parts, url, info; | 
					
						
							|  |  |  |         parts=link.trim().split(';'); | 
					
						
							|  |  |  |         url=parts.shift().replace(/[<>]/g, ''); | 
					
						
							|  |  |  |         info=parts.reduce(function(acc, p) { | 
					
						
							|  |  |  |           var m=p.trim().match(/(.+) *= *"(.+)"/); | 
					
						
							|  |  |  |           if (m) { | 
					
						
							|  |  |  |             acc[m[1]]=m[2]; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return acc; | 
					
						
							|  |  |  |         }, {}); | 
					
						
							|  |  |  |         info.url=url; | 
					
						
							|  |  |  |         return info; | 
					
						
							|  |  |  |       }).reduce(function(acc, link) { | 
					
						
							|  |  |  |         if ('rel' in link) { | 
					
						
							|  |  |  |           acc[link.rel]=link.url; | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         return acc; | 
					
						
							|  |  |  |       }, {}); | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |       return links; | 
					
						
							|  |  |  |     } catch(err) { | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2015-12-15 14:33:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-15 22:07:02 +00:00
										 |  |  |   return Acme; | 
					
						
							|  |  |  | }; |