259 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			259 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | 'use strict'; | ||
|  | 
 | ||
|  | var U = require('./utils.js'); | ||
|  | var E = require('./errors.js'); | ||
|  | 
 | ||
|  | var warned = {}; | ||
|  | 
 | ||
|  | module.exports.wrap = function(greenlock, manager) { | ||
|  | 	greenlock.manager = {}; | ||
|  | 	greenlock.sites = {}; | ||
|  | 	//greenlock.accounts = {};
 | ||
|  | 	//greenlock.certs = {};
 | ||
|  | 
 | ||
|  | 	var allowed = [ | ||
|  | 		'accountKeyType', //: ["P-256", "RSA-2048"],
 | ||
|  | 		'serverKeyType', //: ["RSA-2048", "P-256"],
 | ||
|  | 		'store', // : { module, specific opts },
 | ||
|  | 		'challenges', // : { "http-01", "dns-01", "tls-alpn-01" },
 | ||
|  | 		'subscriberEmail', | ||
|  | 		'agreeToTerms', | ||
|  | 		'agreeTos', | ||
|  | 		'customerEmail', | ||
|  | 		'renewOffset', | ||
|  | 		'renewStagger', | ||
|  | 		'module', // not allowed, just ignored
 | ||
|  | 		'manager' | ||
|  | 	]; | ||
|  | 
 | ||
|  | 	// get / set default site settings such as
 | ||
|  | 	// subscriberEmail, store, challenges, renewOffset, renewStagger
 | ||
|  | 	greenlock.manager.defaults = function(conf) { | ||
|  | 		return greenlock._init().then(function() { | ||
|  | 			if (!conf) { | ||
|  | 				return manager.defaults(); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (conf.sites) { | ||
|  | 				throw new Error('cannot set sites as global config'); | ||
|  | 			} | ||
|  | 			if (conf.routes) { | ||
|  | 				throw new Error('cannot set routes as global config'); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// disallow keys we know to be bad
 | ||
|  | 			[ | ||
|  | 				'subject', | ||
|  | 				'deletedAt', | ||
|  | 				'altnames', | ||
|  | 				'lastAttemptAt', | ||
|  | 				'expiresAt', | ||
|  | 				'issuedAt', | ||
|  | 				'renewAt', | ||
|  | 				'sites', | ||
|  | 				'routes' | ||
|  | 			].some(function(k) { | ||
|  | 				if (k in conf) { | ||
|  | 					throw new Error( | ||
|  | 						'`' + k + '` not allowed as a default setting' | ||
|  | 					); | ||
|  | 				} | ||
|  | 			}); | ||
|  | 			Object.keys(conf).forEach(function(k) { | ||
|  | 				if (!allowed.includes(k) && !warned[k]) { | ||
|  | 					warned[k] = true; | ||
|  | 					console.warn( | ||
|  | 						k + | ||
|  | 							" isn't a known key. Please open an issue and let us know the use case." | ||
|  | 					); | ||
|  | 				} | ||
|  | 			}); | ||
|  | 
 | ||
|  | 			Object.keys(conf).forEach(function(k) { | ||
|  | 				if (-1 !== ['module', 'manager'].indexOf(k)) { | ||
|  | 					return; | ||
|  | 				} | ||
|  | 
 | ||
|  | 				if ('undefined' === typeof k) { | ||
|  | 					throw new Error( | ||
|  | 						"'" + | ||
|  | 							k + | ||
|  | 							"' should be set to a value, or `null`, but not left `undefined`" | ||
|  | 					); | ||
|  | 				} | ||
|  | 			}); | ||
|  | 
 | ||
|  | 			return manager.defaults(conf); | ||
|  | 		}); | ||
|  | 	}; | ||
|  | 
 | ||
|  | 	greenlock.add = greenlock.manager.add = function(args) { | ||
|  | 		if (!args || !Array.isArray(args.altnames) || !args.altnames.length) { | ||
|  | 			throw new Error( | ||
|  | 				'you must specify `altnames` when adding a new site' | ||
|  | 			); | ||
|  | 		} | ||
|  | 		if (args.renewAt) { | ||
|  | 			throw new Error( | ||
|  | 				'you cannot specify `renewAt` when adding a new site' | ||
|  | 			); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return greenlock.manager.set(args); | ||
|  | 	}; | ||
|  | 
 | ||
|  | 	// TODO agreeToTerms should be handled somewhere... maybe?
 | ||
|  | 
 | ||
|  | 	// Add and update remains because I said I had locked the API
 | ||
|  | 	greenlock.manager.set = greenlock.manager.update = function(args) { | ||
|  | 		return greenlock._init().then(function() { | ||
|  | 			// The goal is to make this decently easy to manage by hand without mistakes
 | ||
|  | 			// but also reasonably easy to error check and correct
 | ||
|  | 			// and to make deterministic auto-corrections
 | ||
|  | 
 | ||
|  | 			args.subject = checkSubject(args); | ||
|  | 
 | ||
|  | 			//var subscriberEmail = args.subscriberEmail;
 | ||
|  | 
 | ||
|  | 			// TODO shortcut the other array checks when not necessary
 | ||
|  | 			if (Array.isArray(args.altnames)) { | ||
|  | 				args.altnames = checkAltnames(args.subject, args); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// at this point we know that subject is the first of altnames
 | ||
|  | 			return Promise.all( | ||
|  | 				(args.altnames || []).map(function(d) { | ||
|  | 					d = d.replace('*.', ''); | ||
|  | 					return U._validDomain(d); | ||
|  | 				}) | ||
|  | 			).then(function() { | ||
|  | 				if (!U._uniqueNames(args.altnames || [])) { | ||
|  | 					throw E.NOT_UNIQUE( | ||
|  | 						'add', | ||
|  | 						"'" + args.altnames.join("' '") + "'" | ||
|  | 					); | ||
|  | 				} | ||
|  | 
 | ||
|  | 				// durations
 | ||
|  | 				if (args.renewOffset) { | ||
|  | 					args.renewOffset = U._parseDuration(args.renewOffset); | ||
|  | 				} | ||
|  | 				if (args.renewStagger) { | ||
|  | 					args.renewStagger = U._parseDuration(args.renewStagger); | ||
|  | 				} | ||
|  | 
 | ||
|  | 				return manager.set(args).then(function(result) { | ||
|  | 					greenlock.renew({}).catch(function(err) { | ||
|  | 						if (!err.context) { | ||
|  | 							err.contxt = 'renew'; | ||
|  | 						} | ||
|  | 						greenlock._notify('error', err); | ||
|  | 					}); | ||
|  | 					return result; | ||
|  | 				}); | ||
|  | 			}); | ||
|  | 		}); | ||
|  | 	}; | ||
|  | 
 | ||
|  | 	greenlock.manager.remove = function(args) { | ||
|  | 		args.subject = checkSubject(args); | ||
|  | 		// TODO check no altnames
 | ||
|  | 		return manager.remove(args); | ||
|  | 	}; | ||
|  | 
 | ||
|  | 	/* | ||
|  |   { | ||
|  |     subject: site.subject, | ||
|  |     altnames: site.altnames, | ||
|  |     //issuedAt: site.issuedAt,
 | ||
|  |     //expiresAt: site.expiresAt,
 | ||
|  |     renewOffset: site.renewOffset, | ||
|  |     renewStagger: site.renewStagger, | ||
|  |     renewAt: site.renewAt, | ||
|  |     subscriberEmail: site.subscriberEmail, | ||
|  |     customerEmail: site.customerEmail, | ||
|  |     challenges: site.challenges, | ||
|  |     store: site.store | ||
|  |   }; | ||
|  |   */ | ||
|  | 
 | ||
|  | 	greenlock._find = function(args) { | ||
|  | 		var altnames = args.altnames || []; | ||
|  | 
 | ||
|  | 		// servername, wildname, and altnames are all the same
 | ||
|  | 		['wildname', 'servername'].forEach(function(k) { | ||
|  | 			var altname = args[k] || ''; | ||
|  | 			if (altname && !altnames.includes(altname)) { | ||
|  | 				altnames.push(altname); | ||
|  | 			} | ||
|  | 		}); | ||
|  | 
 | ||
|  | 		if (altnames.length) { | ||
|  | 			args.altnames = altnames; | ||
|  | 			args.altnames = args.altnames.map(U._encodeName); | ||
|  | 			args.altnames = checkAltnames(false, args); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return manager.find(args); | ||
|  | 	}; | ||
|  | }; | ||
|  | 
 | ||
|  | function checkSubject(args) { | ||
|  | 	if (!args || !args.subject) { | ||
|  | 		throw new Error('you must specify `subject` when configuring a site'); | ||
|  | 	} | ||
|  | 	/* | ||
|  | 		if (!args.subject) { | ||
|  | 			throw E.NO_SUBJECT('add'); | ||
|  | 		} | ||
|  |     */ | ||
|  | 
 | ||
|  | 	var subject = (args.subject || '').toLowerCase(); | ||
|  | 	if (subject !== args.subject) { | ||
|  | 		console.warn('`subject` must be lowercase', args.subject); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return U._encodeName(subject); | ||
|  | } | ||
|  | 
 | ||
|  | function checkAltnames(subject, args) { | ||
|  | 	// the things we have to check and get right
 | ||
|  | 	var altnames = (args.altnames || []).map(function(name) { | ||
|  | 		return String(name || '').toLowerCase(); | ||
|  | 	}); | ||
|  | 
 | ||
|  | 	if (subject && subject !== altnames[0]) { | ||
|  | 		throw new Error( | ||
|  | 			'`subject` must be the first domain in `altnames`', | ||
|  | 			args.subject, | ||
|  | 			altnames.join(' ') | ||
|  | 		); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* | ||
|  | 		if (args.subject !== args.altnames[0]) { | ||
|  | 			throw E.BAD_ORDER( | ||
|  | 				'add', | ||
|  | 				'(' + args.subject + ") '" + args.altnames.join("' '") + "'" | ||
|  | 			); | ||
|  | 		} | ||
|  |   */ | ||
|  | 
 | ||
|  | 	// punycode BEFORE validation
 | ||
|  | 	// (set, find, remove)
 | ||
|  | 	args.altnames = args.altnames.map(U._encodeName); | ||
|  | 	if ( | ||
|  | 		!args.altnames.every(function(d) { | ||
|  | 			return U._validName(d); | ||
|  | 		}) | ||
|  | 	) { | ||
|  | 		throw E.INVALID_HOSTNAME('add', "'" + args.altnames.join("' '") + "'"); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (altnames.join() !== args.altnames.join()) { | ||
|  | 		console.warn('all domains in `altnames` must be lowercase', altnames); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return altnames; | ||
|  | } |