fxa-auth-server/error.js

320 строки
6.6 KiB
JavaScript

/* 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 inherits = require('util').inherits
var DEFAULTS = {
code: 500,
error: 'Internal Server Error',
errno: 999,
message: 'Unspecified error',
info: 'https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#response-format'
}
var TOO_LARGE = /^Payload (?:content length|size) greater than maximum allowed/
function AppError(options, extra, headers) {
this.message = options.message || DEFAULTS.message
this.isBoom = true
this.stack = options.stack
this.errno = options.errno || DEFAULTS.errno
this.output = {
statusCode: options.code || DEFAULTS.code,
payload: {
code: options.code || DEFAULTS.code,
errno: this.errno,
error: options.error || DEFAULTS.error,
message: this.message,
info: options.info || DEFAULTS.info
},
headers: headers || {}
}
var keys = Object.keys(extra || {})
for (var i = 0; i < keys.length; i++) {
this.output.payload[keys[i]] = extra[keys[i]]
}
}
inherits(AppError, Error)
AppError.prototype.toString = function () {
return 'Error: ' + this.message
}
AppError.prototype.header = function (name, value) {
this.output.headers[name] = value
}
AppError.prototype.backtrace = function (traced) {
this.output.payload.log = traced
}
/*/
Translates an error from Hapi format to our format
/*/
AppError.translate = function (response) {
var error
if (response instanceof AppError) {
return response
}
var payload = response.output.payload
if (payload.statusCode === 401) {
// These are common errors generated by Hawk auth lib.
if (payload.message === 'Unknown credentials' ||
payload.message === 'Invalid credentials') {
error = AppError.invalidToken()
}
else if (payload.message === 'Stale timestamp') {
error = AppError.invalidTimestamp()
}
else if (payload.message === 'Invalid nonce') {
error = AppError.invalidNonce()
}
else {
error = AppError.invalidSignature(payload.message)
}
}
else if (payload.validation) {
error = AppError.invalidRequestParameter(payload.validation)
}
else if (payload.statusCode === 400 && TOO_LARGE.test(payload.message)) {
error = AppError.requestBodyTooLarge()
}
else {
error = new AppError({
message: payload.message,
code: payload.statusCode,
error: payload.error,
errno: payload.errno,
info: payload.info,
stack: response.stack
})
}
return error
}
// Helper functions for creating particular response types.
AppError.accountExists = function (email) {
return new AppError(
{
code: 400,
error: 'Bad Request',
errno: 101,
message: 'Account already exists'
},
{
email: email
}
)
}
AppError.unknownAccount = function (email) {
return new AppError(
{
code: 400,
error: 'Bad Request',
errno: 102,
message: 'Unknown account'
},
{
email: email
}
)
}
AppError.incorrectPassword = function (dbEmail, requestEmail) {
if (dbEmail !== requestEmail) {
return new AppError(
{
code: 400,
error: 'Bad Request',
errno: 120,
message: 'Incorrect email case'
},
{
email: dbEmail
}
)
}
return new AppError(
{
code: 400,
error: 'Bad Request',
errno: 103,
message: 'Incorrect password'
},
{
email: dbEmail
}
)
}
AppError.unverifiedAccount = function () {
return new AppError({
code: 400,
error: 'Bad Request',
errno: 104,
message: 'Unverified account'
})
}
AppError.invalidVerificationCode = function (details) {
return new AppError(
{
code: 400,
error: 'Bad Request',
errno: 105,
message: 'Invalid verification code'
},
details
)
}
AppError.invalidRequestBody = function () {
return new AppError({
code: 400,
error: 'Bad Request',
errno: 106,
message: 'Invalid JSON in request body'
})
}
AppError.invalidRequestParameter = function (validation) {
return new AppError(
{
code: 400,
error: 'Bad Request',
errno: 107,
message: 'Invalid parameter in request body'
},
{
validation: validation
}
)
}
AppError.missingRequestParameter = function (param) {
return new AppError(
{
code: 400,
error: 'Bad Request',
errno: 108,
message: 'Missing parameter in request body' + (param ? ': ' + param : '')
},
{
param: param
}
)
}
AppError.invalidSignature = function (message) {
return new AppError({
code: 401,
error: 'Unauthorized',
errno: 109,
message: message || 'Invalid request signature'
})
}
AppError.invalidToken = function () {
return new AppError({
code: 401,
error: 'Unauthorized',
errno: 110,
message: 'Invalid authentication token in request signature'
})
}
AppError.invalidTimestamp = function () {
return new AppError(
{
code: 401,
error: 'Unauthorized',
errno: 111,
message: 'Invalid timestamp in request signature'
},
{
serverTime: Math.floor(+new Date() / 1000)
}
)
}
AppError.invalidNonce = function () {
return new AppError({
code: 401,
error: 'Unauthorized',
errno: 115,
message: 'Invalid nonce in request signature'
})
}
AppError.missingContentLength = function () {
return new AppError({
code: 411,
error: 'Length Required',
errno: 112,
message: 'Missing content-length header'
})
}
AppError.requestBodyTooLarge = function () {
return new AppError({
code: 413,
error: 'Request Entity Too Large',
errno: 113,
message: 'Request body too large'
})
}
AppError.tooManyRequests = function (retryAfter) {
if (!retryAfter) {
retryAfter = 30;
}
return new AppError(
{
code: 429,
error: 'Too Many Requests',
errno: 114,
message: 'Client has sent too many requests'
},
{
retryAfter: retryAfter
},
{
'retry-after': retryAfter
}
)
}
AppError.serviceUnavailable = function (retryAfter) {
if (!retryAfter) {
retryAfter = 30;
}
return new AppError(
{
code: 503,
error: 'Service Unavailable',
errno: 201,
message: 'Service unavailable'
},
{
retryAfter: retryAfter
},
{
'retry-after': retryAfter
}
)
}
AppError.gone = function () {
return new AppError({
code: 410,
error: 'Gone',
errno: 116,
message: 'This endpoint is no longer supported'
})
}
module.exports = AppError