feat(client): Strict validation of email and uid on force_auth

fixes #3040
This commit is contained in:
Shane Tomlinson 2016-04-01 12:17:43 +00:00
Родитель ea12cc5d17
Коммит f666c8b759
6 изменённых файлов: 98 добавлений и 23 удалений

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

@ -206,10 +206,13 @@ define(function (require, exports, module) {
errno: 1023,
message: t('Valid email required')
},
/*
Removed in issue #3040
FORCE_AUTH_EMAIL_REQUIRED: {
errno: 1024,
message: t('/force_auth requires an email')
},
*/
EXPIRED_VERIFICATION_LINK: {
errno: 1025,
message: t('The link you clicked to verify your email is expired.')

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

@ -11,7 +11,9 @@ define(function (require, exports, module) {
var Vat = require('vat');
Vat.register('base64jwt', Vat.string().test(Validate.isBase64JwtValid));
Vat.register('email', Vat.string().test(Validate.isEmailValid));
Vat.register('hex', Vat.string().test(Validate.isHexValid));
Vat.register('uid', Vat.string().test(Validate.isUidValid));
Vat.register('uri', Vat.string().test(Validate.isUriValid));
Vat.register('url', Vat.string().test(Validate.isUrlValid));
Vat.register('urn', Vat.string().test(Validate.isUrnValid));

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

@ -16,6 +16,8 @@ define(function (require, exports, module) {
var PasswordResetMixin = require('views/mixins/password-reset-mixin');
var SignInView = require('views/sign_in');
var Template = require('stache!templates/force_auth');
var Transform = require('lib/transform');
var Vat = require('lib/vat');
function getFatalErrorMessage(self, fatalError) {
if (fatalError) {
@ -25,6 +27,11 @@ define(function (require, exports, module) {
return '';
}
var RELIER_DATA_SCHEMA = {
email: Vat.email().required(),
uid: Vat.uid().allow(null)
};
var proto = SignInView.prototype;
var View = SignInView.extend({
@ -38,13 +45,30 @@ define(function (require, exports, module) {
_fatalError: null,
beforeRender: function () {
var self = this;
_getAndValidateAccountData: function () {
var fieldsToPick = ['email', 'uid'];
var accountData = {};
var relier = this.relier;
if (! relier.has('email')) {
this._fatalError = AuthErrors.toError('FORCE_AUTH_EMAIL_REQUIRED');
return;
fieldsToPick.forEach(function (fieldName) {
if (relier.has(fieldName)) {
accountData[fieldName] = relier.get(fieldName);
}
});
return Transform.transformUsingSchema(
accountData, RELIER_DATA_SCHEMA, AuthErrors);
},
beforeRender: function () {
var self = this;
var accountData;
try {
accountData = this._getAndValidateAccountData();
} catch (err) {
this.fatalError(err);
return false;
}
/**
@ -56,11 +80,11 @@ define(function (require, exports, module) {
* and do not allow the user to continue.
*/
var account = this.user.initAccount({
email: relier.get('email'),
uid: relier.get('uid')
email: accountData.email,
uid: accountData.uid
});
if (relier.has('uid')) {
if (accountData.uid) {
return p.all([
this.user.checkAccountEmailExists(account),
this.user.checkAccountUidExists(account)

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

@ -6,6 +6,7 @@ define(function (require, exports, module) {
'use strict';
var _ = require('underscore');
var Constants = require('lib/constants');
var p = require('lib/promise');
var ProfileMock = require('../mocks/profile.js');
var sinon = require('sinon');
@ -81,6 +82,10 @@ define(function (require, exports, module) {
return str;
}
function createUid() {
return createRandomHexString(Constants.UID_LENGTH);
}
function createEmail() {
return 'signin' + Math.random() + '@restmail.net';
}
@ -156,6 +161,7 @@ define(function (require, exports, module) {
addFxaClientSpy: addFxaClientSpy,
createEmail: createEmail,
createRandomHexString: createRandomHexString,
createUid: createUid,
emailToUser: emailToUser,
getValueLabel: getValueLabel,
indexOfEvent: indexOfEvent,

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

@ -24,6 +24,7 @@ define(function (require, exports, module) {
var assert = chai.assert;
describe('/views/force_auth', function () {
var broker;
var email;
@ -73,6 +74,7 @@ define(function (require, exports, module) {
});
sinon.spy(view, 'navigate');
sinon.spy(view, 'fatalError');
}
beforeEach(function () {
@ -88,14 +90,39 @@ define(function (require, exports, module) {
});
describe('render', function () {
describe('with missing email address', function () {
describe('with a missing email address', function () {
beforeEach(function () {
relier.unset('email');
return view.render();
});
it('prints an error message', function () {
assert.include(view.$('.error').text(), 'requires an email');
it('delegates to fatalError', function () {
assert.isTrue(view.fatalError.called);
});
});
describe('with an invalid email address', function () {
beforeEach(function () {
relier.set('email', 'not an email');
return view.render();
});
it('delegates to fatalError', function () {
assert.isTrue(view.fatalError.called);
});
});
describe('with an invalid uid', function () {
beforeEach(function () {
relier.set('uid', 'invalid uid');
return view.render();
});
it('delegates to fatalError', function () {
assert.isTrue(view.fatalError.called);
});
});
@ -118,7 +145,7 @@ define(function (require, exports, module) {
describe('with registered email, registered uid', function () {
beforeEach(function () {
relier.set({
uid: 'registered_uid'
uid: TestHelpers.createUid()
});
isEmailRegistered = isUidRegistered = true;
@ -138,7 +165,7 @@ define(function (require, exports, module) {
describe('with registered email, unregistered uid', function () {
beforeEach(function () {
relier.set({
uid: 'unregistered_uid'
uid: TestHelpers.createUid()
});
isEmailRegistered = true;
@ -189,7 +216,7 @@ define(function (require, exports, module) {
describe('with unregistered email, registered uid', function () {
beforeEach(function () {
relier.set({
uid: 'registered_uid'
uid: TestHelpers.createUid()
});
isEmailRegistered = false;
@ -227,7 +254,7 @@ define(function (require, exports, module) {
describe('with unregistered email, unregistered uid', function () {
beforeEach(function () {
relier.set({
uid: 'unregistered_uid'
uid: TestHelpers.createUid()
});
isEmailRegistered = isUidRegistered = false;

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

@ -21,6 +21,10 @@ define([
var type = FunctionalHelpers.type;
var visibleByQSA = FunctionalHelpers.visibleByQSA;
var testErrorInclude = function (expected) {
return testElementTextInclude('.error', expected);
};
function testAccountNoLongerExistsErrorShown() {
return this.parent
.then(testElementTextInclude('.error', 'no longer exists'));
@ -38,16 +42,25 @@ define([
return FunctionalHelpers.clearBrowserState(this);
},
'with a missing email': function () {
return this.remote
.then(openForceAuth({
header: '#fxa-400-header'
}))
.then(testErrorInclude('missing'))
.then(testErrorInclude('email'));
},
'with an invalid email': function () {
return this.remote
.then(openForceAuth({
// TODO - this is a discrepancy and should go to the 400 page,
// but that's for another PR.
header: '#fxa-unexpected-error-header',
header: '#fxa-400-header',
query: {
email: 'invalid'
}
}));
}))
.then(testErrorInclude('invalid'))
.then(testErrorInclude('email'));
},
'with a registered email, no uid': function () {
@ -64,15 +77,15 @@ define([
.then(createUser(email, PASSWORD, { preVerified: true }))
.then(function (accountInfo) {
return openForceAuth({
// TODO - this is a discrepancy and should go to the 400 page,
// but that's for another PR.
header: '#fxa-unexpected-error-header',
header: '#fxa-400-header',
query: {
email: email,
uid: 'a' + accountInfo.uid
}
}).call(this);
});
})
.then(testErrorInclude('invalid'))
.then(testErrorInclude('uid'));
},
'with a registered email, registered uid': function () {