module.exports = function()

in packages/fxa-customs-server/lib/config/config.js [8:616]


module.exports = function (fs, path, url, convict) {
  var conf = convict({
    env: {
      doc: 'The current node.js environment',
      default: 'prod',
      format: ['dev', 'test', 'stage', 'prod'],
      env: 'NODE_ENV',
    },
    log: {
      level: {
        default: 'trace',
        env: 'LOG_LEVEL',
      },
    },
    publicUrl: {
      format: 'url',
      default: 'http://localhost:7000',
      env: 'PUBLIC_URL',
    },
    listen: {
      host: {
        doc: 'The ip address the server should bind',
        default: 'localhost',
        format: String,
        env: 'IP_ADDRESS',
      },
      port: {
        doc: 'The port the server should bind',
        default: 7000,
        format: 'port',
        env: 'PORT',
      },
    },
    updatePollIntervalSeconds: {
      doc: 'interval to check cache for new limits / allowedIPs. 0 is off',
      default: 0,
      format: 'nat',
      env: 'UPDATE_POLL_INTERVAL_SECONDS',
    },
    limits: {
      blockIntervalSeconds: {
        doc: 'Duration of a manual `block` ban',
        default: 60 * 60 * 24,
        format: 'nat',
        env: 'BLOCK_INTERVAL_SECONDS',
      },
      suspectInterval: {
        doc: 'Duration of a `suspect` flag, instigated by the Dataflow pipeline',
        default: '1 day',
        env: 'SUSPECT_INTERVAL',
        format: 'duration',
      },
      disableInterval: {
        doc: 'Duration of a long-term `disable` flag, instigated by the Dataflow pipeline',
        default: '1 year',
        env: 'DISABLE_INTERVAL',
        format: 'duration',
      },
      rateLimitIntervalSeconds: {
        doc: 'Duration of automatic throttling',
        default: 60 * 15,
        format: 'nat',
        env: 'RATE_LIMIT_INTERVAL_SECONDS',
      },
      maxEmails: {
        doc: 'Number of emails sent within rateLimitIntervalSeconds before throttling',
        default: 3,
        format: 'nat',
        env: 'MAX_EMAILS',
      },
      maxBadLogins: {
        doc: 'Number failed login attempts within rateLimitIntervalSeconds before throttling',
        default: 2,
        format: 'nat',
        env: 'MAX_BAD_LOGINS',
      },
      maxBadLoginsPerIp: {
        doc: 'Number failed login attempts within rateLimitIntervalSeconds on a single IP before throttling',
        default: 3,
        format: 'nat',
        env: 'MAX_BAD_LOGINS_PER_IP',
      },
      maxBadLoginsPerEmail: {
        doc: 'Number failed login attempts within rateLimitIntervalSeconds on a single email before throttling',
        default: 3,
        format: 'nat',
        env: 'MAX_BAD_LOGINS_PER_EMAIL',
      },
      maxUnblockAttempts: {
        doc: 'Number of login attempts that can be unblocked',
        default: 5,
        env: 'MAX_UNBLOCK_ATTEMPTS',
        format: 'nat',
      },
      maxVerifyCodes: {
        doc: 'Number code verifictions within rateLimitIntervalSeconds before throttling',
        default: 10,
        format: 'nat',
        env: 'MAX_VERIFY_CODES',
      },
      badLoginErrnoWeights: {
        doc: 'Maps bad-login errnos to a weight multipler, because some bad logins are badder than others',
        format: Object,
        env: 'BAD_LOGIN_ERRNO_WEIGHTS',
        default: {
          102: 2,
          125: 4,
          126: 2,
        },
      },
      ipRateLimitIntervalSeconds: {
        doc: 'Duration of automatic throttling for IPs',
        default: 60 * 15,
        format: 'nat',
        env: 'IP_RATE_LIMIT_INTERVAL_SECONDS',
      },
      ipRateLimitBanDurationSeconds: {
        doc: 'Duration of automatic ban for throttled IPs',
        default: 60 * 15,
        format: 'nat',
        env: 'IP_RATE_LIMIT_BAN_DURATION_SECONDS',
      },
      smsRateLimit: {
        limitIntervalSeconds: {
          doc: 'Duration of automatic throttling for sms',
          default: 24 * 60 * 60, // One day
          format: 'nat',
          env: 'SMS_RATE_LIMIT_INTERVAL_SECONDS',
        },
        maxSms: {
          doc: 'Number of sms sent within rateLimitIntervalSeconds before throttling',
          default: 6,
          format: 'nat',
          env: 'MAX_SMS',
        },

        // Not currently used... But might leaving around for future calls.
        maxTwilioRequests: {
          doc: 'Number of twilio requests sent within rateLimitIntervalSeconds before throttling',
          default: 10,
          env: 'MAX_TWILIO_REQUESTS',
        },
      },
      uidRateLimit: {
        limitIntervalSeconds: {
          doc: 'Duration of automatic throttling for uids',
          default: 60 * 15,
          format: 'nat',
          env: 'UID_RATE_LIMIT_INTERVAL_SECONDS',
        },
        banDurationSeconds: {
          doc: 'Duration of automatic ban',
          default: 60 * 15,
          format: 'nat',
          env: 'UID_RATE_LIMIT_BAN_DURATION_SECONDS',
        },
        maxChecks: {
          doc: 'Number of checks within uidRateLimitBanDurationSeconds before blocking',
          default: 100,
          format: 'nat',
          env: 'UID_RATE_LIMIT',
        },
      },
      maxAccountStatusCheck: {
        doc: 'Number of account status checks within rateLimitIntervalSeconds before throttling',
        default: 20,
        format: 'nat',
        env: 'MAX_ACCOUNT_STATUS_CHECK',
      },
      maxAccountAccess: {
        doc: 'Number of account access actions within ipRateLimitIntervalSeconds before throttling',
        default: 5,
        format: 'nat',
        env: 'MAX_ACCOUNT_ACCESS',
      },
      passwordResetOtpLimits: {
        maxPasswordResetOtpEmails: {
          doc: 'Number of OTP email for an account email or from an IP can request before rate limiting',
          default: 5,
          format: 'nat',
          env: 'PASSWORD_RESET_OTP_EMAIL_LIMIT',
        },
        passwordResetOtpEmailRequestWindowSeconds: {
          doc: 'Number of seconds when the max number of OTP email requests is allowed',
          default: 600,
          format: 'nat',
          env: 'PASSWORD_RESET_OTP_EMAIL_REQUEST_WINDOW_SECONDS',
        },
        passwordResetOtpRateLimitIntervalSeconds: {
          doc: 'Number of seconds to wait until password reset OTP requests are allowed again',
          default: 15 * 60,
          format: 'nat',
          env: 'PASSWORD_RESET_OTP_EMAIL_RATE_LIMIT_SECONDS',
        },
        maxPasswordResetOtpVerificationRateLimit: {
          doc: 'Number of OTP verification for an account email or from an IP can request before rate limiting for passwordResetOtpVerificationLimitIntervalSeconds',
          default: 5,
          format: 'nat',
          env: 'PASSWORD_RESET_OTP_VERIFICATION_RATE_LIMIT',
        },
        passwordResetOtpVerificationLimitIntervalSeconds: {
          doc: 'Number of seconds to wait until password reset OTP verification is allowed again',
          default: 600,
          format: 'nat',
          env: 'PASSWORD_RESET_OTP_VERIFICATION_RATE_LIMIT_SECONDS',
        },
        maxPasswordResetOtpVerificationBlockLimit: {
          doc: 'Number of OTP verification for an account email or from an IP can request before blocking for passwordResetOtpVerificationBlockWindowSeconds',
          default: 10,
          format: 'nat',
          env: 'PASSWORD_RESET_OTP_VERIFICATION_BLOCK_LIMIT',
        },
        passwordResetOtpVerificationBlockWindowSeconds: {
          doc: 'Number of seconds when the max number of OTP verification is allowed',
          default: 86400,
          format: 'nat',
          env: 'PASSWORD_RESET_OTP_VERIFICATION_WINDOW_SECONDS',
        },
      },
    },
    cache: {
      recordLifetimeSeconds: {
        doc: 'Record expiry',
        default: 900,
        format: 'nat',
        env: 'RECORD_LIFETIME_SECONDS',
      },
    },
    redis: makeRedisConfig(),
    allowedIPs: {
      doc: 'An array of IPs that will not be blocked or rate-limited.',
      format: Array,
      env: 'ALLOWED_IPS',
      default: [],
    },
    allowedEmailDomains: {
      doc: 'An array of email domains that will not be blocked or rate-limited.',
      format: Array,
      env: 'ALLOWED_EMAIL_DOMAINS',
      // These are emails frequently used for testing purposes
      default: ['restmail.net', 'restmail.dev.lcip.org'],
    },
    allowedPhoneNumbers: {
      doc: 'An array of phone numbers that will not be blocked or rate-limited.',
      format: Array,
      env: 'ALLOWED_PHONE_NUMBERS',
      default: [],
    },
    requestChecks: {
      treatEveryoneWithSuspicion: {
        doc: 'Whether to flag all requests as suspicious by default',
        format: Boolean,
        default: false,
        env: 'TREAT_EVERYONE_WITH_SUSPICION',
      },
      flowIdRequiredOnLogin: {
        doc: 'Whether to require a flowId in payload on account login.',
        format: Boolean,
        default: false,
        env: 'FLOW_ID_REQUIRED_ON_LOGIN',
      },
      flowIdExemptUserAgentREs: {
        doc: 'An array of STRING regexes for UAs that dont require a flowId.',
        format: Array,
        default: [],
        env: 'FLOW_ID_EXEMPT_UA_REGEXES',
      },
    },
    ipBlocklist: {
      enable: {
        doc: 'Flag to enable ip blocklist',
        format: Boolean,
        default: false,
        env: 'IP_BLOCKLIST_ENABLE',
      },
      lists: {
        doc: 'An array of ip blocklist file paths that should be blocked',
        format: Array,
        default: [],
        env: 'IP_BLOCKLIST_FILES',
      },
      logOnlyLists: {
        doc: 'An array of ip blocklist file paths that should be logged only',
        format: Array,
        default: [],
        env: 'IP_BLOCKLIST_LOG_ONLY_FILES',
      },
      updatePollInterval: {
        doc: 'Poll interval for checking for updated lists (seconds)',
        default: 300,
        format: 'nat',
        env: 'IP_BLOCKLIST_POLL_INTERVAL_SECONDS',
      },
    },
    reputationService: {
      enable: {
        doc: 'Flag to enable using the IP Reputation Service',
        format: Boolean,
        default: false,
        env: 'REPUTATION_SERVICE_ENABLE',
      },
      enableCheck: {
        doc: 'Flag to enable fetching reputation for IPs on /check route',
        format: Boolean,
        default: false,
        env: 'REPUTATION_SERVICE_ENABLE_CHECK',
      },
      suspectBelow: {
        doc: 'Suspect /check requests from IPs with reputation less than this',
        format: 'int',
        default: 0,
        env: 'REPUTATION_SERVICE_SUSPECT_BELOW',
      },
      blockBelow: {
        doc: 'Block /check requests from IPs with reputation less than this',
        format: 'int',
        default: 0,
        env: 'REPUTATION_SERVICE_BLOCK_BELOW',
      },
      baseUrl: {
        doc: 'The reputation service base url.',
        default: 'http://localhost:8080/',
        format: 'url',
        env: 'REPUTATION_SERVICE_BASE_URL',
      },
      hawkId: {
        doc: 'HAWK ID for sending blocked IPs to the IP Reputation Service',
        default: 'root',
        format: String,
        env: 'REPUTATION_SERVICE_HAWK_ID',
      },
      hawkKey: {
        doc: 'HAWK key for sending blocked IPs to the IP Reputation Service',
        default: 'toor',
        format: String,
        env: 'REPUTATION_SERVICE_HAWK_KEY',
      },
      timeout: {
        doc: 'timeout in ms to wait for requests sent to the IP Reputation Service',
        default: 50,
        format: 'int',
        env: 'REPUTATION_SERVICE_TIMEOUT',
      },
    },
    sentry: {
      dsn: {
        doc: 'Sentry DSN for error and log reporting',
        default: '',
        format: 'String',
        env: 'SENTRY_DSN',
      },
      env: {
        doc: 'Environment name to report to sentry',
        default: 'local',
        format: ['local', 'ci', 'dev', 'stage', 'prod'],
        env: 'SENTRY_ENV',
      },
      sampleRate: {
        doc: 'Rate at which sentry errors are captured.',
        default: 1.0,
        format: 'Number',
        env: 'SENTRY_SAMPLE_RATE',
      },
      serverName: {
        doc: 'Name used by sentry to identify the server.',
        default: 'fxa-customs-server',
        format: 'String',
        env: 'SENTRY_SERVER_NAME',
      },
      tracesSampleRate: {
        doc: 'Rate at which sentry traces are captured',
        default: 0,
        format: 'Number',
        env: 'SENTRY_TRACES_SAMPLE_RATE',
      },
    },
    statsd: {
      enabled: {
        doc: 'Enable StatsD',
        format: Boolean,
        default: false,
        env: 'STATSD_ENABLE',
      },
      sampleRate: {
        doc: 'Sampling rate for StatsD',
        format: Number,
        default: 1,
        env: 'STATSD_SAMPLE_RATE',
      },
      maxBufferSize: {
        doc: 'StatsD message buffer size in number of characters',
        format: Number,
        default: 500,
        env: 'STATSD_BUFFER_SIZE',
      },
      host: {
        doc: 'StatsD host to report to',
        format: String,
        default: 'localhost',
        env: 'DD_AGENT_HOST',
      },
      port: {
        doc: 'Port number of StatsD server',
        format: Number,
        default: 8125,
        env: 'DD_DOGSTATSD_PORT',
      },
      prefix: {
        doc: 'StatsD metrics name prefix',
        format: String,
        default: 'fxa-customs-server.',
        env: 'STATSD_PREFIX',
      },
    },
    tracing: tracingConfig,
    userDefinedRateLimitRules: {
      totpCodeRules: {
        actions: {
          doc: 'Array of actions that this rule should be applied to',
          default: ['verifyTotpCode'],
          format: Array,
        },
        limits: {
          max: {
            doc: 'max actions during `period` that can occur before rate limit is applied',
            format: 'nat',
            default: 5,
            env: 'TOTP_CODE_RULE_MAX',
          },
          periodMs: {
            doc: 'period needed before rate limit is reset',
            format: 'duration',
            default: '30 seconds',
            env: 'TOTP_CODE_RULE_PERIOD_MS',
          },
          rateLimitIntervalMs: {
            doc: 'how long rate limit is applied',
            format: 'duration',
            default: '15 minutes',
            env: 'TOTP_CODE_RULE_LIMIT_INTERVAL_MS',
          },
        },
      },
      recoveryPhoneTotpCodeRules: {
        actions: {
          doc: 'Array of actions that this rule should be applied to',
          default: ['verifyRecoveryPhoneTotpCode'],
          format: Array,
        },
        limits: {
          max: {
            doc: 'max actions during `period` that can occur before rate limit is applied',
            format: 'nat',
            default: 5,
            env: 'RECOVERY_PHONE_TOTP_CODE_RULE_MAX',
          },
          periodMs: {
            doc: 'period needed before rate limit is reset',
            format: 'duration',
            default: '5 minutes',
            env: 'RECOVERY_PHONE_TOTP_CODE_RULE_PERIOD_MS',
          },
          rateLimitIntervalMs: {
            doc: 'how long rate limit is applied',
            format: 'duration',
            default: '15 minutes',
            env: 'RECOVERY_PHONE_TOTP_CODE_RULE_LIMIT_INTERVAL_MS',
          },
        },
      },
      tokenCodeRules: {
        actions: {
          doc: 'Array of actions that this rule should be applied to',
          default: ['verifyTokenCode'], // This rule never appears to be used ??
          format: Array,
        },
        limits: {
          max: {
            doc: 'max actions during `period` that can occur before rate limit is applied',
            format: 'nat',
            default: 5,
            env: 'TOKEN_CODE_RULE_MAX',
          },
          periodMs: {
            doc: 'period needed before rate limit is reset',
            format: 'duration',
            default: '15 minutes',
            env: 'TOKEN_CODE_RULE_PERIOD_MS',
          },
          rateLimitIntervalMs: {
            doc: 'how long rate limit is applied',
            format: 'duration',
            default: '15 minutes',
            env: 'TOKEN_CODE_RULE_LIMIT_INTERVAL_MS',
          },
        },
      },
      recoveryPhoneSendSetupCodeRules: {
        actions: {
          doc:
            'Array of actions that this rule should be applied to. A user should only be able to initiate ' +
            'a recovery phone setup X times a day by sending a setup code.',
          default: ['recoveryPhoneSendSetupCode'],
          format: Array,
        },
        limits: {
          max: {
            doc: 'max actions during `period` that can occur before rate limit is applied',
            format: 'nat',
            default: 9,
            env: 'RECOVERY_PHONE_SEND_SETUP_CODE_RULE_MAX',
          },
          periodMs: {
            doc: 'period needed before rate limit is reset',
            format: 'duration',
            default: '1 day',
            env: 'RECOVERY_PHONE_SEND_SETUP_CODE_RULE_PERIOD_MS',
          },
          rateLimitIntervalMs: {
            doc: 'how long rate limit is applied',
            format: 'duration',
            default: '24 hours',
            env: 'RECOVERY_PHONE_SEND_SETUP_CODE_RULE_LIMIT_INTERVAL_MS',
          },
        },
      },
      recoveryPhoneSendSigninCodeRules: {
        actions: {
          doc:
            'Array of actions that this rule should be applied to. A user should only be able to initiate ' +
            'a recovery phone signin X times a day by sending an sms code.',
          default: ['recoveryPhoneSendSigninCode'],
          format: Array,
        },
        limits: {
          max: {
            doc: 'max actions during `period` that can occur before rate limit is applied',
            format: 'nat',
            default: 6,
            env: 'RECOVERY_PHONE_SEND_SIGNIN_CODE_RULE_MAX',
          },
          periodMs: {
            doc: 'period needed before rate limit is reset',
            format: 'duration',
            default: '1 day',
            env: 'RECOVERY_PHONE_SEND_SIGNIN_CODE_RULE_PERIOD_MS',
          },
          rateLimitIntervalMs: {
            doc: 'how long rate limit is applied',
            format: 'duration',
            default: '24 hours',
            env: 'RECOVERY_PHONE_SEND_SIGNIN_CODE_RULE_LIMIT_INTERVAL_MS',
          },
        },
      },
    },
    dataflow: {
      enabled: {
        doc: 'Enable integration with the Dataflow fraud detection pipeline?',
        format: Boolean,
        default: false,
        env: 'DATAFLOW_ENABLED',
      },
      reportOnly: {
        doc: 'Treat all suggested actions from the Dataflow pipeline as `report`',
        default: true,
        env: 'DATAFLOW_REPORT_ONLY',
        format: Boolean,
      },
      ignoreOlderThan: {
        doc: 'Ignore messages older than this value. Or set to `0` to never ignore old messages',
        default: '1 day',
        env: 'DATAFLOW_IGNORE_OLDER_THAN',
        format: 'duration',
      },
      gcpPubSub: {
        projectId: {
          doc: 'GCP PubSub project ID',
          default: '',
          format: String,
          env: 'DATAFLOW_GCP_PUBSUB_PROJECT_ID',
        },
        subscriptionName: {
          doc: 'GCP PubSub subscription name',
          default: '',
          format: String,
          env: 'DATAFLOW_GCP_PUBSUB_SUBSCRIPTION_NAME',
        },
      },
    },
  });

  // handle configuration files.  you can specify a CSV list of configuration
  // files to process, which will be overlayed in order, in the CONFIG_FILES
  // environment variable. By default, the ./config/<env>.json file is loaded.

  var envConfig = path.join(
    path.dirname(path.dirname(__dirname)),
    'config',
    conf.get('env') + '.json'
  );
  var files = (envConfig + ',' + process.env.CONFIG_FILES)
    .split(',')
    .filter(fs.existsSync);
  conf.loadFile(files);
  conf.validate({ allowed: 'strict' });

  return conf;
};