feat(client): Strict validation of email and uid on force_auth
fixes #3040
This commit is contained in:
Родитель
ea12cc5d17
Коммит
f666c8b759
|
@ -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 () {
|
||||
|
|
Загрузка…
Ссылка в новой задаче