More comprehensive validation of email addresses.

This commit is contained in:
Ryan Kelly 2014-02-18 21:48:23 +11:00
Родитель c75223cc4c
Коммит 7ef3dcbac3
5 изменённых файлов: 188 добавлений и 17 удалений

Просмотреть файл

@ -4,7 +4,6 @@
var validators = require('./validators')
var HEX_STRING = validators.HEX_STRING
var LAZY_EMAIL = validators.LAZY_EMAIL
var Password = require('../crypto/password')
var butil = require('../crypto/butil')
@ -30,7 +29,7 @@ module.exports = function (
config: {
validate: {
payload: {
email: isA.string().max(255).regex(LAZY_EMAIL).required(),
email: validators.email().required(),
authPW: isA.string().min(64).max(64).regex(HEX_STRING).required(),
preVerified: isA.boolean(),
service: isA.string().max(16).alphanum().optional(),
@ -164,7 +163,7 @@ module.exports = function (
config: {
validate: {
payload: {
email: isA.string().max(255).regex(LAZY_EMAIL).required(),
email: validators.email().required(),
authPW: isA.string().min(64).max(64).regex(HEX_STRING).required()
},
},
@ -326,7 +325,7 @@ module.exports = function (
},
response: {
schema: {
email: isA.string().regex(LAZY_EMAIL).required(),
email: validators.email().required(),
verified: isA.boolean().required()
}
}
@ -453,7 +452,7 @@ module.exports = function (
config: {
validate: {
payload: {
email: isA.string().max(255).regex(LAZY_EMAIL).required(),
email: validators.email().required(),
authPW: isA.string().min(64).max(64).regex(HEX_STRING).required()
}
}

Просмотреть файл

@ -4,7 +4,6 @@
var validators = require('./validators')
var HEX_STRING = validators.HEX_STRING
var LAZY_EMAIL = validators.LAZY_EMAIL
var crypto = require('crypto')
var Password = require('../crypto/password')
@ -33,7 +32,7 @@ module.exports = function (
config: {
validate: {
payload: {
email: isA.string().max(255).regex(LAZY_EMAIL).required(),
email: validators.email().required(),
oldAuthPW: isA.string().min(64).max(64).regex(HEX_STRING).required()
}
}
@ -162,7 +161,7 @@ module.exports = function (
config: {
validate: {
payload: {
email: isA.string().max(255).regex(LAZY_EMAIL).required(),
email: validators.email().required(),
service: isA.string().max(16).alphanum().optional(),
redirectTo: isA.string()
.max(512)
@ -230,7 +229,7 @@ module.exports = function (
},
validate: {
payload: {
email: isA.string().max(255).regex(LAZY_EMAIL).required(),
email: validators.email().required(),
service: isA.string().max(16).alphanum().optional(),
redirectTo: isA.string()
.max(512)

Просмотреть файл

@ -4,7 +4,6 @@
var validators = require('./validators')
var HEX_STRING = validators.HEX_STRING
var LAZY_EMAIL = validators.LAZY_EMAIL
module.exports = function (log, crypto, isA, config, redirectDomain) {
@ -42,7 +41,7 @@ module.exports = function (log, crypto, isA, config, redirectDomain) {
config: {
validate: {
query: {
email: isA.string().max(255).regex(LAZY_EMAIL).required(),
email: validators.email().required(),
code: isA.string().max(32).regex(HEX_STRING).required(),
token: isA.string().max(64).regex(HEX_STRING).required(),
service: isA.string().max(16).alphanum().optional(),

Просмотреть файл

@ -2,11 +2,94 @@
* 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 punycode = require('punycode')
var isA = require('hapi').types
// Match any non-empty hex-encoded string.
module.exports.HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
module.exports.LAZY_EMAIL = /^[^@\s]+@[^@\s]+$/
// Joi validator to match any valid email address.
// This is different to Joi's builtin email validator, and
// requires a custom validation function.
module.exports.email = function() {
var email = isA.string().max(255)
// Imma add a custom test to this Joi object using internal
// properties because I can't find a nice API to do that.
email._tests.push({ func: function(value, state, options) {
if (value !== undefined && value !== null) {
if (module.exports.isValidEmailAddress(value)) {
return null;
}
}
return {
type: 'validators.email',
context: { key: '<root>' },
path: state.path
}
}})
return email
}
// Function to validate an email address.
// This is a transliteration of the HTML5 email-validation logic
// inside Firefox. It splits the username and domain portions,
// translates tham into IDN punycode syntax, then does some very
// basic sanity-checking.
module.exports.isValidEmailAddress = function(value) {
// It cant be empty or end with strange chars.
if (!value) {
return false
}
if (value[value.length - 1] === '.' || value[value.length - 1] === '-') {
return false
}
// It must contain an '@' somewhere in the middle.
var atPos = value.indexOf('@')
if (atPos === -1 || atPos === 0 || atPos === value.length) {
return false
}
var username = value.substring(0, atPos)
var domain = value.substring(atPos + 1)
// Unicode is hard, let's work with ascii only.
username = punycode.toASCII(username)
domain = punycode.toASCII(domain)
// The username portion must contain only allowed characters.
for (var i = 0; i < username.length; i++) {
if (!username[i].match(/[a-zA-Z0-9.!#$%&'*+-\/=?^_`{|}~]/)) {
return false
}
}
// The domain portion can't begin with a dot or a dash.
if (domain[0] === '.' || domain[i] === '-') {
return false
}
// The domain portion must be a valid punycode domain.
for (i = 0; i < domain.length; i++) {
if (domain[i] === '.') {
// A dot can't follow a dot or a dash.
if (domain[i - 1] === '.' || domain[i - 1] === '-') {
return false
}
}
else if (domain[i] === '-') {
// A dash can't follow a dot.
if (domain[i - 1] === '.') {
return false
}
} else if (!domain[i].match(/[a-zA-Z0-9-]/)) {
// The domain characters must be alphanumeric.
return false
}
}
return true
}
// Match any subdomain of the given hostname.
module.exports.domainRegex = function (hostname) {
if (!hostname) { return new RegExp() }

Просмотреть файл

@ -231,7 +231,7 @@ TestServer.start(config)
}
).then(
function () {
t.ok(client.sessionToken, "client can login")
t.ok(client.sessionToken, 'client can login')
}
)
}
@ -280,15 +280,106 @@ TestServer.start(config)
)
test(
'/account/create with malformed email address',
'/account/create with a variety of malformed email addresses',
function (t) {
var email = 'notAnEmailAddress'
var password = '123456'
return Client.create(config.publicUrl, email, password, server.mailbox)
var pwd = '123456'
return Client.create(config.publicUrl, 'notAnEmailAddress', pwd)
.then(
t.fail,
function (err) {
t.equal(err.code, 400, 'malformed email is rejected')
return Client.create(config.publicUrl, '\n@example.com', pwd)
}
)
.then(
t.fail,
function (err) {
t.equal(err.code, 400, 'malformed email is rejected')
return Client.create(config.publicUrl, 'me@hello world.com', pwd)
}
)
.then(
t.fail,
function (err) {
t.equal(err.code, 400, 'malformed email is rejected')
return Client.create(config.publicUrl, 'me@hello+world.com', pwd)
}
)
.then(
t.fail,
function (err) {
t.equal(err.code, 400, 'malformed email is rejected')
return Client.create(config.publicUrl, 'me@.example', pwd)
}
)
.then(
t.fail,
function (err) {
t.equal(err.code, 400, 'malformed email is rejected')
return Client.create(config.publicUrl, 'me@example.com-', pwd)
}
)
.then(
t.fail,
function (err) {
t.equal(err.code, 400, 'malformed email is rejected')
}
)
}
)
test(
'/account/create with a variety of unusual but valid email addresses',
function (t) {
var pwd = '123456'
return Client.create(config.publicUrl, 'a+b+c@example.com', pwd)
.then(
function (c) {
return c.destroyAccount()
},
function (err) {
t.equal(err.errno, 101, 'unusual email is not invalid')
}
)
.then(
function () {
return Client.create(config.publicUrl, '#!?-@t-e-s-t.c-o-m', pwd)
}
)
.then(
function (c) {
return c.destroyAccount()
},
function (err) {
t.equal(err.errno, 101, 'unusual email is not invalid')
}
)
.then(
function () {
var email = String.fromCharCode(1234) + '@example.com'
return Client.create(config.publicUrl, email, pwd)
}
)
.then(
function (c) {
return c.destroyAccount()
},
function (err) {
t.equal(err.errno, 101, 'unusual email is not invalid')
}
)
.then(
function () {
var email = 'test@' + String.fromCharCode(5678) + '.com'
return Client.create(config.publicUrl, email, pwd)
}
)
.then(
function (c) {
return c.destroyAccount()
},
function (err) {
t.equal(err.errno, 101, 'unusual email is not invalid')
}
)
}