diff options
-rw-r--r-- | chrome/browser/chromeos/login/kiosk_browsertest.cc | 159 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/oauth2_browsertest.cc | 179 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/oauth2_login_manager_factory.h | 1 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/oauth2_login_verifier.cc | 2 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/oobe_base_test.cc | 171 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/oobe_base_test.h | 70 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/oobe_browsertest.cc | 1 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/saml_browsertest.cc | 15 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 3 | ||||
-rw-r--r-- | google_apis/gaia/fake_gaia.cc | 439 | ||||
-rw-r--r-- | google_apis/gaia/fake_gaia.h | 64 |
11 files changed, 843 insertions, 261 deletions
diff --git a/chrome/browser/chromeos/login/kiosk_browsertest.cc b/chrome/browser/chromeos/login/kiosk_browsertest.cc index 4969217..dee046a 100644 --- a/chrome/browser/chromeos/login/kiosk_browsertest.cc +++ b/chrome/browser/chromeos/login/kiosk_browsertest.cc @@ -8,73 +8,33 @@ #include "ash/desktop_background/desktop_background_controller.h" #include "ash/desktop_background/desktop_background_controller_observer.h" #include "ash/shell.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/command_line.h" -#include "base/location.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/path_service.h" -#include "base/prefs/scoped_user_pref_update.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/chrome_browser_main.h" -#include "chrome/browser/chrome_browser_main_extra_parts.h" -#include "chrome/browser/chrome_content_browser_client.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" #include "chrome/browser/chromeos/login/app_launch_controller.h" -#include "chrome/browser/chromeos/login/app_launch_signin_screen.h" -#include "chrome/browser/chromeos/login/existing_user_controller.h" #include "chrome/browser/chromeos/login/fake_user_manager.h" -#include "chrome/browser/chromeos/login/login_display_host_impl.h" #include "chrome/browser/chromeos/login/mock_user_manager.h" +#include "chrome/browser/chromeos/login/oobe_base_test.h" #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h" -#include "chrome/browser/chromeos/login/webui_login_display.h" #include "chrome/browser/chromeos/login/wizard_controller.h" -#include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h" #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h" #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_test_message_listener.h" -#include "chrome/browser/lifetime/application_lifetime.h" -#include "chrome/browser/profiles/profile_manager.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" -#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h" -#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "chrome/test/base/interactive_test_utils.h" -#include "chrome/test/base/ui_test_utils.h" #include "chromeos/chromeos_switches.h" -#include "chromeos/settings/cros_settings_names.h" -#include "components/policy/core/common/cloud/policy_builder.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_service.h" #include "content/public/test/browser_test_utils.h" -#include "content/public/test/test_utils.h" -#include "extensions/common/extension.h" -#include "google_apis/gaia/fake_gaia.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_switches.h" #include "google_apis/gaia/gaia_urls.h" -#include "net/base/network_change_notifier.h" -#include "net/dns/mock_host_resolver.h" -#include "net/test/embedded_test_server/embedded_test_server.h" -#include "net/test/embedded_test_server/http_request.h" -#include "net/test/embedded_test_server/http_response.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/aura/window.h" -#include "ui/compositor/layer.h" namespace em = enterprise_management; @@ -111,9 +71,6 @@ const char kTestClientId[] = "fake-client-id"; const char kTestAppScope[] = "https://www.googleapis.com/auth/userinfo.profile"; -// Note the path name must be the same as in shill stub. -const char kStubEthernetServicePath[] = "eth1"; - // Helper function for GetConsumerKioskModeStatusCallback. void ConsumerKioskModeStatusCheck( KioskAppManager::ConsumerKioskModeStatus* out_status, @@ -231,7 +188,7 @@ class ShellWindowObserver : public apps::ShellWindowRegistry::Observer { DISALLOW_COPY_AND_ASSIGN(ShellWindowObserver); }; -class KioskTest : public InProcessBrowserTest { +class KioskTest : public OobeBaseTest { public: KioskTest() { set_exit_when_last_browser_closes(false); @@ -240,48 +197,20 @@ class KioskTest : public InProcessBrowserTest { virtual ~KioskTest() {} protected: - virtual void SetUp() OVERRIDE { - base::FilePath test_data_dir; - PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir); - embedded_test_server()->ServeFilesFromDirectory(test_data_dir); - embedded_test_server()->RegisterRequestHandler( - base::Bind(&FakeGaia::HandleRequest, base::Unretained(&fake_gaia_))); - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); - // Stop IO thread here because no threads are allowed while - // spawning sandbox host process. See crbug.com/322732. - embedded_test_server()->StopThread(); + virtual void SetUp() OVERRIDE { mock_user_manager_.reset(new MockUserManager); AppLaunchController::SkipSplashWaitForTesting(); AppLaunchController::SetNetworkWaitForTesting(kTestNetworkTimeoutSeconds); - InProcessBrowserTest::SetUp(); - } - - virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { - host_resolver()->AddRule("*", "127.0.0.1"); - - network_portal_detector_ = new NetworkPortalDetectorTestImpl(); - NetworkPortalDetector::InitializeForTesting(network_portal_detector_); - network_portal_detector_->SetDefaultNetworkPathForTesting( - kStubEthernetServicePath); - } - - virtual void SetUpOnMainThread() OVERRIDE { - // Restart the thread as the sandbox host process has already been spawned. - embedded_test_server()->RestartThreadAndListen(); + OobeBaseTest::SetUp(); } virtual void CleanUpOnMainThread() OVERRIDE { AppLaunchController::SetNetworkTimeoutCallbackForTesting(NULL); AppLaunchSigninScreen::SetUserManagerForTesting(NULL); - // If the login display is still showing, exit gracefully. - if (LoginDisplayHostImpl::default_host()) { - base::MessageLoop::current()->PostTask(FROM_HERE, - base::Bind(&chrome::AttemptExit)); - content::RunMessageLoop(); - } + OobeBaseTest::CleanUpOnMainThread(); // Clean up while main thread still runs. // See http://crbug.com/176659. @@ -289,25 +218,12 @@ class KioskTest : public InProcessBrowserTest { } virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { - command_line->AppendSwitch(chromeos::switches::kLoginManager); - command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests); - command_line->AppendSwitch(::switches::kDisableBackgroundNetworking); - command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user"); + OobeBaseTest::SetUpCommandLine(command_line); // Create gaia and webstore URL from test server url but using different // host names. This is to avoid gaia response being tagged as from // webstore in chrome_resource_dispatcher_host_delegate.cc. const GURL& server_url = embedded_test_server()->base_url(); - - std::string gaia_host("gaia"); - GURL::Replacements replace_gaia_host; - replace_gaia_host.SetHostStr(gaia_host); - GURL gaia_url = server_url.ReplaceComponents(replace_gaia_host); - command_line->AppendSwitchASCII(::switches::kGaiaUrl, gaia_url.spec()); - command_line->AppendSwitchASCII(::switches::kLsoUrl, gaia_url.spec()); - command_line->AppendSwitchASCII(::switches::kGoogleApisUrl, - gaia_url.spec()); - std::string webstore_host("webstore"); GURL::Replacements replace_webstore_host; replace_webstore_host.SetHostStr(webstore_host); @@ -408,46 +324,6 @@ class KioskTest : public InProcessBrowserTest { EXPECT_TRUE(launch_data_check_listener.was_satisfied()); } - void SimulateNetworkOffline() { - NetworkPortalDetector::CaptivePortalState offline_state; - offline_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE; - network_portal_detector_->SetDetectionResultsForTesting( - kStubEthernetServicePath, offline_state); - network_portal_detector_->NotifyObserversForTesting(); - } - - base::Closure SimulateNetworkOfflineClosure() { - return base::Bind(&KioskTest::SimulateNetworkOffline, - base::Unretained(this)); - } - - void SimulateNetworkOnline() { - NetworkPortalDetector::CaptivePortalState online_state; - online_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE; - online_state.response_code = 204; - network_portal_detector_->SetDetectionResultsForTesting( - kStubEthernetServicePath, online_state); - network_portal_detector_->NotifyObserversForTesting(); - } - - base::Closure SimulateNetworkOnlineClosure() { - return base::Bind(&KioskTest::SimulateNetworkOnline, - base::Unretained(this)); - } - - void SimulateNetworkPortal() { - NetworkPortalDetector::CaptivePortalState portal_state; - portal_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL; - network_portal_detector_->SetDetectionResultsForTesting( - kStubEthernetServicePath, portal_state); - network_portal_detector_->NotifyObserversForTesting(); - } - - base::Closure SimulateNetworkPortalClosure() { - return base::Bind(&KioskTest::SimulateNetworkPortal, - base::Unretained(this)); - } - void WaitForAppLaunchNetworkTimeout() { if (GetAppLaunchController()->network_wait_timedout()) return; @@ -491,35 +367,12 @@ class KioskTest : public InProcessBrowserTest { return status; } - void JsExpect(const std::string& expression) { - bool result; - ASSERT_TRUE(content::ExecuteScriptAndExtractBool( - GetLoginUI()->GetWebContents(), - "window.domAutomationController.send(!!(" + expression + "));", - &result)); - ASSERT_TRUE(result) << expression; - } - - content::WebUI* GetLoginUI() { - return static_cast<chromeos::LoginDisplayHostImpl*>( - chromeos::LoginDisplayHostImpl::default_host())->GetOobeUI()->web_ui(); - } - - SigninScreenHandler* GetSigninScreenHandler() { - return static_cast<chromeos::LoginDisplayHostImpl*>( - chromeos::LoginDisplayHostImpl::default_host()) - ->GetOobeUI() - ->signin_screen_handler_for_test(); - } - AppLaunchController* GetAppLaunchController() { return chromeos::LoginDisplayHostImpl::default_host() ->GetAppLaunchController(); } - FakeGaia fake_gaia_; scoped_ptr<MockUserManager> mock_user_manager_; - NetworkPortalDetectorTestImpl* network_portal_detector_; }; IN_PROC_BROWSER_TEST_F(KioskTest, InstallAndLaunchApp) { diff --git a/chrome/browser/chromeos/login/oauth2_browsertest.cc b/chrome/browser/chromeos/login/oauth2_browsertest.cc new file mode 100644 index 0000000..bbbed3e --- /dev/null +++ b/chrome/browser/chromeos/login/oauth2_browsertest.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2013 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 "base/message_loop/message_loop.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/chromeos/login/oauth2_login_manager.h" +#include "chrome/browser/chromeos/login/oauth2_login_manager_factory.h" +#include "chrome/browser/chromeos/login/oobe_base_test.h" +#include "chrome/browser/chromeos/login/wizard_controller.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h" +#include "content/public/browser/notification_service.h" +#include "google_apis/gaia/gaia_constants.h" +#include "google_apis/gaia/gaia_urls.h" + +namespace chromeos { + +namespace { + +// Email of owner account for test. +const char kTestAccountId[] = "username@gmail.com"; + +const char kTestAuthCode[] = "fake-auth-code"; +const char kTestGaiaUberToken[] = "fake-uber-token"; +const char kTestAuthLoginAccessToken[] = "fake-access-token"; +const char kTestRefreshToken[] = "fake-refresh-token"; +const char kTestSessionSIDCookie[] = "fake-session-SID-cookie"; +const char kTestSessionLSIDCookie[] = "fake-session-LSID-cookie"; +const char kTestUserinfoToken[] = "fake-userinfo-token"; +const char kTestLoginToken[] = "fake-login-token"; +const char kTestSyncToken[] = "fake-sync-token"; +const char kTestAuthLoginToken[] = "fake-oauthlogin-token"; + +} // namespace + +class OAuth2Test : public OobeBaseTest { + protected: + OAuth2Test() {} + + virtual void SetUpOnMainThread() OVERRIDE { + OobeBaseTest::SetUpOnMainThread(); + + // Configure OAuth authentication. + GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); + + fake_gaia_.SetAuthTokens(kTestAuthCode, + kTestRefreshToken, + kTestAuthLoginAccessToken, + kTestGaiaUberToken, + kTestSessionSIDCookie, + kTestSessionLSIDCookie); + // This token satisfies the userinfo.email request from + // DeviceOAuth2TokenService used in token validation. + FakeGaia::AccessTokenInfo userinfo_token_info; + userinfo_token_info.token = kTestUserinfoToken; + userinfo_token_info.scopes.insert( + "https://www.googleapis.com/auth/userinfo.email"); + userinfo_token_info.audience = gaia_urls->oauth2_chrome_client_id(); + userinfo_token_info.email = kTestAccountId; + fake_gaia_.IssueOAuthToken(kTestRefreshToken, userinfo_token_info); + + FakeGaia::AccessTokenInfo userinfo_profile_token_info; + userinfo_profile_token_info.token = kTestUserinfoToken; + userinfo_profile_token_info.scopes.insert( + "https://www.googleapis.com/auth/userinfo.profile"); + userinfo_profile_token_info.audience = gaia_urls->oauth2_chrome_client_id(); + userinfo_profile_token_info.email = kTestAccountId; + fake_gaia_.IssueOAuthToken(kTestRefreshToken, userinfo_profile_token_info); + + // The any-api access token for accessing the token minting endpoint. + FakeGaia::AccessTokenInfo login_token_info; + login_token_info.token = kTestLoginToken; + login_token_info.scopes.insert(GaiaConstants::kAnyApiOAuth2Scope); + login_token_info.audience = gaia_urls->oauth2_chrome_client_id(); + fake_gaia_.IssueOAuthToken(kTestRefreshToken, login_token_info); + + // The /auth/chromesync access token for accessing sync endpoint. + FakeGaia::AccessTokenInfo sync_token_info; + sync_token_info.token = kTestSyncToken; + sync_token_info.scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope); + sync_token_info.audience = gaia_urls->oauth2_chrome_client_id(); + fake_gaia_.IssueOAuthToken(kTestRefreshToken, sync_token_info); + + FakeGaia::AccessTokenInfo auth_login_token_info; + auth_login_token_info.token = kTestAuthLoginToken; + auth_login_token_info.scopes.insert(gaia_urls->oauth1_login_scope()); + auth_login_token_info.audience = gaia_urls->oauth2_chrome_client_id(); + fake_gaia_.IssueOAuthToken(kTestRefreshToken, auth_login_token_info); + } + + private: + DISALLOW_COPY_AND_ASSIGN(OAuth2Test); +}; + +class OAuth2LoginManagerStateWaiter : public OAuth2LoginManager::Observer { + public: + explicit OAuth2LoginManagerStateWaiter(Profile* profile) + : profile_(profile), + waiting_for_state_(false), + final_state_(OAuth2LoginManager::SESSION_RESTORE_NOT_STARTED) { + } + + void WaitForStates( + const std::set<OAuth2LoginManager::SessionRestoreState>& states) { + DCHECK(!waiting_for_state_); + OAuth2LoginManager* login_manager = + OAuth2LoginManagerFactory::GetInstance()->GetForProfile(profile_); + states_ = states; + if (states_.find(login_manager->state()) != states_.end()) { + final_state_ = login_manager->state(); + return; + } + + waiting_for_state_ = true; + login_manager->AddObserver(this); + runner_ = new content::MessageLoopRunner; + runner_->Run(); + login_manager->RemoveObserver(this); + } + + OAuth2LoginManager::SessionRestoreState final_state() { return final_state_; } + + private: + // OAuth2LoginManager::Observer overrides. + virtual void OnSessionRestoreStateChanged( + Profile* user_profile, + OAuth2LoginManager::SessionRestoreState state) OVERRIDE { + if (!waiting_for_state_) + return; + + if (states_.find(state) == states_.end()) + return; + + final_state_ = state; + waiting_for_state_ = false; + runner_->Quit(); + } + + Profile* profile_; + std::set<OAuth2LoginManager::SessionRestoreState> states_; + bool waiting_for_state_; + OAuth2LoginManager::SessionRestoreState final_state_; + scoped_refptr<content::MessageLoopRunner> runner_; + + DISALLOW_COPY_AND_ASSIGN(OAuth2LoginManagerStateWaiter); +}; + +IN_PROC_BROWSER_TEST_F(OAuth2Test, NewUser) { + SimulateNetworkOnline(); + chromeos::WizardController::SkipPostLoginScreensForTesting(); + chromeos::WizardController* wizard_controller = + chromeos::WizardController::default_controller(); + wizard_controller->SkipToLoginForTesting(LoginScreenContext()); + + content::WindowedNotificationObserver( + chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, + content::NotificationService::AllSources()).Wait(); + + // Use capitalized and dotted user name on purpose to make sure + // our email normalization kicks in. + GetLoginDisplay()->ShowSigninScreenForCreds("User.Name", "password"); + + content::WindowedNotificationObserver( + chrome::NOTIFICATION_SESSION_STARTED, + content::NotificationService::AllSources()).Wait(); + + std::set<OAuth2LoginManager::SessionRestoreState> states; + states.insert(OAuth2LoginManager::SESSION_RESTORE_DONE); + states.insert(OAuth2LoginManager::SESSION_RESTORE_FAILED); + states.insert(OAuth2LoginManager::SESSION_RESTORE_CONNECTION_FAILED); + OAuth2LoginManagerStateWaiter merge_session_waiter( + ProfileManager::GetPrimaryUserProfile()); + merge_session_waiter.WaitForStates(states); + EXPECT_EQ(merge_session_waiter.final_state(), + OAuth2LoginManager::SESSION_RESTORE_DONE); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/oauth2_login_manager_factory.h b/chrome/browser/chromeos/login/oauth2_login_manager_factory.h index 8de820e7..0dde695 100644 --- a/chrome/browser/chromeos/login/oauth2_login_manager_factory.h +++ b/chrome/browser/chromeos/login/oauth2_login_manager_factory.h @@ -8,6 +8,7 @@ #include "base/memory/singleton.h" #include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" +class BrowserContextKeyedService; class Profile; namespace chromeos { diff --git a/chrome/browser/chromeos/login/oauth2_login_verifier.cc b/chrome/browser/chromeos/login/oauth2_login_verifier.cc index 4f48206..593921e 100644 --- a/chrome/browser/chromeos/login/oauth2_login_verifier.cc +++ b/chrome/browser/chromeos/login/oauth2_login_verifier.cc @@ -76,7 +76,7 @@ void OAuth2LoginVerifier::VerifyProfileTokens(Profile* profile) { (detector && detector->GetCaptivePortalState(default_network).status != NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE)) { // If network is offline, defer the token fetching until online. - VLOG(1) << "Network is offline. Deferring OAuth2 access token fetch."; + LOG(WARNING) << "Network is offline. Deferring OAuth2 access token fetch."; BrowserThread::PostDelayedTask( BrowserThread::UI, FROM_HERE, diff --git a/chrome/browser/chromeos/login/oobe_base_test.cc b/chrome/browser/chromeos/login/oobe_base_test.cc new file mode 100644 index 0000000..090be6d --- /dev/null +++ b/chrome/browser/chromeos/login/oobe_base_test.cc @@ -0,0 +1,171 @@ +// Copyright (c) 2013 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/chromeos/login/oobe_base_test.h" + +#include "base/command_line.h" +#include "base/message_loop/message_loop.h" +#include "base/path_service.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/chromeos/login/existing_user_controller.h" +#include "chrome/browser/chromeos/login/fake_user_manager.h" +#include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h" +#include "chrome/browser/lifetime/application_lifetime.h" +#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chromeos/chromeos_switches.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_service.h" +#include "content/public/test/browser_test_utils.h" +#include "google_apis/gaia/gaia_switches.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/http_request.h" +#include "net/test/embedded_test_server/http_response.h" + +namespace chromeos { + +namespace { + +// Note the path name must be the same as in shill stub. +const char kStubEthernetServicePath[] = "eth1"; + +} // namespace + +OobeBaseTest::OobeBaseTest() : network_portal_detector_(NULL) { + set_exit_when_last_browser_closes(false); +} + +OobeBaseTest::~OobeBaseTest() { +} + +void OobeBaseTest::SetUp() { + base::FilePath test_data_dir; + PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir); + embedded_test_server()->ServeFilesFromDirectory(test_data_dir); + embedded_test_server()->RegisterRequestHandler( + base::Bind(&FakeGaia::HandleRequest, base::Unretained(&fake_gaia_))); + ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + // Stop IO thread here because no threads are allowed while + // spawning sandbox host process. See crbug.com/322732. + embedded_test_server()->StopThread(); + + InProcessBrowserTest::SetUp(); +} + +void OobeBaseTest::SetUpInProcessBrowserTestFixture() { + host_resolver()->AddRule("*", "127.0.0.1"); + network_portal_detector_ = new NetworkPortalDetectorTestImpl(); + NetworkPortalDetector::InitializeForTesting(network_portal_detector_); + network_portal_detector_->SetDefaultNetworkPathForTesting( + kStubEthernetServicePath); +} + +void OobeBaseTest::SetUpOnMainThread() { + // Restart the thread as the sandbox host process has already been spawned. + embedded_test_server()->RestartThreadAndListen(); +} + +void OobeBaseTest::CleanUpOnMainThread() { + // If the login display is still showing, exit gracefully. + if (LoginDisplayHostImpl::default_host()) { + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&chrome::AttemptExit)); + content::RunMessageLoop(); + } +} + +void OobeBaseTest::SetUpCommandLine(CommandLine* command_line) { + command_line->AppendSwitch(chromeos::switches::kLoginManager); + command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests); + command_line->AppendSwitch(::switches::kDisableBackgroundNetworking); + command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user"); + + // Create gaia and webstore URL from test server url but using different + // host names. This is to avoid gaia response being tagged as from + // webstore in chrome_resource_dispatcher_host_delegate.cc. + const GURL& server_url = embedded_test_server()->base_url(); + + std::string gaia_host("gaia"); + GURL::Replacements replace_gaia_host; + replace_gaia_host.SetHostStr(gaia_host); + GURL gaia_url = server_url.ReplaceComponents(replace_gaia_host); + command_line->AppendSwitchASCII(::switches::kGaiaUrl, gaia_url.spec()); + command_line->AppendSwitchASCII(::switches::kLsoUrl, gaia_url.spec()); + command_line->AppendSwitchASCII(::switches::kGoogleApisUrl, + gaia_url.spec()); + fake_gaia_.Initialize(); +} + +void OobeBaseTest::SimulateNetworkOffline() { + NetworkPortalDetector::CaptivePortalState offline_state; + offline_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE; + network_portal_detector_->SetDetectionResultsForTesting( + kStubEthernetServicePath, offline_state); + network_portal_detector_->NotifyObserversForTesting(); +} + +base::Closure OobeBaseTest::SimulateNetworkOfflineClosure() { + return base::Bind(&OobeBaseTest::SimulateNetworkOffline, + base::Unretained(this)); +} + +void OobeBaseTest::SimulateNetworkOnline() { + NetworkPortalDetector::CaptivePortalState online_state; + online_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE; + online_state.response_code = 204; + network_portal_detector_->SetDetectionResultsForTesting( + kStubEthernetServicePath, online_state); + network_portal_detector_->NotifyObserversForTesting(); +} + +base::Closure OobeBaseTest::SimulateNetworkOnlineClosure() { + return base::Bind(&OobeBaseTest::SimulateNetworkOnline, + base::Unretained(this)); +} + +void OobeBaseTest::SimulateNetworkPortal() { + NetworkPortalDetector::CaptivePortalState portal_state; + portal_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL; + network_portal_detector_->SetDetectionResultsForTesting( + kStubEthernetServicePath, portal_state); + network_portal_detector_->NotifyObserversForTesting(); +} + +base::Closure OobeBaseTest::SimulateNetworkPortalClosure() { + return base::Bind(&OobeBaseTest::SimulateNetworkPortal, + base::Unretained(this)); +} + +void OobeBaseTest::JsExpect(const std::string& expression) { + bool result; + ASSERT_TRUE(content::ExecuteScriptAndExtractBool( + GetLoginUI()->GetWebContents(), + "window.domAutomationController.send(!!(" + expression + "));", + &result)); + ASSERT_TRUE(result) << expression; +} + +content::WebUI* OobeBaseTest::GetLoginUI() { + return static_cast<chromeos::LoginDisplayHostImpl*>( + chromeos::LoginDisplayHostImpl::default_host())->GetOobeUI()->web_ui(); +} + +SigninScreenHandler* OobeBaseTest::GetSigninScreenHandler() { + return static_cast<chromeos::LoginDisplayHostImpl*>( + chromeos::LoginDisplayHostImpl::default_host()) + ->GetOobeUI() + ->signin_screen_handler_for_test(); +} + +WebUILoginDisplay* OobeBaseTest::GetLoginDisplay() { + ExistingUserController* controller = + ExistingUserController::current_controller(); + CHECK(controller); + return static_cast<WebUILoginDisplay*>( + controller->login_display()); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/oobe_base_test.h b/chrome/browser/chromeos/login/oobe_base_test.h new file mode 100644 index 0000000..5901f0a --- /dev/null +++ b/chrome/browser/chromeos/login/oobe_base_test.h @@ -0,0 +1,70 @@ +// Copyright 2013 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. + +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_OOBE_BASE_TEST_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_OOBE_BASE_TEST_H_ + +#include <string> + +#include "base/callback.h" +#include "base/command_line.h" +#include "chrome/browser/chromeos/login/login_display_host_impl.h" +#include "chrome/browser/chromeos/login/webui_login_display.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "content/public/test/test_utils.h" +#include "google_apis/gaia/fake_gaia.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +namespace content { +class WebUI; +} // namespace content + +namespace chromeos { + +class FakeUserManager; +class NetworkPortalDetectorTestImpl; +class SigninScreenHandler; + +// Base class for OOBE and Kiosk tests. +class OobeBaseTest : public InProcessBrowserTest { + public: + OobeBaseTest(); + virtual ~OobeBaseTest(); + + protected: + // InProcessBrowserTest overrides. + virtual void SetUp() OVERRIDE; + virtual void SetUpInProcessBrowserTestFixture() OVERRIDE; + virtual void SetUpOnMainThread() OVERRIDE; + virtual void CleanUpOnMainThread() OVERRIDE; + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE; + + // Network status control functions. + void SimulateNetworkOffline(); + void SimulateNetworkOnline(); + void SimulateNetworkPortal(); + + base::Closure SimulateNetworkOfflineClosure(); + base::Closure SimulateNetworkOnlineClosure(); + base::Closure SimulateNetworkPortalClosure(); + + // Checks JavaScript |expression| in login screen. + void JsExpect(const std::string& expression); + + // Returns chrome://oobe WebUI. + content::WebUI* GetLoginUI(); + + // Returns SigninScreenHandler for login screen. + SigninScreenHandler* GetSigninScreenHandler(); + + // Returns login display. + WebUILoginDisplay* GetLoginDisplay(); + + FakeGaia fake_gaia_; + NetworkPortalDetectorTestImpl* network_portal_detector_; +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_OOBE_BASE_TEST_H_ diff --git a/chrome/browser/chromeos/login/oobe_browsertest.cc b/chrome/browser/chromeos/login/oobe_browsertest.cc index b0e51c4..04483ea 100644 --- a/chrome/browser/chromeos/login/oobe_browsertest.cc +++ b/chrome/browser/chromeos/login/oobe_browsertest.cc @@ -38,6 +38,7 @@ class OobeTest : public InProcessBrowserTest { command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user"); command_line->AppendSwitchASCII( chromeos::switches::kAuthExtensionPath, "gaia_auth"); + fake_gaia_.Initialize(); } virtual void SetUpOnMainThread() OVERRIDE { diff --git a/chrome/browser/chromeos/login/saml_browsertest.cc b/chrome/browser/chromeos/login/saml_browsertest.cc index af8c2a0..6b55bce4 100644 --- a/chrome/browser/chromeos/login/saml_browsertest.cc +++ b/chrome/browser/chromeos/login/saml_browsertest.cc @@ -36,6 +36,13 @@ namespace chromeos { namespace { +const char kTestAuthCode[] = "fake-auth-code"; +const char kTestGaiaUberToken[] = "fake-uber-token"; +const char kTestAuthLoginAccessToken[] = "fake-access-token"; +const char kTestRefreshToken[] = "fake-refresh-token"; +const char kTestSessionSIDCookie[] = "fake-session-SID-cookie"; +const char kTestSessionLSIDCookie[] = "fake-session-LSID-cookie"; + const char kRelayState[] = "RelayState"; const char kDefaultIdpHtml[] = @@ -155,6 +162,7 @@ class SamlTest : public InProcessBrowserTest { command_line->AppendSwitchASCII(::switches::kLsoUrl, gaia_url_.spec()); command_line->AppendSwitchASCII(::switches::kGoogleApisUrl, gaia_url_.spec()); + fake_gaia_.Initialize(); std::string saml_idp_host("saml.idp"); GURL::Replacements replace_saml_idp_host; @@ -167,6 +175,13 @@ class SamlTest : public InProcessBrowserTest { } virtual void SetUpOnMainThread() OVERRIDE { + fake_gaia_.SetAuthTokens(kTestAuthCode, + kTestRefreshToken, + kTestAuthLoginAccessToken, + kTestGaiaUberToken, + kTestSessionSIDCookie, + kTestSessionLSIDCookie); + embedded_test_server()->RegisterRequestHandler( base::Bind(&FakeGaia::HandleRequest, base::Unretained(&fake_gaia_))); embedded_test_server()->RegisterRequestHandler(base::Bind( diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index c6706ad..4c70e74 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1022,6 +1022,9 @@ 'browser/chromeos/login/mock_authenticator.cc', 'browser/chromeos/login/mock_authenticator.h', 'browser/chromeos/login/saml_browsertest.cc', + 'browser/chromeos/login/oauth2_browsertest.cc', + 'browser/chromeos/login/oobe_base_test.cc', + 'browser/chromeos/login/oobe_base_test.h', 'browser/chromeos/login/session_login_browsertest.cc', 'browser/chromeos/login/screen_locker_tester.cc', 'browser/chromeos/login/screen_locker_tester.h', diff --git a/google_apis/gaia/fake_gaia.cc b/google_apis/gaia/fake_gaia.cc index 7474aca8..9eddd85 100644 --- a/google_apis/gaia/fake_gaia.cc +++ b/google_apis/gaia/fake_gaia.cc @@ -7,6 +7,8 @@ #include <vector> #include "base/base_paths.h" +#include "base/bind.h" +#include "base/bind_helpers.h" #include "base/file_util.h" #include "base/files/file_path.h" #include "base/json/json_writer.h" @@ -15,6 +17,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/values.h" #include "google_apis/gaia/gaia_urls.h" #include "net/base/url_util.h" @@ -23,11 +26,25 @@ #include "net/test/embedded_test_server/http_response.h" #include "url/url_parse.h" +#define REGISTER_RESPONSE_HANDLER(url, method) \ + request_handlers_.insert(std::make_pair( \ + url.path(), base::Bind(&FakeGaia::method, base::Unretained(this)))) + +#define REGISTER_PATH_RESPONSE_HANDLER(path, method) \ + request_handlers_.insert(std::make_pair( \ + path, base::Bind(&FakeGaia::method, base::Unretained(this)))) + using namespace net::test_server; namespace { + const base::FilePath::CharType kServiceLogin[] = FILE_PATH_LITERAL("google_apis/test/service_login.html"); + +// OAuth2 Authentication header value prefix. +const char kAuthHeaderBearer[] = "Bearer "; +const char kAuthHeaderOAuth[] = "OAuth "; + } FakeGaia::AccessTokenInfo::AccessTokenInfo() @@ -45,113 +62,310 @@ FakeGaia::FakeGaia() { FakeGaia::~FakeGaia() {} -scoped_ptr<HttpResponse> FakeGaia::HandleRequest(const HttpRequest& request) { +void FakeGaia::SetAuthTokens(const std::string& auth_code, + const std::string& refresh_token, + const std::string& access_token, + const std::string& gaia_uber_token, + const std::string& session_sid_cookie, + const std::string& session_lsid_cookie) { + fake_auth_code_ = auth_code; + fake_refresh_token_ = refresh_token; + fake_access_token_ = access_token; + fake_gaia_uber_token_ = gaia_uber_token; + fake_session_sid_cookie_ = session_sid_cookie; + fake_session_lsid_cookie_ = session_lsid_cookie; +} + +void FakeGaia::Initialize() { GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); + // Handles /ServiceLogin GAIA call. + REGISTER_RESPONSE_HANDLER( + gaia_urls->service_login_url(), HandleServiceLogin); + + // Handles /ServiceLoginAuth GAIA call. + REGISTER_RESPONSE_HANDLER( + gaia_urls->service_login_auth_url(), HandleServiceLoginAuth); + + // Handles /o/oauth2/programmatic_auth GAIA call. + REGISTER_RESPONSE_HANDLER( + gaia_urls->client_login_to_oauth2_url(), HandleProgramaticAuth); + + // Handles /o/oauth2/token GAIA call. + REGISTER_RESPONSE_HANDLER( + gaia_urls->oauth2_token_url(), HandleAuthToken); + + // Handles /OAuthLogin GAIA call. + REGISTER_RESPONSE_HANDLER( + gaia_urls->oauth1_login_url(), HandleOAuthLogin); + + // Handles /MergeSession GAIA call. + REGISTER_RESPONSE_HANDLER( + gaia_urls->merge_session_url(), HandleMergeSession); + + // Handles /oauth2/v2/IssueToken GAIA call. + REGISTER_RESPONSE_HANDLER( + gaia_urls->oauth2_issue_token_url(), HandleIssueToken); + + // Handles /oauth2/v2/tokeninfo GAIA call. + REGISTER_RESPONSE_HANDLER( + gaia_urls->oauth2_token_info_url(), HandleTokenInfo); + + // Handles /SSO GAIA call (not GAIA, made up for SAML tests). + REGISTER_PATH_RESPONSE_HANDLER("/SSO", HandleSSO); +} + +void FakeGaia::HandleProgramaticAuth( + const HttpRequest& request, + BasicHttpResponse* http_response) { + GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); + std::string scope; + if (!GetQueryParameter(request.content, "scope", &scope) || + gaia_urls->oauth1_login_scope() != scope) { + http_response->set_code(net::HTTP_BAD_REQUEST); + return; + } + + std::string client_id; + if (!GetQueryParameter(request.content, "client_id", &client_id) || + gaia_urls->oauth2_chrome_client_id() != client_id) { + http_response->set_code(net::HTTP_BAD_REQUEST); + return; + } + + http_response->AddCustomHeader( + "Set-Cookie", + base::StringPrintf( + "oauth_code=%s; Path=/o/GetOAuth2Token; Secure; HttpOnly;", + fake_auth_code_.c_str())); + http_response->set_code(net::HTTP_OK); + http_response->set_content_type("text/html"); +} + +void FakeGaia::HandleServiceLogin(const HttpRequest& request, + BasicHttpResponse* http_response) { + http_response->set_code(net::HTTP_OK); + http_response->set_content(service_login_response_); + http_response->set_content_type("text/html"); +} + +void FakeGaia::HandleOAuthLogin(const HttpRequest& request, + BasicHttpResponse* http_response) { + http_response->set_code(net::HTTP_BAD_REQUEST); + std::string access_token; + if (!GetAccessToken(request, kAuthHeaderOAuth, &access_token)) { + LOG(ERROR) << "/OAuthLogin missing access token in the header"; + return; + } - // The scheme and host of the URL is actually not important but required to - // get a valid GURL in order to parse |request.relative_url|. GURL request_url = GURL("http://localhost").Resolve(request.relative_url); - std::string request_path = request_url.path(); + std::string request_query = request_url.query(); - scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse()); - if (request_path == gaia_urls->service_login_url().path()) { + std::string source; + if (!GetQueryParameter(request_query, "source", &source)) { + LOG(ERROR) << "Missing 'source' param in /OAuthLogin call"; + return; + } + + std::string issue_uberauth; + if (GetQueryParameter(request_query, "issueuberauth", &issue_uberauth) && + issue_uberauth == "1") { + http_response->set_content(fake_gaia_uber_token_); http_response->set_code(net::HTTP_OK); - http_response->set_content(service_login_response_); - http_response->set_content_type("text/html"); - } else if (request_path == gaia_urls->service_login_auth_url().path()) { - std::string continue_url = gaia_urls->service_login_url().spec(); - GetQueryParameter(request.content, "continue", &continue_url); - std::string redirect_url = continue_url; - - std::string email; - if (GetQueryParameter(request.content, "Email", &email) && - saml_account_idp_map_.find(email) != saml_account_idp_map_.end()) { - GURL url(saml_account_idp_map_[email]); - url = net::AppendQueryParameter(url, "SAMLRequest", "fake_request"); - url = net::AppendQueryParameter(url, "RelayState", continue_url); - redirect_url = url.spec(); - } + // Issue GAIA uber token. + } else { + LOG(FATAL) << "/OAuthLogin for SID/LSID is not supported"; + } +} + +void FakeGaia::HandleMergeSession(const HttpRequest& request, + BasicHttpResponse* http_response) { + http_response->set_code(net::HTTP_BAD_REQUEST); + + std::string uber_token; + if (!GetQueryParameter(request.content, "uberauth", &uber_token) || + uber_token != fake_gaia_uber_token_) { + LOG(ERROR) << "Missing or invalid 'uberauth' param in /MergeSession call"; + return; + } + + std::string continue_url; + if (!GetQueryParameter(request.content, "continue", &continue_url)) { + LOG(ERROR) << "Missing or invalid 'continue' param in /MergeSession call"; + return; + } + + std::string source; + if (!GetQueryParameter(request.content, "source", &source)) { + LOG(ERROR) << "Missing or invalid 'source' param in /MergeSession call"; + return; + } + + http_response->AddCustomHeader( + "Set-Cookie", + base::StringPrintf( + "SID=%s; LSID=%s; Path=/; Secure; HttpOnly;", + fake_session_sid_cookie_.c_str(), + fake_session_lsid_cookie_.c_str())); + // TODO(zelidrag): Not used now. + http_response->set_content("OK"); + http_response->set_code(net::HTTP_OK); +} + + +void FakeGaia::HandleServiceLoginAuth(const HttpRequest& request, + BasicHttpResponse* http_response) { + std::string continue_url = + GaiaUrls::GetInstance()->service_login_url().spec(); + GetQueryParameter(request.content, "continue", &continue_url); + + std::string redirect_url = continue_url; + + std::string email; + if (GetQueryParameter(request.content, "Email", &email) && + saml_account_idp_map_.find(email) != saml_account_idp_map_.end()) { + GURL url(saml_account_idp_map_[email]); + url = net::AppendQueryParameter(url, "SAMLRequest", "fake_request"); + url = net::AppendQueryParameter(url, "RelayState", continue_url); + redirect_url = url.spec(); + } + + http_response->set_code(net::HTTP_TEMPORARY_REDIRECT); + http_response->AddCustomHeader("Location", redirect_url); +} + +void FakeGaia::HandleSSO(const HttpRequest& request, + BasicHttpResponse* http_response) { + std::string relay_state; + GetQueryParameter(request.content, "RelayState", &relay_state); + std::string redirect_url = relay_state; + http_response->set_code(net::HTTP_TEMPORARY_REDIRECT); + http_response->AddCustomHeader("Location", redirect_url); +} + +void FakeGaia::HandleAuthToken(const HttpRequest& request, + BasicHttpResponse* http_response) { + std::string grant_type; + std::string refresh_token; + std::string client_id; + std::string scope; + std::string auth_code; + const AccessTokenInfo* token_info = NULL; + GetQueryParameter(request.content, "scope", &scope); - http_response->set_code(net::HTTP_TEMPORARY_REDIRECT); - http_response->AddCustomHeader("Location", redirect_url); - } else if (request_path == gaia_urls->oauth2_token_url().path()) { - std::string refresh_token; - std::string client_id; - std::string scope; - const AccessTokenInfo* token_info = NULL; - GetQueryParameter(request.content, "scope", &scope); - if (GetQueryParameter(request.content, "refresh_token", &refresh_token) && - GetQueryParameter(request.content, "client_id", &client_id) && - (token_info = GetAccessTokenInfo(refresh_token, client_id, scope))) { - base::DictionaryValue response_dict; - response_dict.SetString("access_token", token_info->token); - response_dict.SetInteger("expires_in", 3600); - FormatJSONResponse(response_dict, http_response.get()); - } else { + if (!GetQueryParameter(request.content, "grant_type", &grant_type)) { + http_response->set_code(net::HTTP_BAD_REQUEST); + LOG(ERROR) << "No 'grant_type' param in /o/oauth2/token"; + return; + } + + if (grant_type == "authorization_code") { + if (!GetQueryParameter(request.content, "code", &auth_code) || + auth_code != fake_auth_code_) { http_response->set_code(net::HTTP_BAD_REQUEST); - } - } else if (request_path == gaia_urls->oauth2_token_info_url().path()) { - const AccessTokenInfo* token_info = NULL; - std::string access_token; - if (GetQueryParameter(request.content, "access_token", &access_token)) { - for (AccessTokenInfoMap::const_iterator entry( - access_token_info_map_.begin()); - entry != access_token_info_map_.end(); - ++entry) { - if (entry->second.token == access_token) { - token_info = &(entry->second); - break; - } - } + LOG(ERROR) << "No 'code' param in /o/oauth2/token"; + return; } - if (token_info) { - base::DictionaryValue response_dict; - response_dict.SetString("issued_to", token_info->issued_to); - response_dict.SetString("audience", token_info->audience); - response_dict.SetString("user_id", token_info->user_id); - std::vector<std::string> scope_vector(token_info->scopes.begin(), - token_info->scopes.end()); - response_dict.SetString("scope", JoinString(scope_vector, " ")); - response_dict.SetInteger("expires_in", token_info->expires_in); - response_dict.SetString("email", token_info->email); - FormatJSONResponse(response_dict, http_response.get()); - } else { + if (GaiaUrls::GetInstance()->oauth1_login_scope() != scope) { http_response->set_code(net::HTTP_BAD_REQUEST); - } - } else if (request_path == gaia_urls->oauth2_issue_token_url().path()) { - std::string access_token; - std::map<std::string, std::string>::const_iterator auth_header_entry = - request.headers.find("Authorization"); - if (auth_header_entry != request.headers.end()) { - if (StartsWithASCII(auth_header_entry->second, "Bearer ", true)) - access_token = auth_header_entry->second.substr(7); + LOG(ERROR) << "Invalid scope for /o/oauth2/token - " << scope; + return; } - std::string scope; - std::string client_id; - const AccessTokenInfo* token_info = NULL; - if (GetQueryParameter(request.content, "scope", &scope) && - GetQueryParameter(request.content, "client_id", &client_id) && - (token_info = GetAccessTokenInfo(access_token, client_id, scope))) { - base::DictionaryValue response_dict; - response_dict.SetString("issueAdvice", "auto"); - response_dict.SetString("expiresIn", - base::IntToString(token_info->expires_in)); - response_dict.SetString("token", token_info->token); - FormatJSONResponse(response_dict, http_response.get()); - } else { - http_response->set_code(net::HTTP_BAD_REQUEST); + base::DictionaryValue response_dict; + response_dict.SetString("refresh_token", fake_refresh_token_); + response_dict.SetString("access_token", fake_access_token_); + response_dict.SetInteger("expires_in", 3600); + FormatJSONResponse(response_dict, http_response); + } else if (GetQueryParameter(request.content, + "refresh_token", + &refresh_token) && + GetQueryParameter(request.content, + "client_id", + &client_id) && + (token_info = FindAccessTokenInfo(refresh_token, + client_id, + scope))) { + base::DictionaryValue response_dict; + response_dict.SetString("access_token", token_info->token); + response_dict.SetInteger("expires_in", 3600); + FormatJSONResponse(response_dict, http_response); + } else { + LOG(ERROR) << "Bad request for /o/oauth2/token - " + << "refresh_token = " << refresh_token + << ", scope = " << scope + << ", client_id = " << client_id; + http_response->set_code(net::HTTP_BAD_REQUEST); + } +} + +void FakeGaia::HandleTokenInfo(const HttpRequest& request, + BasicHttpResponse* http_response) { + const AccessTokenInfo* token_info = NULL; + std::string access_token; + if (GetQueryParameter(request.content, "access_token", &access_token)) { + for (AccessTokenInfoMap::const_iterator entry( + access_token_info_map_.begin()); + entry != access_token_info_map_.end(); + ++entry) { + if (entry->second.token == access_token) { + token_info = &(entry->second); + break; + } } - } else if (request_path == "/SSO") { - std::string relay_state; - GetQueryParameter(request.content, "RelayState", &relay_state); - std::string redirect_url = relay_state; - http_response->set_code(net::HTTP_TEMPORARY_REDIRECT); - http_response->AddCustomHeader("Location", redirect_url); + } + + if (token_info) { + base::DictionaryValue response_dict; + response_dict.SetString("issued_to", token_info->issued_to); + response_dict.SetString("audience", token_info->audience); + response_dict.SetString("user_id", token_info->user_id); + std::vector<std::string> scope_vector(token_info->scopes.begin(), + token_info->scopes.end()); + response_dict.SetString("scope", JoinString(scope_vector, " ")); + response_dict.SetInteger("expires_in", token_info->expires_in); + response_dict.SetString("email", token_info->email); + FormatJSONResponse(response_dict, http_response); + } else { + http_response->set_code(net::HTTP_BAD_REQUEST); + } +} + +void FakeGaia::HandleIssueToken(const HttpRequest& request, + BasicHttpResponse* http_response) { + std::string access_token; + std::string scope; + std::string client_id; + const AccessTokenInfo* token_info = NULL; + if (GetAccessToken(request, kAuthHeaderBearer, &access_token) && + GetQueryParameter(request.content, "scope", &scope) && + GetQueryParameter(request.content, "client_id", &client_id) && + (token_info = FindAccessTokenInfo(access_token, client_id, scope))) { + base::DictionaryValue response_dict; + response_dict.SetString("issueAdvice", "auto"); + response_dict.SetString("expiresIn", + base::IntToString(token_info->expires_in)); + response_dict.SetString("token", token_info->token); + FormatJSONResponse(response_dict, http_response); + } else { + http_response->set_code(net::HTTP_BAD_REQUEST); + } +} + + +scoped_ptr<HttpResponse> FakeGaia::HandleRequest(const HttpRequest& request) { + // The scheme and host of the URL is actually not important but required to + // get a valid GURL in order to parse |request.relative_url|. + GURL request_url = GURL("http://localhost").Resolve(request.relative_url); + std::string request_path = request_url.path(); + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse()); + RequestHandlerMap::iterator iter = request_handlers_.find(request_path); + if (iter != request_handlers_.end()) { + LOG(WARNING) << "Serving request " << request_path; + iter->second.Run(request, http_response.get()); } else { - // Request not understood. - return scoped_ptr<HttpResponse>(); + LOG(ERROR) << "Unhandled request " << request_path; + return scoped_ptr<HttpResponse>(); // Request not understood. } return http_response.PassAs<HttpResponse>(); @@ -167,16 +381,6 @@ void FakeGaia::RegisterSamlUser(const std::string& account_id, saml_account_idp_map_[account_id] = saml_idp; } -// static -bool FakeGaia::GetQueryParameter(const std::string& query, - const std::string& key, - std::string* value) { - // Name and scheme actually don't matter, but are required to get a valid URL - // for parsing. - GURL query_url("http://localhost?" + query); - return net::GetValueForKeyInQuery(query_url, key, value); -} - void FakeGaia::FormatJSONResponse(const base::DictionaryValue& response_dict, BasicHttpResponse* http_response) { std::string response_json; @@ -185,7 +389,7 @@ void FakeGaia::FormatJSONResponse(const base::DictionaryValue& response_dict, http_response->set_code(net::HTTP_OK); } -const FakeGaia::AccessTokenInfo* FakeGaia::GetAccessTokenInfo( +const FakeGaia::AccessTokenInfo* FakeGaia::FindAccessTokenInfo( const std::string& auth_token, const std::string& client_id, const std::string& scope_string) const { @@ -208,3 +412,30 @@ const FakeGaia::AccessTokenInfo* FakeGaia::GetAccessTokenInfo( return NULL; } + +// static +bool FakeGaia::GetQueryParameter(const std::string& query, + const std::string& key, + std::string* value) { + // Name and scheme actually don't matter, but are required to get a valid URL + // for parsing. + GURL query_url("http://localhost?" + query); + return net::GetValueForKeyInQuery(query_url, key, value); +} + +// static +bool FakeGaia::GetAccessToken(const HttpRequest& request, + const char* auth_token_prefix, + std::string* access_token) { + std::map<std::string, std::string>::const_iterator auth_header_entry = + request.headers.find("Authorization"); + if (auth_header_entry != request.headers.end()) { + if (StartsWithASCII(auth_header_entry->second, auth_token_prefix, true)) { + *access_token = auth_header_entry->second.substr( + strlen(auth_token_prefix)); + return true; + } + } + + return false; +} diff --git a/google_apis/gaia/fake_gaia.h b/google_apis/gaia/fake_gaia.h index 7e211ae..6052c2b 100644 --- a/google_apis/gaia/fake_gaia.h +++ b/google_apis/gaia/fake_gaia.h @@ -10,6 +10,7 @@ #include <string> #include "base/basictypes.h" +#include "base/callback.h" #include "base/memory/scoped_ptr.h" #include "url/gurl.h" @@ -49,6 +50,18 @@ class FakeGaia { FakeGaia(); ~FakeGaia(); + // Sets the initial value of tokens and cookies. + void SetAuthTokens(const std::string& auth_code, + const std::string& refresh_token, + const std::string& access_token, + const std::string& gaia_uber_token, + const std::string& session_sid_cookie, + const std::string& session_lsid_cookie); + + // Initializes HTTP request handlers. Should be called after switches + // for tweaking GaiaUrls are in place. + void Initialize(); + // Handles a request and returns a response if the request was recognized as a // GAIA request. Note that this respects the switches::kGaiaUrl and friends so // that this can used with EmbeddedTestServer::RegisterRequestHandler(). @@ -81,16 +94,61 @@ class FakeGaia { void FormatJSONResponse(const base::DictionaryValue& response_dict, net::test_server::BasicHttpResponse* http_response); + typedef base::Callback<void( + const net::test_server::HttpRequest& request, + net::test_server::BasicHttpResponse* http_response)> + HttpRequestHandlerCallback; + typedef std::map<std::string, HttpRequestHandlerCallback> RequestHandlerMap; + + // HTTP request handlers. + void HandleProgramaticAuth( + const net::test_server::HttpRequest& request, + net::test_server::BasicHttpResponse* http_response); + void HandleServiceLogin(const net::test_server::HttpRequest& request, + net::test_server::BasicHttpResponse* http_response); + void HandleOAuthLogin(const net::test_server::HttpRequest& request, + net::test_server::BasicHttpResponse* http_response); + void HandleSSO(const net::test_server::HttpRequest& request, + net::test_server::BasicHttpResponse* http_response); + void HandleMergeSession(const net::test_server::HttpRequest& request, + net::test_server::BasicHttpResponse* http_response); + void HandleServiceLoginAuth( + const net::test_server::HttpRequest& request, + net::test_server::BasicHttpResponse* http_response); + void HandleAuthToken(const net::test_server::HttpRequest& request, + net::test_server::BasicHttpResponse* http_response); + void HandleTokenInfo(const net::test_server::HttpRequest& request, + net::test_server::BasicHttpResponse* http_response); + void HandleIssueToken(const net::test_server::HttpRequest& request, + net::test_server::BasicHttpResponse* http_response); + // Returns the access token associated with |auth_token| that matches the // given |client_id| and |scope_string|. If |scope_string| is empty, the first // token satisfying the other criteria is returned. Returns NULL if no token // matches. - const AccessTokenInfo* GetAccessTokenInfo(const std::string& auth_token, - const std::string& client_id, - const std::string& scope_string) + const AccessTokenInfo* FindAccessTokenInfo(const std::string& auth_token, + const std::string& client_id, + const std::string& scope_string) const; + // Extracts the |access_token| from authorization header of |request|. + static bool GetAccessToken(const net::test_server::HttpRequest& request, + const char* auth_token_prefix, + std::string* access_token); + + // auth_code cookie value response for /o/oauth2/programmatic_auth call. + std::string fake_auth_code_; + + // refresh_token field value response for the initial /o/oauth2/token call + // with ...&grant_type=authorization_code. + std::string fake_refresh_token_; + std::string fake_access_token_; + std::string fake_gaia_uber_token_; + std::string fake_session_sid_cookie_; + std::string fake_session_lsid_cookie_; + AccessTokenInfoMap access_token_info_map_; + RequestHandlerMap request_handlers_; std::string service_login_response_; SamlAccountIdpMap saml_account_idp_map_; |