diff --git a/lib/allowed_email_domains.js b/lib/allowed_email_domains.js deleted file mode 100644 index 8a1f2bc..0000000 --- a/lib/allowed_email_domains.js +++ /dev/null @@ -1,68 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -module.exports = function (config, mc, log) { - - var pollInterval = null - - function AllowedEmailDomains(domains) { - this.setAll(domains) - } - - AllowedEmailDomains.prototype.isAllowed = function (email) { - var match = /^.+@(.+)$/.exec(email) - return match ? this.domains[match[1]] : false - } - - AllowedEmailDomains.prototype.setAll = function (domains) { - this.domains = {} - for (var i = 0; i < domains.length; i++) { - this.domains[domains[i]] = true - } - return Object.keys(this.domains) - } - - AllowedEmailDomains.prototype.push = function () { - log.info({ op: 'allowedEmailDomains.push' }) - return mc.setAsync('allowedEmailDomains', Object.keys(this.domains), 0) - .then(this.refresh.bind(this)) - } - - AllowedEmailDomains.prototype.refresh = function (options) { - log.info({ op: 'allowedEmailDomains.refresh' }) - var result = mc.getAsync('allowedEmailDomains').then(validate) - - if (options && options.pushOnMissing) { - result = result.catch(this.push.bind(this)) - } - - return result.then( - this.setAll.bind(this), - function (err) { - log.error({ op: 'allowedEmailDomains.refresh', err: err }) - } - ) - } - - AllowedEmailDomains.prototype.pollForUpdates = function () { - this.stopPolling() - pollInterval = setInterval(this.refresh.bind(this), config.updatePollIntervalSeconds * 1000) - pollInterval.unref() - } - - AllowedEmailDomains.prototype.stopPolling = function () { - clearInterval(pollInterval) - } - - function validate(domains) { - if (!Array.isArray(domains)) { - log.error({ op: 'allowedEmailDomains.validate.invalid', data: domains }) - throw new Error('invalid allowedEmailDomains from memcache') - } - return domains - } - - var allowedEmailDomains = new AllowedEmailDomains(config.allowedEmailDomains || []) - return allowedEmailDomains -} diff --git a/lib/allowed_ips.js b/lib/allowed_ips.js deleted file mode 100644 index 0f20218..0000000 --- a/lib/allowed_ips.js +++ /dev/null @@ -1,70 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -var net = require('net') - -module.exports = function (config, mc, log) { - - var pollInterval = null - - function AllowedIPs(ips) { - this.setAll(ips) - } - - AllowedIPs.prototype.setAll = function (ips) { - this.ips = {} - for (var i = 0; i < ips.length; i++) { - this.ips[ips[i]] = true - } - return Object.keys(this.ips) - } - - AllowedIPs.prototype.push = function () { - log.info({ op: 'allowedIPs.push' }) - return mc.setAsync('allowedIPs', Object.keys(this.ips), 0) - .then(this.refresh.bind(this)) - } - - AllowedIPs.prototype.refresh = function (options) { - log.info({ op: 'allowedIPs.refresh' }) - var result = mc.getAsync('allowedIPs').then(validate) - - if (options && options.pushOnMissing) { - result = result.catch(this.push.bind(this)) - } - - return result.then( - this.setAll.bind(this), - function (err) { - log.error({ op: 'allowedIPs.refresh', err: err }) - } - ) - } - - AllowedIPs.prototype.pollForUpdates = function () { - this.stopPolling() - pollInterval = setInterval(this.refresh.bind(this), config.updatePollIntervalSeconds * 1000) - pollInterval.unref() - } - - AllowedIPs.prototype.stopPolling = function () { - clearInterval(pollInterval) - } - - function validate(ips) { - if (!Array.isArray(ips)) { - log.error({ op: 'allowedIPs.validate.invalid', data: ips }) - throw new Error('invalid allowedIPs from memcache') - } - return ips.filter(function (ip) { - var is = net.isIPv4(ip) - if (!is) { - log.error({ op: 'allowedIPs.validate.err', ip: ip }) - } - return is - }) - } - - var allowedIPs = new AllowedIPs(config.allowedIPs || []) - return allowedIPs -} diff --git a/lib/limits.js b/lib/limits.js deleted file mode 100644 index a8d02e0..0000000 --- a/lib/limits.js +++ /dev/null @@ -1,99 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var deepEqual = require('deep-equal') - -module.exports = function (config, mc, log) { - - var pollInterval = null - - function Limits(settings) { - this.setAll(settings) - } - - Limits.prototype.setAll = function (settings) { - this.blockIntervalSeconds = settings.blockIntervalSeconds - this.blockIntervalMs = settings.blockIntervalSeconds * 1000 - this.rateLimitIntervalSeconds = settings.rateLimitIntervalSeconds - this.rateLimitIntervalMs = settings.rateLimitIntervalSeconds * 1000 - this.maxEmails = settings.maxEmails - this.maxBadLogins = settings.maxBadLogins - this.maxBadLoginsPerIp = settings.maxBadLoginsPerIp - this.maxUnblockAttempts = settings.maxUnblockAttempts - this.maxVerifyCodes = settings.maxVerifyCodes - this.ipRateLimitIntervalSeconds = settings.ipRateLimitIntervalSeconds - this.ipRateLimitIntervalMs = settings.ipRateLimitIntervalSeconds * 1000 - this.ipRateLimitBanDurationSeconds = settings.ipRateLimitBanDurationSeconds - this.ipRateLimitBanDurationMs = settings.ipRateLimitBanDurationSeconds * 1000 - this.maxAccountStatusCheck = settings.maxAccountStatusCheck - this.badLoginErrnoWeights = settings.badLoginErrnoWeights || {} - this.uidRateLimit = settings.uidRateLimit || {} - this.maxChecksPerUid = this.uidRateLimit.maxChecks - this.uidRateLimitBanDurationMs = this.uidRateLimit.banDurationSeconds * 1000 - this.uidRateLimitIntervalMs = this.uidRateLimit.limitIntervalSeconds * 1000 - this.smsRateLimit = settings.smsRateLimit || {} - this.maxSms = settings.smsRateLimit.maxSms - this.smsRateLimitIntervalSeconds = this.smsRateLimit.limitIntervalSeconds - this.smsRateLimitIntervalMs = this.smsRateLimitIntervalSeconds * 1000 - - return this - } - - Limits.prototype.push = function () { - log.info({ op: 'limits.push' }) - return mc.setAsync('limits', this, 0) - .then(this.refresh.bind(this)) - } - - Limits.prototype.refresh = function (options) { - log.info({ op: 'limits.refresh' }) - var result = mc.getAsync('limits').then(validate) - - if (options && options.pushOnMissing) { - result = result.catch(this.push.bind(this)) - } - - return result.then( - this.setAll.bind(this), - function (err) { - log.error({ op: 'limits.refresh', err: err }) - } - ) - } - - Limits.prototype.pollForUpdates = function () { - this.stopPolling() - pollInterval = setInterval(this.refresh.bind(this), config.updatePollIntervalSeconds * 1000) - pollInterval.unref() - } - - Limits.prototype.stopPolling = function () { - clearInterval(pollInterval) - } - - var limits = new Limits(config.limits) - - function validate(settings) { - if (typeof(settings) !== 'object') { - log.error({ op: 'limits.validate.invalid', data: settings }) - throw new Error('invalid limits from memcache') - } - var keys = Object.keys(config.limits) - for (var i = 0; i < keys.length; i++) { - var key = keys[i] - var current = limits[key] - var future = settings[key] - if (typeof(current) !== typeof(future)) { - log.error({ op: 'limits.validate.err', key: key, message: 'types do not match'}) - settings[key] = current - } - else if (!deepEqual(current, future)) { - log.info({ op: 'limits.validate.changed', key: key, current: current, future: future }) - } - } - return settings - } - - return limits -} diff --git a/lib/requestChecks.js b/lib/requestChecks.js deleted file mode 100644 index 6391f0e..0000000 --- a/lib/requestChecks.js +++ /dev/null @@ -1,82 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var deepEqual = require('deep-equal') - -module.exports = function (config, mc, log) { - - var requestChecks = null - var pollInterval = null - - function RequestChecks(settings) { - this.setAll(settings) - } - - RequestChecks.prototype.setAll = function (settings) { - this.treatEveryoneWithSuspicion = settings.treatEveryoneWithSuspicion - // The private branch puts some additional private config here. - return this - } - - RequestChecks.prototype.push = function () { - log.info({ op: 'requestChecks.push' }) - return mc.setAsync('requestChecks', this, 0) - .then(this.refresh.bind(this)) - } - - RequestChecks.prototype.refresh = function (options) { - log.info({ op: 'requestChecks.refresh' }) - var result = mc.getAsync('requestChecks').then(validateAndMerge) - - if (options && options.pushOnMissing) { - result = result.catch(this.push.bind(this)) - } - - return result.then( - this.setAll.bind(this), - function (err) { - log.error({ op: 'requestChecks.refresh', err: err }) - } - ) - } - - RequestChecks.prototype.pollForUpdates = function () { - this.stopPolling() - pollInterval = setInterval(this.refresh.bind(this), config.updatePollIntervalSeconds * 1000) - pollInterval.unref() - } - - RequestChecks.prototype.stopPolling = function () { - clearInterval(pollInterval) - } - - // Type-checks updates to the settings, and merges them - // with the current values, modying its argument in-place. - function validateAndMerge(settings) { - if (typeof(settings) !== 'object') { - log.error({ op: 'requestChecks.validate.invalid', data: settings }) - throw new Error('invalid requestChecks from memcache') - } - var keys = Object.keys(config.requestChecks) - for (var i = 0; i < keys.length; i++) { - var key = keys[i] - var current = requestChecks[key] - var future = settings[key] - if (typeof(future) === 'undefined') { - settings[key] = current - } - else if (typeof(current) !== typeof(future)) { - log.error({ op: 'requestChecks.validate.err', key: key, message: 'types do not match' }) - settings[key] = current - } - else if (!deepEqual(current, future)) { - log.info({ op: 'requestChecks.validate.changed', key: key, current: current, future: future }) - } - } - return settings - } - - requestChecks = new RequestChecks(config.requestChecks) - return requestChecks -} diff --git a/lib/server.js b/lib/server.js index 3015ea7..71e6a15 100755 --- a/lib/server.js +++ b/lib/server.js @@ -4,6 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict' + var Memcached = require('memcached') var restify = require('restify') var safeJsonFormatter = require('restify-safe-json-formatter') @@ -40,20 +42,22 @@ module.exports = function createServer(config, log) { ) var reputationService = require('./reputationService')(config, log) - var limits = require('./limits')(config, mc, log) - var allowedIPs = require('./allowed_ips')(config, mc, log) - var allowedEmailDomains = require('./allowed_email_domains')(config, mc, log) - var requestChecks = require('./requestChecks')(config, mc, log) + const Settings = require('./settings/settings')(config, mc, log) + var limits = require('./settings/limits')(config, Settings, log) + var allowedIPs = require('./settings/allowed_ips')(config, Settings, log) + var allowedEmailDomains = require('./settings/allowed_email_domains')(config, Settings, log) + var requestChecks = require('./settings/requestChecks')(config, Settings, log) if (config.updatePollIntervalSeconds) { - limits.refresh({ pushOnMissing: true }) - limits.pollForUpdates() - allowedIPs.refresh({ pushOnMissing: true }) - allowedIPs.pollForUpdates() - allowedEmailDomains.refresh({ pushOnMissing: true }) - allowedEmailDomains.pollForUpdates() - requestChecks.refresh({ pushOnMissing: true }) - requestChecks.pollForUpdates() + [ + allowedEmailDomains, + allowedIPs, + limits, + requestChecks + ].forEach(settings => { + settings.refresh({ pushOnMissing: true }).catch(() => {}) + settings.pollForUpdates() + }) } var IpEmailRecord = require('./ip_email_record')(limits) diff --git a/lib/settings/allowed_email_domains.js b/lib/settings/allowed_email_domains.js new file mode 100644 index 0000000..7ee89cb --- /dev/null +++ b/lib/settings/allowed_email_domains.js @@ -0,0 +1,43 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict' + +module.exports = (config, Settings, log) => { + + class AllowedEmailDomains extends Settings { + constructor(domains) { + super('allowedEmailDomains') + this.setAll(domains) + } + + isAllowed(email) { + var match = /^.+@(.+)$/.exec(email) + return match ? this.domains[match[1]] : false + } + + setAll(domains) { + this.domains = {} + for (var i = 0; i < domains.length; i++) { + this.domains[domains[i]] = true + } + return Object.keys(this.domains) + } + + validate(domains) { + if (!Array.isArray(domains)) { + log.error({ op: 'allowedEmailDomains.validate.invalid', data: domains }) + throw new Settings.Missing('invalid allowedEmailDomains from memcache') + } + return domains + } + + toJSON() { + return Object.keys(this.domains) + } + + } + + return new AllowedEmailDomains(config.allowedEmailDomains || []) +} diff --git a/lib/settings/allowed_ips.js b/lib/settings/allowed_ips.js new file mode 100644 index 0000000..6e62582 --- /dev/null +++ b/lib/settings/allowed_ips.js @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict' + +const net = require('net') + +module.exports = (config, Settings, log) => { + + class AllowedIPs extends Settings { + constructor(ips) { + super('allowedIPs') + this.setAll(ips) + } + + setAll(ips) { + this.ips = {} + for (var i = 0; i < ips.length; i++) { + this.ips[ips[i]] = true + } + return Object.keys(this.ips) + } + + validate(ips) { + if (!Array.isArray(ips)) { + log.error({ op: 'allowedIPs.validate.invalid', data: ips }) + throw new Settings.Missing('invalid allowedIPs from memcache') + } + return ips.filter(function (ip) { + var is = net.isIPv4(ip) + if (!is) { + log.error({ op: 'allowedIPs.validate.err', ip: ip }) + } + return is + }) + } + + toJSON() { + return Object.keys(this.ips) + } + } + + return new AllowedIPs(config.allowedIPs || []) +} diff --git a/lib/settings/limits.js b/lib/settings/limits.js new file mode 100644 index 0000000..bcbbf23 --- /dev/null +++ b/lib/settings/limits.js @@ -0,0 +1,70 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict' + +var deepEqual = require('deep-equal') + +module.exports = (config, Settings, log) => { + + class Limits extends Settings { + constructor(settings) { + super('limits') + this.setAll(settings) + } + + setAll(settings) { + this.blockIntervalSeconds = settings.blockIntervalSeconds + this.blockIntervalMs = settings.blockIntervalSeconds * 1000 + this.rateLimitIntervalSeconds = settings.rateLimitIntervalSeconds + this.rateLimitIntervalMs = settings.rateLimitIntervalSeconds * 1000 + this.maxEmails = settings.maxEmails + this.maxBadLogins = settings.maxBadLogins + this.maxBadLoginsPerIp = settings.maxBadLoginsPerIp + this.maxUnblockAttempts = settings.maxUnblockAttempts + this.maxVerifyCodes = settings.maxVerifyCodes + this.ipRateLimitIntervalSeconds = settings.ipRateLimitIntervalSeconds + this.ipRateLimitIntervalMs = settings.ipRateLimitIntervalSeconds * 1000 + this.ipRateLimitBanDurationSeconds = settings.ipRateLimitBanDurationSeconds + this.ipRateLimitBanDurationMs = settings.ipRateLimitBanDurationSeconds * 1000 + this.maxAccountStatusCheck = settings.maxAccountStatusCheck + this.badLoginErrnoWeights = settings.badLoginErrnoWeights || {} + this.uidRateLimit = settings.uidRateLimit || {} + this.maxChecksPerUid = this.uidRateLimit.maxChecks + this.uidRateLimitBanDurationMs = this.uidRateLimit.banDurationSeconds * 1000 + this.uidRateLimitIntervalMs = this.uidRateLimit.limitIntervalSeconds * 1000 + this.smsRateLimit = settings.smsRateLimit || {} + this.maxSms = settings.smsRateLimit.maxSms + this.smsRateLimitIntervalSeconds = this.smsRateLimit.limitIntervalSeconds + this.smsRateLimitIntervalMs = this.smsRateLimitIntervalSeconds * 1000 + + return this + } + + validate(settings) { + if (typeof settings !== 'object') { + log.error({ op: 'limits.validate.invalid', data: settings }) + throw new Settings.Missing('invalid limits from memcache') + } + var keys = Object.keys(config.limits) + for (var i = 0; i < keys.length; i++) { + var key = keys[i] + var current = this[key] + var future = settings[key] + if (typeof(current) !== typeof(future)) { + log.error({ op: 'limits.validate.err', key: key, message: 'types do not match'}) + settings[key] = current + } + else if (!deepEqual(current, future)) { + log.info({ op: 'limits.validate.changed', key: key, current: current, future: future }) + } + } + return settings + } + + } + + + return new Limits(config.limits) +} diff --git a/lib/settings/requestChecks.js b/lib/settings/requestChecks.js new file mode 100644 index 0000000..c57d39a --- /dev/null +++ b/lib/settings/requestChecks.js @@ -0,0 +1,52 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict' + +const deepEqual = require('deep-equal') + +module.exports = (config, Settings, log) => { + + class RequestChecks extends Settings { + constructor(settings) { + super('requestChecks') + this.setAll(settings) + } + + setAll(settings) { + this.treatEveryoneWithSuspicion = settings.treatEveryoneWithSuspicion + // The private branch puts some additional private config here. + return this + } + + // Type-checks updates to the settings, and merges them + // with the current values, modying its argument in-place. + validate(settings) { + if (typeof settings !== 'object') { + log.error({ op: 'requestChecks.validate.invalid', data: settings }) + throw new Settings.Missing('invalid requestChecks from memcache') + } + const keys = Object.keys(config.requestChecks) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + const current = this[key] + const future = settings[key] + if (typeof future === 'undefined') { + settings[key] = current + } + else if (typeof current !== typeof future) { + log.error({ op: 'requestChecks.validate.err', key: key, message: 'types do not match' }) + settings[key] = current + } + else if (!deepEqual(current, future)) { + log.info({ op: 'requestChecks.validate.changed', key: key, current: current, future: future }) + } + } + return settings + } + + } + + return new RequestChecks(config.requestChecks) +} diff --git a/lib/settings/settings.js b/lib/settings/settings.js new file mode 100644 index 0000000..4c30e25 --- /dev/null +++ b/lib/settings/settings.js @@ -0,0 +1,91 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict' + +const assert = require('assert') + +const KEY = Symbol() +const POLL_INTERVAL = Symbol() + +module.exports = (config, mc, log) => { + + // A sentinel error for signaling that the result was invalid/missing, + // and we should try to push our own representation to memcached. + class Missing extends Error {} + + // An abstract class that stores options in memcached. + // + // Others extend this class to hotload options from memcached. + class Settings { + + constructor(key) { + assert(typeof key === 'string') + this[KEY] = key + } + + // subclasses should provide their own implementation + setAll(value) { + return this + } + + // subclasses should provide their own implementation + validate(value) { + if (value == null) { + throw new Missing('value was undefined or null') + } + return value + } + + // Pushes `this` as JSON to `key` + // + // Customize what is pushed with a toJSON method. + push() { + log.info({ op: this[KEY] + '.push' }) + return mc.setAsync(this[KEY], this, 0) + .then(() => this.refresh()) + } + + refresh(options) { + log.info({ op: this[KEY] + '.refresh' }) + let result = mc.getAsync(this[KEY]).then(value => this.validate(value)) + + if (options && options.pushOnMissing) { + result = result.catch(err => { + if (err instanceof Missing) { + log.info({ op: this[KEY] + '.refresh.pushOnMissing' }) + return this.push() + } else { + throw err + } + }) + } + return result.then( + value => this.setAll(value), + err => { + log.error({ op: this[KEY] + '.refresh', err: err }) + throw err + } + ) + } + + pollForUpdates() { + this.stopPolling() + this[POLL_INTERVAL] = setInterval(() => { + this.refresh() + // refresh error should just log, but nothing else + .catch(() => {}) + }, config.updatePollIntervalSeconds * 1000) + this[POLL_INTERVAL].unref() + } + + stopPolling() { + clearInterval(this[POLL_INTERVAL]) + } + } + + Settings.Missing = Missing + + return Settings +} diff --git a/test/local/settings_tests.js b/test/local/settings_tests.js new file mode 100644 index 0000000..f27f438 --- /dev/null +++ b/test/local/settings_tests.js @@ -0,0 +1,93 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +'use strict' + +const P = require('bluebird') +const test = require('tap').test + +const config = {} +const mc = {} +const log = { + info() {}, + error() {} +} +const Settings = require('../../lib/settings/settings')(config, mc, log) + +class TestSettings extends Settings { + constructor() { + super('tests') + } + + setAll(settings) { + this.testOption = !!settings.testOption + return this + } + + validate(other) { + if (!other) { + throw new Settings.Missing() + } + return other + } +} + +test( + 'refresh without pushOnMissing does not call push', + t => { + let pushed + mc.getAsync = () => P.resolve(pushed) + mc.setAsync = (key, val) => { + pushed = val + return P.resolve(val) + } + const settings = new TestSettings() + settings.setAll({ testOption: true }) + return settings.refresh() + .then( + t.fail, + err => { + t.equal(pushed, undefined) + t.ok(err instanceof Settings.Missing) + } + ) + } +) + +test( + 'refresh pushOnMissing works on Missing error', + t => { + let pushed + mc.getAsync = () => P.resolve(pushed) + mc.setAsync = (key, val) => { + pushed = val + return P.resolve(val) + } + const settings = new TestSettings() + settings.setAll({ testOption: true }) + return settings.refresh({ pushOnMissing: true }) + .then(() => { + t.deepEqual(pushed, { testOption: true }) + }, t.fail) + } +) + +test( + 'refresh pushOnMissing returns other Errors', + t => { + const mcError = new Error('memcached error') + mc.getAsync = () => P.reject(mcError) + mc.setAsync = (key, val) => { + return P.reject(new Error('setAsync should not have been called')) + } + const settings = new TestSettings() + settings.setAll({ testOption: true }) + return settings.refresh({ pushOnMissing: true }) + .then( + t.fail, + err => { + t.equal(err, mcError) + } + ) + } +) diff --git a/test/memcache-helper.js b/test/memcache-helper.js index 318a2ef..ce54ad3 100644 --- a/test/memcache-helper.js +++ b/test/memcache-helper.js @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict' + var P = require('bluebird') var Memcached = require('memcached') P.promisifyAll(Memcached.prototype) @@ -52,10 +54,11 @@ module.exports.mc = mc var TEST_EMAIL = 'test@example.com' var TEST_IP = '192.0.2.1' -var limits = require('../lib/limits')(config, mc, console) -var allowedIPs = require('../lib/allowed_ips')(config, mc, console) -var allowedEmailDomains = require('../lib/allowed_email_domains')(config, mc, console) -var requestChecks = require('../lib/requestChecks')(config, mc, console) +const Settings = require('../lib/settings/settings')(config, mc, console) +var limits = require('../lib/settings/limits')(config, Settings, console) +var allowedIPs = require('../lib/settings/allowed_ips')(config, Settings, console) +var allowedEmailDomains = require('../lib/settings/allowed_email_domains')(config, Settings, console) +var requestChecks = require('../lib/settings/requestChecks')(config, Settings, console) var EmailRecord = require('../lib/email_record')(limits) var IpEmailRecord = require('../lib/ip_email_record')(limits) var IpRecord = require('../lib/ip_record')(limits)