Commit 5e4c4c5a authored by kelvinp's avatar kelvinp Committed by Commit bot

Use the local user's email when connecting to It2MeHelpeeChannel

An It2Me connection requires an email for domain policy check.
We should use the email of the helpee instead of the helper.

Summary of changes:
1. Modifies it2meHelpeeChannel to fetch its token and the user's credentials
   using remoting.identity.
2. Implement the background page as an object to initialize remoting.identity
   appropriately.
3. Makes the consentDialog argument optional in remoting.identity as user
   consent is suggested as opposed to required in the chrome identity API.

BUG=454897

Review URL: https://codereview.chromium.org/896003002

Cr-Commit-Position: refs/heads/master@{#314651}
parent be606d08
...@@ -241,6 +241,7 @@ ...@@ -241,6 +241,7 @@
'webapp/crd/js/hangout_consent_dialog.js', 'webapp/crd/js/hangout_consent_dialog.js',
'webapp/crd/js/host_installer.js', 'webapp/crd/js/host_installer.js',
'webapp/crd/js/host_session.js', 'webapp/crd/js/host_session.js',
'webapp/crd/js/identity.js',
'webapp/crd/js/it2me_helpee_channel.js', 'webapp/crd/js/it2me_helpee_channel.js',
'webapp/crd/js/it2me_helper_channel.js', 'webapp/crd/js/it2me_helper_channel.js',
'webapp/crd/js/it2me_host_facade.js', 'webapp/crd/js/it2me_host_facade.js',
......
...@@ -78,6 +78,7 @@ base.Disposables.prototype.dispose = function() { ...@@ -78,6 +78,7 @@ base.Disposables.prototype.dispose = function() {
for (var i = 0; i < this.disposables_.length; i++) { for (var i = 0; i < this.disposables_.length; i++) {
this.disposables_[i].dispose(); this.disposables_[i].dispose();
} }
this.disposables_ = null;
}; };
/** /**
......
...@@ -9,45 +9,71 @@ var remoting = remoting || {}; ...@@ -9,45 +9,71 @@ var remoting = remoting || {};
'use strict'; 'use strict';
var ENABLE_HANGOUT_REMOTE_ASSISTANCE = true;
/** /**
* The background service is responsible for listening to incoming connection * @constructor
* requests from Hangouts and the webapp.
*
* @param {remoting.AppLauncher} appLauncher
*/ */
function initializeBackgroundService(appLauncher) { var BackgroundPage = function() {
function initializeIt2MeService() { /** @private {remoting.AppLauncher} */
/** @type {remoting.It2MeService} */ this.appLauncher_ = null;
remoting.it2meService = new remoting.It2MeService(appLauncher); /** @private {remoting.ActivationHandler} */
remoting.it2meService.init(); this.activationHandler_ = null;
} /** @private {remoting.It2MeService} */
this.it2meService_ = null;
/** @private {base.Disposables} */
this.disposables_ = null;
chrome.runtime.onSuspend.addListener(function() { this.preInit_();
base.debug.assert(remoting.it2meService != null); this.onResumed_();
remoting.it2meService.dispose();
remoting.it2meService = null;
});
remoting.settings = new remoting.Settings(); chrome.runtime.onSuspendCanceled.addListener(this.onResumed_.bind(this));
chrome.runtime.onSuspend.addListener(this.onSuspended_.bind(this));
};
chrome.runtime.onSuspendCanceled.addListener(initializeIt2MeService); /**
initializeIt2MeService(); * Initialize members and globals that are valid throughout the entire lifetime
} * of the background page.
*
* @private
*/
BackgroundPage.prototype.preInit_ = function() {
remoting.settings = new remoting.Settings();
if (base.isAppsV2()) {
remoting.identity = new remoting.Identity();
} else {
remoting.oauth2 = new remoting.OAuth2();
var oauth2 = /** @type {*} */ (remoting.oauth2);
remoting.identity = /** @type {remoting.Identity} */ (oauth2);
}
function main() {
if (base.isAppsV2()) { if (base.isAppsV2()) {
new remoting.ActivationHandler(base.Ipc.getInstance(), this.appLauncher_ = new remoting.V2AppLauncher();
new remoting.V2AppLauncher()); this.activationHandler_ = new remoting.ActivationHandler(
base.Ipc.getInstance(), this.appLauncher_);
} else {
this.appLauncher_ = new remoting.V1AppLauncher();
} }
} };
/** @private */
BackgroundPage.prototype.onResumed_ = function() {
if (ENABLE_HANGOUT_REMOTE_ASSISTANCE) {
this.it2meService_ = new remoting.It2MeService(this.appLauncher_);
this.it2meService_.init();
this.disposables_ = new base.Disposables(this.it2meService_);
}
};
remoting.enableHangoutsRemoteAssistance = function() { /** @private */
/** @type {remoting.AppLauncher} */ BackgroundPage.prototype.onSuspended_ = function() {
var appLauncher = base.isAppsV2() ? new remoting.V1AppLauncher(): this.it2meService_ = null;
new remoting.V2AppLauncher(); base.dispose(this.disposables_);
initializeBackgroundService(appLauncher); this.disposables_ = null;
}; };
window.addEventListener('load', main, false); window.addEventListener('load', function() {
remoting.backgroundPage = new BackgroundPage();
}, false);
}()); }());
...@@ -21,12 +21,12 @@ var remoting = remoting || {}; ...@@ -21,12 +21,12 @@ var remoting = remoting || {};
remoting.identity = null; remoting.identity = null;
/** /**
* @param {remoting.Identity.ConsentDialog} consentDialog * @param {remoting.Identity.ConsentDialog=} opt_consentDialog
* @constructor * @constructor
*/ */
remoting.Identity = function(consentDialog) { remoting.Identity = function(opt_consentDialog) {
/** @private */ /** @private */
this.consentDialog_ = consentDialog; this.consentDialog_ = opt_consentDialog;
/** @type {string} @private */ /** @type {string} @private */
this.email_ = ''; this.email_ = '';
/** @type {string} @private */ /** @type {string} @private */
...@@ -36,7 +36,7 @@ remoting.Identity = function(consentDialog) { ...@@ -36,7 +36,7 @@ remoting.Identity = function(consentDialog) {
}; };
/** /**
* chrome.identity.getAuthToken must be initiated from user interactions if * chrome.identity.getAuthToken should be initiated from user interactions if
* called with interactive equals true. This interface prompts a dialog for * called with interactive equals true. This interface prompts a dialog for
* the user's consent. * the user's consent.
* *
...@@ -223,7 +223,9 @@ remoting.Identity.prototype.onAuthComplete_ = function(interactive, token) { ...@@ -223,7 +223,9 @@ remoting.Identity.prototype.onAuthComplete_ = function(interactive, token) {
// If there's no token, but we haven't yet prompted for permission, do so // If there's no token, but we haven't yet prompted for permission, do so
// now. // now.
var that = this; var that = this;
this.consentDialog_.show().then(function() { var showConsentDialog =
(this.consentDialog_) ? this.consentDialog_.show() : Promise.resolve();
showConsentDialog.then(function() {
chrome.identity.getAuthToken({'interactive': true}, chrome.identity.getAuthToken({'interactive': true},
that.onAuthComplete_.bind(that, true)); that.onAuthComplete_.bind(that, true));
}); });
......
...@@ -249,33 +249,26 @@ remoting.It2MeHelpeeChannel.prototype.onHangoutDisconnect_ = function() { ...@@ -249,33 +249,26 @@ remoting.It2MeHelpeeChannel.prototype.onHangoutDisconnect_ = function() {
*/ */
remoting.It2MeHelpeeChannel.prototype.handleConnect_ = remoting.It2MeHelpeeChannel.prototype.handleConnect_ =
function(message) { function(message) {
var email = getStringAttr(message, 'email');
var bounds = var bounds =
/** @type {Bounds} */ (getObjectAttr(message, 'hangoutBounds', null)); /** @type {Bounds} */ (getObjectAttr(message, 'hangoutBounds', null));
if (!email) {
throw new Error('Missing required parameter: email');
}
if (this.hostState_ !== remoting.HostSession.State.UNKNOWN) { if (this.hostState_ !== remoting.HostSession.State.UNKNOWN) {
throw new Error('An existing connection is in progress.'); throw new Error('An existing connection is in progress.');
} }
var that = this; var that = this;
this.showConfirmDialog_(bounds).then( this.showConfirmDialog_(bounds)
this.initializeHost_.bind(this) .then(this.initializeHost_.bind(this))
).then( .then(this.fetchOAuthToken_.bind(this))
this.fetchOAuthToken_.bind(this) .then(this.fetchEmail_.bind(this))
).then( /** @param {{email:string, token:string}|Promise} result */
/** @type {function(*):void} */(this.connectToHost_.bind(this, email)) .then(function(result) {
).catch( that.connectToHost_(result.email, result.token);
/** @param {*} reason */ /** @param {*} reason */
function(reason) { }).catch(function(reason) {
var error = /** @type {Error} */ (reason); that.sendErrorResponse_(message, /** @type {Error} */ (reason));
that.sendErrorResponse_(message, error); that.dispose();
that.dispose(); });
}
);
}; };
/** /**
...@@ -368,9 +361,10 @@ remoting.It2MeHelpeeChannel.prototype.fetchOAuthToken_ = function() { ...@@ -368,9 +361,10 @@ remoting.It2MeHelpeeChannel.prototype.fetchOAuthToken_ = function() {
if (base.isAppsV2()) { if (base.isAppsV2()) {
/** /**
* @param {function(*=):void} resolve * @param {function(*=):void} resolve
* @param {function(*=):void} reject
*/ */
return new Promise(function(resolve){ return new Promise(function(resolve, reject){
chrome.identity.getAuthToken({'interactive': true}, resolve); remoting.identity.callWithToken(resolve, reject);
}); });
} else { } else {
/** /**
...@@ -378,23 +372,40 @@ remoting.It2MeHelpeeChannel.prototype.fetchOAuthToken_ = function() { ...@@ -378,23 +372,40 @@ remoting.It2MeHelpeeChannel.prototype.fetchOAuthToken_ = function() {
* @param {function(*=):void} reject * @param {function(*=):void} reject
*/ */
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
/** @type {remoting.OAuth2} */
var oauth2 = new remoting.OAuth2();
/** @param {remoting.Error} error */ /** @param {remoting.Error} error */
var onError = function(error) { var onError = function(error) {
if (error === remoting.Error.NOT_AUTHENTICATED) { if (error === remoting.Error.NOT_AUTHENTICATED) {
oauth2.doAuthRedirect(function() { remoting.oauth2.doAuthRedirect(function() {
oauth2.callWithToken(resolve, reject); remoting.identity.callWithToken(resolve, reject);
}); });
return; return;
} }
reject(new Error(remoting.Error.NOT_AUTHENTICATED)); reject(new Error(remoting.Error.NOT_AUTHENTICATED));
}; };
oauth2.callWithToken(resolve, onError); remoting.identity.callWithToken(resolve, onError);
}); });
} }
}; };
/**
* @param {string|Promise} token
* @return {Promise} Promise that resolves with the access token and the email
* of the user.
*/
remoting.It2MeHelpeeChannel.prototype.fetchEmail_ = function(token) {
/**
* @param {function(*=):void} resolve
* @param {function(*=):void} reject
*/
return new Promise(function(resolve, reject){
/** @param {string} email */
function onEmail (email) {
resolve({ email: email, token: token });
}
remoting.identity.getEmail(onEmail, reject);
});
};
/** /**
* Connects to the It2Me Native Messaging Host and retrieves the access code * Connects to the It2Me Native Messaging Host and retrieves the access code
* in the |onHostStateChanged_| callback. * in the |onHostStateChanged_| callback.
......
...@@ -16,7 +16,9 @@ var remoting = remoting || {}; ...@@ -16,7 +16,9 @@ var remoting = remoting || {};
/** /**
* @param {remoting.AppLauncher} appLauncher * @param {remoting.AppLauncher} appLauncher
*
* @constructor * @constructor
* @implements {base.Disposable}
*/ */
remoting.It2MeService = function(appLauncher) { remoting.It2MeService = function(appLauncher) {
/** /**
......
...@@ -47,6 +47,11 @@ module('It2MeHelpeeChannel', { ...@@ -47,6 +47,11 @@ module('It2MeHelpeeChannel', {
// remoting.settings // remoting.settings
remoting.settings = new remoting.Settings(); remoting.settings = new remoting.Settings();
remoting.identity = new remoting.Identity();
},
tearDown: function() {
remoting.settings = null;
remoting.identity = null;
} }
}); });
...@@ -112,19 +117,6 @@ test('downloadHost() should trigger a host download', ...@@ -112,19 +117,6 @@ test('downloadHost() should trigger a host download',
sinon.assert.called(hostInstaller.download); sinon.assert.called(hostInstaller.download);
}); });
test('connect() should return error if email is missing',
function() {
var MessageTypes = remoting.It2MeHelpeeChannel.HangoutMessageTypes;
hangoutPort.onMessage.mock$fire({
method: MessageTypes.CONNECT
});
sinon.assert.calledWithMatch(hangoutPort.postMessage, {
method: MessageTypes.ERROR
});
});
QUnit.asyncTest('connect() should return access code', QUnit.asyncTest('connect() should return access code',
function() { function() {
// Stubs authentication. // Stubs authentication.
...@@ -136,6 +128,10 @@ QUnit.asyncTest('connect() should return access code', ...@@ -136,6 +128,10 @@ QUnit.asyncTest('connect() should return access code',
}); });
sinon.stub(chrome.identity, 'getAuthToken') sinon.stub(chrome.identity, 'getAuthToken')
.callsArgWith(1, 'token'); .callsArgWith(1, 'token');
sinon.stub(remoting.identity, 'callWithToken')
.callsArgWith(0, 'token');
sinon.stub(remoting.identity, 'getEmail')
.callsArgWith(0, {token: 'token', email: 'test@chromium.org'});
// Stubs Host behavior. // Stubs Host behavior.
sinon.stub(host, 'initialized').returns(true); sinon.stub(host, 'initialized').returns(true);
sinon.stub(host, 'connect') sinon.stub(host, 'connect')
...@@ -145,7 +141,6 @@ QUnit.asyncTest('connect() should return access code', ...@@ -145,7 +141,6 @@ QUnit.asyncTest('connect() should return access code',
var MessageTypes = remoting.It2MeHelpeeChannel.HangoutMessageTypes; var MessageTypes = remoting.It2MeHelpeeChannel.HangoutMessageTypes;
hangoutPort.onMessage.mock$fire({ hangoutPort.onMessage.mock$fire({
method: MessageTypes.CONNECT, method: MessageTypes.CONNECT,
email: 'test@chromium.org',
hangoutBounds: {widht: 10, height: 10, left:10, top: 10} hangoutBounds: {widht: 10, height: 10, left:10, top: 10}
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment