fix(auth_broker): fx-desktop-v1 merged into fx-ios-v1
This commit is contained in:
Shane Tomlinson 2019-03-20 08:48:00 +00:00 коммит произвёл GitHub
Родитель 5c66b7b7f9 6cd6fae2f0
Коммит e978dc4ae0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 204 добавлений и 292 удалений

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

@ -1,96 +0,0 @@
/* 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/. */
/**
* V1 of the broker to communicate with Fx Desktop when signing in to Sync.
*/
define(function (require, exports, module) {
'use strict';
const _ = require('underscore');
const FxDesktopChannel = require('../../lib/channels/fx-desktop-v1');
const FxSyncChannelAuthenticationBroker = require('../auth_brokers/fx-sync-channel');
const HaltBehavior = require('../../views/behaviors/halt');
const Url = require('../../lib/url');
var proto = FxSyncChannelAuthenticationBroker.prototype;
var FxDesktopV1AuthenticationBroker = FxSyncChannelAuthenticationBroker.extend({
type: 'fx-desktop-v1',
commands: {
CAN_LINK_ACCOUNT: 'can_link_account',
CHANGE_PASSWORD: 'change_password',
DELETE_ACCOUNT: 'delete_account',
LOADED: 'loaded',
LOGIN: 'login'
},
defaultBehaviors: _.extend({}, proto.defaultBehaviors, {
// about:accounts displays its own screen after sign in, no need
// to show anything.
afterForceAuth: new HaltBehavior(),
// about:accounts displays its own screen after password reset, no
// need to show anything.
afterResetPasswordConfirmationPoll: new HaltBehavior(),
// about:accounts displays its own screen after sign in, no need
// to show anything.
afterSignIn: new HaltBehavior(),
// about:accounts displays its own screen after sign in, no need
// to show anything.
afterSignInConfirmationPoll: new HaltBehavior(),
// about:accounts displays its own screen after sign in, no need
// to show anything.
afterSignUpConfirmationPoll: new HaltBehavior()
}),
createChannel () {
var channel = new FxDesktopChannel();
channel.initialize({
// Fx Desktop browser will send messages with an origin of the string
// `null`. These messages are trusted by the channel by default.
//
// 1) Fx on iOS and functional tests will send messages from the
// content server itself. Accept messages from the content
// server to handle these cases.
// 2) Fx 18 (& FxOS 1.*) do not support location.origin. Build the origin from location.href
origin: this.window.location.origin || Url.getOrigin(this.window.location.href),
window: this.window
});
channel.on('error', this.trigger.bind(this, 'error'));
return channel;
},
afterResetPasswordConfirmationPoll (account) {
// We wouldn't expect `customizeSync` to be set when completing
// a password reset, but the field must be present for the login
// message to be sent. false is the default value set in
// lib/fxa-client.js if the value is not present.
// See #5528
if (! account.has('customizeSync')) {
account.set('customizeSync', false);
}
// Only fx-desktop-v1 based integrations send a login message
// after reset password complete, assuming the user verifies
// in the same browser. fx-desktop-v1 based integrations
// do not support WebChannels, and the login message must be
// sent within about:accounts for the browser to receive it.
// Integrations that support WebChannel messages will send
// the login message from the verification tab, and for users
// of either integration that verify in a different browser,
// they will be asked to signin in this browser using the
// new password.
return this._notifyRelierOfLogin(account)
.then(() => proto.afterResetPasswordConfirmationPoll.call(this, account));
}
});
module.exports = FxDesktopV1AuthenticationBroker;
});

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

@ -11,19 +11,88 @@ define(function (require, exports, module) {
'use strict'; 'use strict';
const _ = require('underscore'); const _ = require('underscore');
const FxDesktopV1AuthenticationBroker = require('../auth_brokers/fx-desktop-v1'); const FxDesktopChannel = require('../../lib/channels/fx-desktop-v1');
const FxSyncChannelAuthenticationBroker = require('../auth_brokers/fx-sync-channel');
const HaltBehavior = require('../../views/behaviors/halt');
const NavigateBehavior = require('../../views/behaviors/navigate'); const NavigateBehavior = require('../../views/behaviors/navigate');
const UserAgent = require('../../lib/user-agent'); const UserAgent = require('../../lib/user-agent');
const proto = FxDesktopV1AuthenticationBroker.prototype; const proto = FxSyncChannelAuthenticationBroker.prototype;
const FxiOSV1AuthenticationBroker = FxSyncChannelAuthenticationBroker.extend({
type: 'fx-ios-v1',
commands: {
CAN_LINK_ACCOUNT: 'can_link_account',
CHANGE_PASSWORD: 'change_password',
DELETE_ACCOUNT: 'delete_account',
LOADED: 'loaded',
LOGIN: 'login'
},
defaultBehaviors: _.extend({}, proto.defaultBehaviors, {
// about:accounts displays its own screen after sign in, no need
// to show anything.
afterForceAuth: new HaltBehavior(),
// about:accounts displays its own screen after password reset, no
// need to show anything.
afterResetPasswordConfirmationPoll: new HaltBehavior(),
// about:accounts displays its own screen after sign in, no need
// to show anything.
afterSignIn: new HaltBehavior(),
// about:accounts display the "Signin confirmed" screen after
// the user signin is successful
afterSignInConfirmationPoll: new NavigateBehavior('signin_confirmed'),
// about:accounts display the "Signup complete!" screen after
// the users verify their email
afterSignUpConfirmationPoll: new NavigateBehavior('signup_confirmed')
}),
const FxiOSV1AuthenticationBroker = FxDesktopV1AuthenticationBroker.extend({
defaultCapabilities: _.extend({}, proto.defaultCapabilities, { defaultCapabilities: _.extend({}, proto.defaultCapabilities, {
chooseWhatToSyncCheckbox: false, chooseWhatToSyncCheckbox: false,
chooseWhatToSyncWebV1: true, chooseWhatToSyncWebV1: true,
convertExternalLinksToText: true convertExternalLinksToText: true
}), }),
createChannel () {
var channel = new FxDesktopChannel();
channel.initialize({
// Fx on iOS and functional tests will send messages from the
// content server itself. Accept messages from the content
// server to handle these cases.
origin: this.window.location.origin,
window: this.window
});
channel.on('error', this.trigger.bind(this, 'error'));
return channel;
},
afterResetPasswordConfirmationPoll (account) {
// We wouldn't expect `customizeSync` to be set when completing
// a password reset, but the field must be present for the login
// message to be sent. false is the default value set in
// lib/fxa-client.js if the value is not present.
// See #5528
if (! account.has('customizeSync')) {
account.set('customizeSync', false);
}
// fx-ios-v1 send a login message after reset password complete,
// assuming the user verifies in the same browser. fx-ios-v1
// do not support WebChannels, and the login message must be
// sent within about:accounts for the browser to receive it.
// Integrations that support WebChannel messages will send
// the login message from the verification tab, and for users
// of either integration that verify in a different browser,
// they will be asked to signin in this browser using the
// new password.
return this._notifyRelierOfLogin(account)
.then(() => proto.afterResetPasswordConfirmationPoll.call(this, account));
},
initialize (options = {}) { initialize (options = {}) {
proto.initialize.call(this, options); proto.initialize.call(this, options);
@ -36,15 +105,6 @@ define(function (require, exports, module) {
if (! this._supportsChooseWhatToSync(version)) { if (! this._supportsChooseWhatToSync(version)) {
this.setCapability('chooseWhatToSyncWebV1', false); this.setCapability('chooseWhatToSyncWebV1', false);
} }
// Fx for iOS allows the user to see the "confirm your email" screen,
// but never takes it away after the user verifies. Allow the poll
// so that the user sees the "Signup complete!" screen after they
// verify their email.
this.setBehavior(
'afterSignInConfirmationPoll', new NavigateBehavior('signin_confirmed'));
this.setBehavior(
'afterSignUpConfirmationPoll', new NavigateBehavior('signup_confirmed'));
}, },
/** /**

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

@ -1,169 +0,0 @@
/* 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/. */
define(function (require, exports, module) {
'use strict';
const chai = require('chai');
const FxDesktopV1AuthenticationBroker = require('models/auth_brokers/fx-desktop-v1');
const NullChannel = require('lib/channels/null');
const sinon = require('sinon');
const User = require('models/user');
const WindowMock = require('../../../mocks/window');
var assert = chai.assert;
describe('models/auth_brokers/fx-desktop-v1', function () {
var account;
var broker;
var channelMock;
var user;
var windowMock;
beforeEach(function () {
windowMock = new WindowMock();
channelMock = new NullChannel();
channelMock.send = sinon.spy(function () {
return Promise.resolve();
});
user = new User();
account = user.initAccount({
email: 'testuser@testuser.com',
keyFetchToken: 'key-fetch-token',
uid: 'uid',
unwrapBKey: 'unwrap-b-key'
});
broker = new FxDesktopV1AuthenticationBroker({
channel: channelMock,
window: windowMock
});
sinon.stub(broker, '_hasRequiredLoginFields').callsFake(() => true);
});
it('has the expected capabilities', () => {
assert.isTrue(broker.hasCapability('signup'));
assert.isTrue(broker.hasCapability('handleSignedInNotification'));
assert.isTrue(broker.hasCapability('emailVerificationMarketingSnippet'));
});
describe('createChannel', function () {
it('creates a channel', function () {
assert.ok(broker.createChannel());
});
});
describe('channel errors', function () {
it('are propagated outwards', function () {
var channel = broker.createChannel();
var errorSpy = sinon.spy();
broker.on('error', errorSpy);
var error = new Error('malformed message');
channel.trigger('error', error);
assert.isTrue(errorSpy.calledWith(error));
});
});
describe('afterLoaded', function () {
it('sends a `loaded` message', function () {
sinon.spy(broker, 'send');
return broker.afterLoaded()
.then(function () {
assert.isTrue(broker.send.calledWith('loaded'));
});
});
});
describe('afterSignIn', function () {
it('notifies the channel of login, halts by default', function () {
sinon.spy(broker, 'send');
return broker.afterSignIn(account)
.then(function (result) {
assert.isTrue(broker.send.calledWith('login'));
assert.isTrue(result.halt);
});
});
});
describe('afterSignInConfirmationPoll', () => {
it('halts by default', () => {
return broker.afterSignInConfirmationPoll(account)
.then((result) => {
assert.isTrue(result.halt);
});
});
});
describe('beforeSignUpConfirmationPoll', function () {
it('notifies the channel of login', function () {
sinon.spy(broker, 'send');
return broker.beforeSignUpConfirmationPoll(account)
.then((result) => {
assert.isTrue(broker.send.calledWith('login'));
});
});
});
describe('afterSignUpConfirmationPoll', () => {
it('halts by default', () => {
return broker.afterSignUpConfirmationPoll(account)
.then((result) => {
assert.isTrue(result.halt);
});
});
});
describe('afterResetPasswordConfirmationPoll', function () {
it('notifies the channel of login, halts by default', function () {
sinon.spy(broker, 'send');
// customizeSync is required to send the `login` message, but
// it won't be set because the user hasn't visited the signup/in
// page.
account.unset('customizeSync');
return broker.afterResetPasswordConfirmationPoll(account)
.then(function (result) {
assert.isTrue(broker.send.calledWith('login'));
assert.isTrue(broker.send.calledOnce);
const loginData = broker.send.args[0][1];
assert.isFalse(loginData.customizeSync);
assert.isTrue(result.halt);
});
});
});
describe('afterChangePassword', function () {
it('notifies the channel of change_password with the new login info', function () {
sinon.spy(broker, 'send');
return broker.afterChangePassword(account)
.then(function () {
assert.isTrue(broker.send.calledWith('change_password'));
});
});
});
describe('afterDeleteAccount', function () {
it('notifies the channel of delete_account', function () {
sinon.spy(broker, 'send');
account.set('uid', 'uid');
return broker.afterDeleteAccount(account)
.then(function () {
assert.isTrue(broker.send.calledWith('delete_account'));
});
});
});
});
});

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

@ -5,23 +5,24 @@
define(function (require, exports, module) { define(function (require, exports, module) {
'use strict'; 'use strict';
const Account = require('models/account');
const { assert } = require('chai'); const { assert } = require('chai');
const FxiOSAuthenticationBroker = require('models/auth_brokers/fx-ios-v1'); const FxiOSAuthenticationBroker = require('models/auth_brokers/fx-ios-v1');
const FxDesktopV1AuthenticationBroker = require('models/auth_brokers/fx-desktop-v1');
const NullChannel = require('lib/channels/null'); const NullChannel = require('lib/channels/null');
const Relier = require('models/reliers/relier'); const Relier = require('models/reliers/relier');
const sinon = require('sinon'); const sinon = require('sinon');
const User = require('models/user');
const WindowMock = require('../../../mocks/window'); const WindowMock = require('../../../mocks/window');
const IMMEDIATE_UNVERIFIED_LOGIN_UA_STRING = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/6.1 Mobile/12F69 Safari/600.1.4'; //eslint-disable-line max-len const IMMEDIATE_UNVERIFIED_LOGIN_UA_STRING = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/6.1 Mobile/12F69 Safari/600.1.4'; //eslint-disable-line max-len
const CHOOSE_WHAT_TO_SYNC_UA_STRING = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/11.0 Mobile/12F69 Safari/600.1.4'; //eslint-disable-line max-len const CHOOSE_WHAT_TO_SYNC_UA_STRING = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/11.0 Mobile/12F69 Safari/600.1.4'; //eslint-disable-line max-len
describe('models/auth_brokers/fx-ios-v1', () => { describe('models/auth_brokers/fx-ios-v1', () => {
let account;
let broker; let broker;
let channel; let channel;
const loginMessageDelayMS = 250; const loginMessageDelayMS = 250;
let relier; let relier;
let user;
let windowMock; let windowMock;
let sandbox; let sandbox;
@ -33,6 +34,14 @@ define(function (require, exports, module) {
relier: relier, relier: relier,
window: windowMock window: windowMock
}); });
user = new User();
account = user.initAccount({
email: 'testuser@testuser.com',
keyFetchToken: 'key-fetch-token',
uid: 'uid',
unwrapBKey: 'unwrap-b-key'
});
sandbox.stub(broker, '_hasRequiredLoginFields').callsFake(() => true); sandbox.stub(broker, '_hasRequiredLoginFields').callsFake(() => true);
} }
@ -80,15 +89,10 @@ define(function (require, exports, module) {
}); });
describe('_notifyRelierOfLogin', () => { describe('_notifyRelierOfLogin', () => {
let account;
beforeEach(() => { beforeEach(() => {
sandbox.stub(broker, 'send').callsFake(() => Promise.resolve()); sandbox.stub(broker, 'send').callsFake(() => Promise.resolve());
sandbox.spy(windowMock, 'setTimeout'); sandbox.spy(windowMock, 'setTimeout');
sandbox.spy(windowMock, 'clearTimeout'); sandbox.spy(windowMock, 'clearTimeout');
account = new Account({
uid: 'uid'
});
}); });
function testLoginSent(triggerLoginCB) { function testLoginSent(triggerLoginCB) {
@ -126,23 +130,137 @@ define(function (require, exports, module) {
}); });
describe('afterCompleteSignInWithCode', () => { describe('afterCompleteSignInWithCode', () => {
let account;
beforeEach(() => { beforeEach(() => {
sandbox.spy(broker, 'afterCompleteSignInWithCode'); sandbox.spy(broker, 'afterCompleteSignInWithCode');
sandbox.spy(broker, '_notifyRelierOfLogin'); sandbox.spy(broker, '_notifyRelierOfLogin');
sandbox.spy(FxDesktopV1AuthenticationBroker.prototype, 'afterCompleteSignInWithCode'); sandbox.spy(FxiOSAuthenticationBroker.prototype, 'afterCompleteSignInWithCode');
account = new Account({
uid: 'uid'
});
return broker.afterCompleteSignInWithCode(account); return broker.afterCompleteSignInWithCode(account);
}); });
it('broker calls correct methods', () => { it('broker calls correct methods', () => {
assert.isTrue(FxDesktopV1AuthenticationBroker.prototype.afterCompleteSignInWithCode.called); assert.isTrue(broker.afterCompleteSignInWithCode.called);
assert.isTrue(broker._notifyRelierOfLogin.called); assert.isTrue(broker._notifyRelierOfLogin.called);
}); });
}); });
}); });
describe('createChannel', () => {
it('creates a channel', () => {
assert.ok(broker.createChannel());
});
});
describe('channel errors', () => {
it('are propagated outwards', () => {
var channel = broker.createChannel();
var errorSpy = sinon.spy();
broker.on('error', errorSpy);
var error = new Error('malformed message');
channel.trigger('error', error);
assert.isTrue(errorSpy.calledWith(error));
});
});
describe('afterLoaded', () => {
it('sends a `loaded` message', () => {
sinon.spy(broker, 'send');
return broker.afterLoaded()
.then(() => {
assert.isTrue(broker.send.calledWith('loaded'));
});
});
});
describe('afterSignIn', () => {
it('notifies the channel of login, halts by default', () => {
sinon.spy(broker, 'send');
return broker.afterSignIn(account)
.then((result) => {
assert.isTrue(broker.send.calledWith('login'));
assert.isTrue(result.halt);
});
});
});
describe('afterSignInConfirmationPoll', () => {
it('navigates to `signin_confirmed` by default', () => {
return broker.afterSignInConfirmationPoll(account)
.then(() => {
assert.equal(broker.getBehavior('afterSignInConfirmationPoll').type, 'navigate');
assert.equal(broker.getBehavior('afterSignInConfirmationPoll').endpoint, 'signin_confirmed');
});
});
});
describe('beforeSignUpConfirmationPoll', () => {
it('notifies the channel of login', () => {
sinon.spy(broker, 'send');
return broker.beforeSignUpConfirmationPoll(account)
.then(() => {
assert.isTrue(broker.send.calledWith('login'));
});
});
});
describe('afterSignUpConfirmationPoll', () => {
it('navigates to `signup_confirmed` by default', () => {
return broker.afterSignUpConfirmationPoll(account)
.then(() => {
assert.equal(broker.getBehavior('afterSignUpConfirmationPoll').type, 'navigate');
assert.equal(broker.getBehavior('afterSignUpConfirmationPoll').endpoint, 'signup_confirmed');
});
});
});
describe('afterResetPasswordConfirmationPoll', () => {
it('notifies the channel of login, halts by default', () => {
sinon.spy(broker, 'send');
// customizeSync is required to send the `login` message, but
// it won't be set because the user hasn't visited the signup/in
// page.
account.unset('customizeSync');
return broker.afterResetPasswordConfirmationPoll(account)
.then((result) => {
assert.isTrue(broker.send.calledWith('login'));
assert.isTrue(broker.send.calledOnce);
const loginData = broker.send.args[0][1];
assert.isFalse(loginData.customizeSync);
assert.isTrue(result.halt);
});
});
});
describe('afterChangePassword', () => {
it('notifies the channel of change_password with the new login info', () => {
sinon.spy(broker, 'send');
return broker.afterChangePassword(account)
.then(() => {
assert.isTrue(broker.send.calledWith('change_password'));
});
});
});
describe('afterDeleteAccount', () => {
it('notifies the channel of delete_account', () => {
sinon.spy(broker, 'send');
account.set('uid', 'uid');
return broker.afterDeleteAccount(account)
.then(() => {
assert.isTrue(broker.send.calledWith('delete_account'));
});
});
});
}); });
}); });

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

@ -84,7 +84,6 @@ require('./spec/lib/xss');
require('./spec/models/account'); require('./spec/models/account');
require('./spec/models/attached-clients'); require('./spec/models/attached-clients');
require('./spec/models/auth_brokers/base'); require('./spec/models/auth_brokers/base');
require('./spec/models/auth_brokers/fx-desktop-v1');
require('./spec/models/auth_brokers/fx-desktop-v2'); require('./spec/models/auth_brokers/fx-desktop-v2');
require('./spec/models/auth_brokers/fx-desktop-v3'); require('./spec/models/auth_brokers/fx-desktop-v3');
require('./spec/models/auth_brokers/fx-fennec-v1'); require('./spec/models/auth_brokers/fx-fennec-v1');