// Copyright 2020 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/strings/escape.h" #include "base/strings/strcat.h" #include "components/signin/core/browser/signin_header_helper.h" #include "google_apis/gaia/gaia_switches.h" #include "google_apis/gaia/gaia_urls.h" #include "net/base/url_util.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/default_handlers.h" #include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_response.h" #include "weblayer/browser/browser_impl.h" #include "weblayer/browser/profile_impl.h" #include "weblayer/browser/signin_url_loader_throttle.h" #include "weblayer/browser/tab_impl.h" #include "weblayer/public/google_accounts_delegate.h" #include "weblayer/public/navigation_controller.h" #include "weblayer/shell/browser/shell.h" #include "weblayer/test/test_navigation_observer.h" #include "weblayer/test/weblayer_browser_test.h" #include "weblayer/test/weblayer_browser_test_utils.h" namespace weblayer { namespace { constexpr char kGaiaDomain[] = "fakegaia.com"; constexpr char kGoogleAccountsPath[] = "/manageaccounts"; constexpr char kGoogleAccountsRedirectPath[] = "/manageaccounts-redirect"; } // namespace class GoogleAccountsBrowserTest : public WebLayerBrowserTest, public GoogleAccountsDelegate { public: // WebLayerBrowserTest: void SetUpOnMainThread() override { host_resolver()->AddRule("*", "127.0.0.1"); shell()->tab()->SetGoogleAccountsDelegate(this); } void SetUpCommandLine(base::CommandLine* command_line) override { https_server_.RegisterRequestHandler(base::BindRepeating( &GoogleAccountsBrowserTest::HandleGoogleAccountsRequest, base::Unretained(this))); net::test_server::RegisterDefaultHandlers(&https_server_); ASSERT_TRUE(https_server_.Start()); command_line->AppendSwitchASCII(switches::kGaiaUrl, GetGaiaURL("/").spec()); command_line->AppendSwitch("ignore-certificate-errors"); } // GoogleAccountsDelegate: void OnGoogleAccountsRequest( const signin::ManageAccountsParams& params) override { params_ = params; if (run_loop_) run_loop_->Quit(); } std::string GetGaiaId() override { return ""; } const signin::ManageAccountsParams& WaitForGoogleAccounts() { if (!params_) { run_loop_ = std::make_unique(); run_loop_->Run(); } EXPECT_TRUE(params_.has_value()); return params_.value(); } GURL GetGaiaURL(const std::string& path) { return https_server_.GetURL(kGaiaDomain, path); } GURL GetNonGaiaURL(const std::string& path) { return https_server_.GetURL(path); } bool HasReceivedGoogleAccountsResponse() { return params_.has_value(); } std::string GetBody() { return ExecuteScript(shell(), "document.body.innerText", true).GetString(); } protected: std::string response_header_; private: std::unique_ptr HandleGoogleAccountsRequest( const net::test_server::HttpRequest& request) { std::string path = request.GetURL().path(); if (path != kSignOutPath && path != kGoogleAccountsPath && path != kGoogleAccountsRedirectPath) { return nullptr; } auto http_response = std::make_unique(); if (path == kGoogleAccountsRedirectPath) { http_response->set_code(net::HTTP_MOVED_PERMANENTLY); http_response->AddCustomHeader( "Location", base::UnescapeBinaryURLComponent(request.GetURL().query_piece())); } else { http_response->set_code(net::HTTP_OK); } if (base::Contains(request.headers, signin::kChromeConnectedHeader)) { http_response->AddCustomHeader(signin::kChromeManageAccountsHeader, response_header_); } http_response->set_content(""); return http_response; } net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS}; absl::optional params_; std::unique_ptr run_loop_; }; IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, Basic) { response_header_ = "action=ADDSESSION,email=foo@bar.com," "continue_url=https://blah.com,is_same_tab=true"; NavigateAndWaitForCompletion(GetGaiaURL(kGoogleAccountsPath), shell()); const signin::ManageAccountsParams& params = WaitForGoogleAccounts(); EXPECT_EQ(params.service_type, signin::GAIA_SERVICE_TYPE_ADDSESSION); EXPECT_EQ(params.email, "foo@bar.com"); EXPECT_EQ(params.continue_url, "https://blah.com"); EXPECT_TRUE(params.is_same_tab); } IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, NonGaiaUrl) { response_header_ = "action=ADDSESSION"; NavigateAndWaitForCompletion(GetNonGaiaURL(kGoogleAccountsPath), shell()); // Navigate again to make sure the manage accounts response would have been // received. NavigateAndWaitForCompletion(GetNonGaiaURL("/echo"), shell()); EXPECT_FALSE(HasReceivedGoogleAccountsResponse()); } IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, RedirectFromGaiaURL) { response_header_ = "action=SIGNUP"; GURL final_url = GetGaiaURL(kGoogleAccountsPath); TestNavigationObserver test_observer( final_url, TestNavigationObserver::NavigationEvent::kCompletion, shell()->tab()); shell()->tab()->GetNavigationController()->Navigate(GetNonGaiaURL( base::StrCat({kGoogleAccountsRedirectPath, "?", final_url.spec()}))); test_observer.Wait(); const signin::ManageAccountsParams& params = WaitForGoogleAccounts(); EXPECT_EQ(params.service_type, signin::GAIA_SERVICE_TYPE_SIGNUP); } IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, RedirectToGaiaURL) { response_header_ = "action=SIGNUP"; GURL final_url = GetNonGaiaURL(kGoogleAccountsPath); TestNavigationObserver test_observer( final_url, TestNavigationObserver::NavigationEvent::kCompletion, shell()->tab()); shell()->tab()->GetNavigationController()->Navigate(GetGaiaURL( base::StrCat({kGoogleAccountsRedirectPath, "?", final_url.spec()}))); test_observer.Wait(); const signin::ManageAccountsParams& params = WaitForGoogleAccounts(); EXPECT_EQ(params.service_type, signin::GAIA_SERVICE_TYPE_SIGNUP); } IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, AddsQueryParamsToSignoutURL) { response_header_ = "action=SIGNUP"; // Sign out URL on the GAIA domain will get a query param added to it. GURL sign_out_url = GetGaiaURL(kSignOutPath); GURL sign_out_url_with_params = net::AppendQueryParameter(sign_out_url, "manage", "true"); { TestNavigationObserver test_observer( sign_out_url_with_params, TestNavigationObserver::NavigationEvent::kCompletion, shell()->tab()); shell()->tab()->GetNavigationController()->Navigate(sign_out_url); test_observer.Wait(); } // Other URLs will not have query param added. NavigateAndWaitForCompletion(GetGaiaURL(kGoogleAccountsPath), shell()); NavigateAndWaitForCompletion(GetNonGaiaURL(kSignOutPath), shell()); // Redirecting to sign out URL will also add params. { TestNavigationObserver test_observer( sign_out_url_with_params, TestNavigationObserver::NavigationEvent::kCompletion, shell()->tab()); shell()->tab()->GetNavigationController()->Navigate( GetNonGaiaURL("/server-redirect?" + sign_out_url.spec())); test_observer.Wait(); } } IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, AddsRequestHeaderToGaiaURLs) { const std::string path = base::StrCat({"/echoheader?", signin::kChromeConnectedHeader}); NavigateAndWaitForCompletion(GetGaiaURL(path), shell()); EXPECT_EQ(GetBody(), "source=WebLayer,mode=3,enable_account_consistency=true," "consistency_enabled_by_default=false"); // Non GAIA URL should not get the header. NavigateAndWaitForCompletion(GetNonGaiaURL(path), shell()); EXPECT_EQ(GetBody(), "None"); } class IncognitoGoogleAccountsBrowserTest : public GoogleAccountsBrowserTest { public: IncognitoGoogleAccountsBrowserTest() { SetShellStartsInIncognitoMode(); } }; IN_PROC_BROWSER_TEST_F(IncognitoGoogleAccountsBrowserTest, HeaderAddedForIncognitoBrowser) { const std::string path = base::StrCat({"/echoheader?", signin::kChromeConnectedHeader}); NavigateAndWaitForCompletion(GetGaiaURL(path), shell()); EXPECT_EQ(GetBody(), "source=WebLayer,mode=3,enable_account_consistency=true," "consistency_enabled_by_default=false"); } } // namespace weblayer