Commit 3fb85eb7 authored by xiyuan's avatar xiyuan Committed by Commit bot

cros: Pass gaia_auth init params via postMessage.

- Pass gaia auth init params via postMessage instead of URL params;
- Validate message origins on both ends to ensure gaia auth only
  takes params from intended hosting pages and hosting pages only
  send params to gaia_auth;
- Add string data storage to GaiaAuthExtensionLoader;
- Use the string data storage to pass frameURL for switchToFullTab;
- Update CSP in manifests to be more restrictive;
- Clean up unused code in util.js;

BUG=453994

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

Cr-Commit-Position: refs/heads/master@{#314905}
parent a490eab9
......@@ -86,7 +86,7 @@ void UnloadGaiaAuthExtension(BrowserContext* context) {
namespace extensions {
GaiaAuthExtensionLoader::GaiaAuthExtensionLoader(BrowserContext* context)
: browser_context_(context), load_count_(0) {}
: browser_context_(context), load_count_(0), last_data_id_(0) {}
GaiaAuthExtensionLoader::~GaiaAuthExtensionLoader() {
DCHECK_EQ(0, load_count_);
......@@ -100,8 +100,25 @@ void GaiaAuthExtensionLoader::LoadIfNeeded() {
void GaiaAuthExtensionLoader::UnloadIfNeeded() {
--load_count_;
if (load_count_ == 0)
if (load_count_ == 0) {
UnloadGaiaAuthExtension(browser_context_);
data_.clear();
}
}
int GaiaAuthExtensionLoader::AddData(const std::string& data) {
++last_data_id_;
data_[last_data_id_] = data;
return last_data_id_;
}
bool GaiaAuthExtensionLoader::GetData(int data_id, std::string* data) {
auto it = data_.find(data_id);
if (it == data_.end())
return false;
*data = it->second;
return true;
}
void GaiaAuthExtensionLoader::Shutdown() {
......@@ -109,6 +126,7 @@ void GaiaAuthExtensionLoader::Shutdown() {
UnloadGaiaAuthExtension(browser_context_);
load_count_ = 0;
}
data_.clear();
}
// static
......
......@@ -5,7 +5,10 @@
#ifndef CHROME_BROWSER_EXTENSIONS_SIGNIN_GAIA_AUTH_EXTENSION_LOADER_H_
#define CHROME_BROWSER_EXTENSIONS_SIGNIN_GAIA_AUTH_EXTENSION_LOADER_H_
#include "base/memory/scoped_ptr.h"
#include <map>
#include <string>
#include "base/macros.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
namespace content {
......@@ -29,6 +32,15 @@ class GaiaAuthExtensionLoader : public BrowserContextKeyedAPI {
// Unload the gaia auth extension if no pending reference.
void UnloadIfNeeded();
// Add a string data for gaia auth extension. Returns an ID that
// could be used to get the data. All strings are cleared when gaia auth
// is unloaded.
int AddData(const std::string& data);
// Get data for the given ID. Returns true if the data is found and
// its value is copied to |data|. Otherwise, returns false.
bool GetData(int data_id, std::string* data);
static GaiaAuthExtensionLoader* Get(content::BrowserContext* context);
// BrowserContextKeyedAPI implementation.
......@@ -50,6 +62,9 @@ class GaiaAuthExtensionLoader : public BrowserContextKeyedAPI {
content::BrowserContext* browser_context_;
int load_count_;
int last_data_id_;
std::map<int, std::string> data_;
DISALLOW_COPY_AND_ASSIGN(GaiaAuthExtensionLoader);
};
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/signin/gaia_auth_extension_loader.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
namespace extensions {
namespace {
const char kTestData1[] = "A test string";
const char kTestData2[] = "Another test string";
} // namespace
typedef InProcessBrowserTest GaiaAuthExtensionLoaderTest;
IN_PROC_BROWSER_TEST_F(GaiaAuthExtensionLoaderTest, AddAndGet) {
GaiaAuthExtensionLoader* loader = GaiaAuthExtensionLoader::Get(
browser()->profile());
loader->LoadIfNeeded();
int id1 = loader->AddData(kTestData1);
int id2 = loader->AddData(kTestData2);
EXPECT_NE(id1, id2);
std::string fetched;
EXPECT_TRUE(loader->GetData(id1, &fetched));
EXPECT_EQ(kTestData1, fetched);
EXPECT_TRUE(loader->GetData(id2, &fetched));
EXPECT_EQ(kTestData2, fetched);
const int kUnknownId = 1234;
EXPECT_FALSE(loader->GetData(kUnknownId, &fetched));
loader->UnloadIfNeeded();
}
IN_PROC_BROWSER_TEST_F(GaiaAuthExtensionLoaderTest, ClearDataOnUnload) {
GaiaAuthExtensionLoader* loader = GaiaAuthExtensionLoader::Get(
browser()->profile());
loader->LoadIfNeeded();
int id = loader->AddData(kTestData1);
std::string fetched;
EXPECT_TRUE(loader->GetData(id, &fetched));
loader->UnloadIfNeeded();
EXPECT_FALSE(loader->GetData(id, &fetched));
}
} // namespace extensions
......@@ -22,6 +22,11 @@ login.createScreen('OAuthEnrollmentScreen', 'oauth-enrollment', function() {
*/
signInUrl_: null,
/**
* Gaia auth params for sign in frame.
*/
signInParams_: {},
/**
* The current step. This is the last value passed to showStep().
*/
......@@ -132,18 +137,17 @@ login.createScreen('OAuthEnrollmentScreen', 'oauth-enrollment', function() {
* URL.
*/
onBeforeShow: function(data) {
var url = data.signin_url;
url += '?gaiaUrl=' + encodeURIComponent(data.gaiaUrl);
url += '&needPassword=0';
this.signInUrl_ = url;
this.signInParams_ = {};
this.signInParams_['gaiaUrl'] = data.gaiaUrl;
this.signInParams_['needPassword'] = false;
this.signInUrl_ = data.signin_url;
var modes = ['manual', 'forced', 'recovery'];
for (var i = 0; i < modes.length; ++i) {
this.classList.toggle('mode-' + modes[i],
data.enrollment_mode == modes[i]);
}
this.managementDomain_ = data.management_domain;
$('oauth-enroll-signin-frame').contentWindow.location.href =
this.signInUrl_;
this.doReload();
this.learnMoreHelpTopicID_ = data.learn_more_help_topic_id;
this.updateLocalizedContent();
this.showStep(STEP_SIGNIN);
......@@ -187,8 +191,16 @@ login.createScreen('OAuthEnrollmentScreen', 'oauth-enrollment', function() {
},
doReload: function() {
$('oauth-enroll-signin-frame').contentWindow.location.href =
this.signInUrl_;
var signInFrame = $('oauth-enroll-signin-frame');
var sendParamsOnLoad = function() {
signInFrame.removeEventListener('load', sendParamsOnLoad);
signInFrame.contentWindow.postMessage(this.signInParams_,
'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik');
}.bind(this);
signInFrame.addEventListener('load', sendParamsOnLoad);
signInFrame.contentWindow.location.href = this.signInUrl_;
},
/**
......
......@@ -35,6 +35,15 @@ Authenticator.API_KEY_TYPES = [
'KEY_TYPE_PASSWORD_PLAIN',
];
/**
* Allowed origins of the hosting page.
* @type {Array.<string>}
*/
Authenticator.ALLOWED_PARENT_ORIGINS = [
'chrome://oobe',
'chrome://chrome-signin'
];
/**
* Singleton getter of Authenticator.
* @return {Object} The singleton instance of Authenticator.
......@@ -73,23 +82,32 @@ Authenticator.prototype = {
GAIA_URL: 'https://accounts.google.com/',
GAIA_PAGE_PATH: 'ServiceLogin?skipvpage=true&sarp=1&rm=hide',
PARENT_PAGE: 'chrome://oobe/',
SERVICE_ID: 'chromeoslogin',
CONTINUE_URL: Authenticator.THIS_EXTENSION_ORIGIN + '/success.html',
CONSTRAINED_FLOW_SOURCE: 'chrome',
initialize: function() {
var params = getUrlSearchParams(location.search);
this.parentPage_ = params.parentPage || this.PARENT_PAGE;
this.gaiaUrl_ = params.gaiaUrl || this.GAIA_URL;
var handleInitializeMessage = function(e) {
if (Authenticator.ALLOWED_PARENT_ORIGINS.indexOf(e.origin) == -1) {
console.error('Unexpected parent message, origin=' + e.origin);
return;
}
window.removeEventListener('message', handleInitializeMessage);
// Sanitize Gaia url before continuing.
var scheme = extractProtocol(this.gaiaUrl_);
if (scheme != 'https:' && scheme != 'http:') {
console.error('Bad Gaia URL, url=' + this.gaiaURL_);
return;
}
var params = e.data;
params.parentPage = e.origin;
this.initializeFromParent_(params);
this.onPageLoad_();
}.bind(this);
document.addEventListener('DOMContentLoaded', function() {
window.addEventListener('message', handleInitializeMessage);
});
},
initializeFromParent_: function(params) {
this.parentPage_ = params.parentPage;
this.gaiaUrl_ = params.gaiaUrl || this.GAIA_URL;
this.gaiaPath_ = params.gaiaPath || this.GAIA_PAGE_PATH;
this.inputLang_ = params.hl;
this.inputEmail_ = params.email;
......@@ -108,8 +126,6 @@ Authenticator.prototype = {
this.assumeLoadedOnLoadEvent_ =
this.gaiaPath_.indexOf('ServiceLogin') !== 0 ||
this.service_ !== 'chromeoslogin';
document.addEventListener('DOMContentLoaded', this.onPageLoad_.bind(this));
},
isGaiaMessage_: function(msg) {
......
......@@ -17,7 +17,7 @@
"all_frames": true
}
],
"content_security_policy": "default-src 'self'; script-src 'self'; frame-src *; style-src 'self' 'unsafe-inline'",
"content_security_policy": "default-src 'self'; script-src 'self'; frame-src 'self' http: https:; style-src 'self'",
"description": "GAIA Component Extension",
"incognito": "split",
"web_accessible_resources": [
......
......@@ -25,7 +25,7 @@
"all_frames": true
}
],
"content_security_policy": "default-src 'self'; script-src 'self'; frame-src *; style-src 'self' 'unsafe-inline'",
"content_security_policy": "default-src 'self'; script-src 'self'; frame-src 'self' http: https:; style-src 'self'",
"description": "GAIA Component Extension",
"incognito": "split",
"web_accessible_resources": [
......
......@@ -6,9 +6,11 @@
* @fileoverview Offline login implementation.
*/
function load() {
var params = getUrlSearchParams(location.search);
/**
* Initialize the offline page.
* @param {Object} params Intialization params passed from parent page.
*/
function load(params) {
// Setup localized strings.
$('sign-in-title').textContent = decodeURIComponent(params['stringSignIn']);
$('email-label').textContent = decodeURIComponent(params['stringEmail']);
......@@ -59,4 +61,26 @@ function load() {
window.parent.postMessage({'method': 'loginUILoaded'}, 'chrome://oobe/');
}
document.addEventListener('DOMContentLoaded', load);
/**
* Handles initialization message from parent page.
* @param {MessageEvent} e
*/
function handleInitializeMessage(e) {
var ALLOWED_PARENT_ORIGINS = [
'chrome://oobe',
'chrome://chrome-signin'
];
if (ALLOWED_PARENT_ORIGINS.indexOf(e.origin) == -1)
return;
window.removeEventListener('message', handleInitializeMessage);
var params = e.data;
params.parentPage = e.origin;
load(params);
}
document.addEventListener('DOMContentLoaded', function() {
window.addEventListener('message', handleInitializeMessage);
});
......@@ -11,31 +11,6 @@ function $(id) {
return document.getElementById(id);
}
/**
* Extract query params from given search string of an URL.
* @param {string} search The search portion of an URL to extract params.
* @return {Object} The key value pairs of the extracted params.
*/
function getUrlSearchParams(search) {
var params = {};
if (search) {
// Strips leading '?'
search = search.substring(1);
var pairs = search.split('&');
for (var i = 0; i < pairs.length; ++i) {
var pair = pairs[i].split('=');
if (pair.length == 2) {
params[pair[0]] = decodeURIComponent(pair[1]);
} else {
params[pair] = true;
}
}
}
return params;
}
/**
* Creates a new URL which is the old URL with a GET param of key=value.
* Copied from ui/webui/resources/js/util.js.
......@@ -72,15 +47,3 @@ function extractDomain(url) {
a.href = url;
return a.hostname;
}
/**
* Extract protocol from an URL.
* @param {string} url An URL string.
* @return {string} The protocol of the URL.
*/
function extractProtocol(url) {
var a = document.createElement('a');
a.href = url;
return a.protocol;
}
......@@ -109,6 +109,12 @@ cr.define('cr.login', function() {
GaiaAuthHost.prototype = {
__proto__: cr.EventTarget.prototype,
/**
* Auth extension params
* @type {Object}
*/
authParams_: {},
/**
* An url to use with {@code reload}.
* @type {?string}
......@@ -237,7 +243,7 @@ cr.define('cr.login', function() {
* invoked with a credential object.
*/
load: function(authMode, data, successCallback) {
var params = [];
var params = {};
var populateParams = function(nameList, values) {
if (!values)
......@@ -246,14 +252,13 @@ cr.define('cr.login', function() {
for (var i in nameList) {
var name = nameList[i];
if (values[name])
params.push(name + '=' + encodeURIComponent(values[name]));
params[name] = values[name];
}
};
populateParams(SUPPORTED_PARAMS, data);
populateParams(LOCALIZED_STRING_PARAMS, data.localizedStrings);
params.push('parentPage=' + encodeURIComponent(window.location.origin));
params.push('needPassword=1');
params['needPassword'] = true;
var url;
switch (authMode) {
......@@ -262,23 +267,29 @@ cr.define('cr.login', function() {
break;
case AuthMode.DESKTOP:
url = AUTH_URL;
params.push('desktopMode=1');
params['desktopMode'] = true;
break;
default:
url = AUTH_URL;
}
url += '?' + params.join('&');
this.frame_.src = url;
this.authParams_ = params;
this.reloadUrl_ = url;
this.successCallback_ = successCallback;
this.authFlow = AuthFlow.GAIA;
this.reload();
},
/**
* Reloads the auth extension.
*/
reload: function() {
var sendParamsOnLoad = function() {
this.frame_.removeEventListener('load', sendParamsOnLoad);
this.frame_.contentWindow.postMessage(this.authParams_, AUTH_URL_BASE);
}.bind(this);
this.frame_.addEventListener('load', sendParamsOnLoad);
this.frame_.src = this.reloadUrl_;
this.authFlow = AuthFlow.GAIA;
},
......
......@@ -6,9 +6,11 @@
#include "base/bind.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/signin/gaia_auth_extension_loader.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/ui/browser_navigator.h"
......@@ -60,11 +62,12 @@ void InlineLoginHandler::HandleInitializeMessage(const base::ListValue* args) {
signin::GetLandingURL(signin::kSignInPromoQueryKeySource,
static_cast<int>(source)).spec());
Profile* profile = Profile::FromWebUI(web_ui());
std::string default_email;
if (source != signin_metrics::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT &&
source != signin_metrics::SOURCE_REAUTH) {
default_email = Profile::FromWebUI(web_ui())->GetPrefs()->GetString(
prefs::kGoogleServicesLastUsername);
default_email =
profile->GetPrefs()->GetString(prefs::kGoogleServicesLastUsername);
} else {
if (!net::GetValueForKeyInQuery(current_url, "email", &default_email))
default_email.clear();
......@@ -72,10 +75,16 @@ void InlineLoginHandler::HandleInitializeMessage(const base::ListValue* args) {
if (!default_email.empty())
params.SetString("email", default_email);
std::string frame_url_id_str;
net::GetValueForKeyInQuery(current_url, "frameUrlId", &frame_url_id_str);
int frame_url_id;
std::string frame_url;
net::GetValueForKeyInQuery(current_url, "frameUrl", &frame_url);
if (!frame_url.empty())
if (!frame_url_id_str.empty() &&
base::StringToInt(frame_url_id_str, &frame_url_id) &&
extensions::GaiaAuthExtensionLoader::Get(profile)
->GetData(frame_url_id, &frame_url)) {
params.SetString("frameUrl", frame_url);
}
std::string is_constrained;
net::GetValueForKeyInQuery(
......@@ -101,13 +110,17 @@ void InlineLoginHandler::HandleCompleteLoginMessage(
void InlineLoginHandler::HandleSwitchToFullTabMessage(
const base::ListValue* args) {
base::string16 url_str;
std::string url_str;
CHECK(args->GetString(0, &url_str));
Profile* profile = Profile::FromWebUI(web_ui());
const int frame_url_id =
extensions::GaiaAuthExtensionLoader::Get(profile)->AddData(url_str);
content::WebContents* web_contents = web_ui()->GetWebContents();
GURL main_frame_url(web_contents->GetURL());
main_frame_url = net::AppendOrReplaceQueryParameter(
main_frame_url, "frameUrl", base::UTF16ToASCII(url_str));
main_frame_url, "frameUrlId", base::IntToString(frame_url_id));
// Adds extra parameters to the signin URL so that Chrome will close the tab
// and show the account management view of the avatar menu upon completion.
......@@ -117,7 +130,7 @@ void InlineLoginHandler::HandleSwitchToFullTabMessage(
main_frame_url, signin::kSignInPromoQueryKeyShowAccountManagement, "1");
chrome::NavigateParams params(
Profile::FromWebUI(web_ui()),
profile,
net::AppendOrReplaceQueryParameter(
main_frame_url, signin::kSignInPromoQueryKeyConstrained, "0"),
ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
......
......@@ -273,6 +273,7 @@
'browser/extensions/requirements_checker_browsertest.cc',
'browser/extensions/sandboxed_pages_apitest.cc',
'browser/extensions/shared_module_apitest.cc',
'browser/extensions/signin/gaia_auth_extension_loader_browsertest.cc',
'browser/extensions/startup_helper_browsertest.cc',
'browser/extensions/stubs_apitest.cc',
'browser/extensions/subscribe_page_action_browsertest.cc',
......
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