1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <string> 6 #include <vector> 7 8 #include "base/bind.h" 9 #include "base/command_line.h" 10 #include "base/run_loop.h" 11 #include "base/stl_util.h" 12 #include "chrome/browser/browser_process.h" 13 #include "chrome/browser/chrome_notification_types.h" 14 #include "chrome/browser/chromeos/login/existing_user_controller.h" 15 #include "chrome/browser/chromeos/login/ui/webui_login_display.h" 16 #include "chrome/browser/chromeos/login/wizard_controller.h" 17 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" 18 #include "chrome/browser/chromeos/policy/enterprise_install_attributes.h" 19 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h" 20 #include "chrome/common/chrome_switches.h" 21 #include "chrome/test/base/in_process_browser_test.h" 22 #include "chromeos/chromeos_switches.h" 23 #include "components/policy/core/common/cloud/device_management_service.h" 24 #include "components/policy/core/common/policy_switches.h" 25 #include "components/user_manager/user_manager.h" 26 #include "content/public/browser/notification_observer.h" 27 #include "content/public/browser/notification_registrar.h" 28 #include "content/public/browser/notification_service.h" 29 #include "content/public/test/test_utils.h" 30 #include "google_apis/gaia/fake_gaia.h" 31 #include "google_apis/gaia/gaia_switches.h" 32 #include "google_apis/gaia/gaia_urls.h" 33 #include "net/http/http_status_code.h" 34 #include "net/test/embedded_test_server/embedded_test_server.h" 35 #include "net/test/embedded_test_server/http_request.h" 36 #include "net/test/embedded_test_server/http_response.h" 37 #include "policy/proto/device_management_backend.pb.h" 38 #include "testing/gtest/include/gtest/gtest.h" 39 40 namespace chromeos { 41 42 namespace { 43 44 namespace em = enterprise_management; 45 46 const char kDomain[] = "domain.com"; 47 const char kUsername[] = "user (at) domain.com"; 48 const char kUsernameOtherDomain[] = "user (at) other.com"; 49 50 const char kOAuthCodeCookie[] = "oauth_code=1234; Secure; HttpOnly"; 51 52 const char kOAuth2TokenPairData[] = 53 "{" 54 " \"refresh_token\": \"1234\"," 55 " \"access_token\": \"5678\"," 56 " \"expires_in\": 3600" 57 "}"; 58 59 const char kOAuth2AccessTokenData[] = 60 "{" 61 " \"access_token\": \"5678\"," 62 " \"expires_in\": 3600" 63 "}"; 64 65 const char kDMRegisterRequest[] = "/device_management?request=register"; 66 const char kDMPolicyRequest[] = "/device_management?request=policy"; 67 68 void CopyLockResult(base::RunLoop* loop, 69 policy::EnterpriseInstallAttributes::LockResult* out, 70 policy::EnterpriseInstallAttributes::LockResult result) { 71 *out = result; 72 loop->Quit(); 73 } 74 75 } // namespace 76 77 struct BlockingLoginTestParam { 78 const int steps; 79 const char* username; 80 const bool enroll_device; 81 }; 82 83 class BlockingLoginTest 84 : public InProcessBrowserTest, 85 public content::NotificationObserver, 86 public testing::WithParamInterface<BlockingLoginTestParam> { 87 public: 88 BlockingLoginTest() : profile_added_(NULL) {} 89 90 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 91 // Initialize the test server early, so that we can use its base url for 92 // the command line flags. 93 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 94 95 // Use the login manager screens and the gaia auth extension. 96 command_line->AppendSwitch(switches::kLoginManager); 97 command_line->AppendSwitch(switches::kForceLoginManagerInTests); 98 command_line->AppendSwitchASCII(switches::kLoginProfile, "user"); 99 command_line->AppendSwitchASCII(::switches::kAuthExtensionPath, 100 "gaia_auth"); 101 102 // Redirect requests to gaia and the policy server to the test server. 103 command_line->AppendSwitchASCII(::switches::kGaiaUrl, 104 embedded_test_server()->base_url().spec()); 105 command_line->AppendSwitchASCII(::switches::kLsoUrl, 106 embedded_test_server()->base_url().spec()); 107 command_line->AppendSwitchASCII( 108 policy::switches::kDeviceManagementUrl, 109 embedded_test_server()->GetURL("/device_management").spec()); 110 } 111 112 virtual void SetUpOnMainThread() OVERRIDE { 113 fake_gaia_.Initialize(); 114 115 embedded_test_server()->RegisterRequestHandler( 116 base::Bind(&BlockingLoginTest::HandleRequest, base::Unretained(this))); 117 embedded_test_server()->RegisterRequestHandler( 118 base::Bind(&FakeGaia::HandleRequest, base::Unretained(&fake_gaia_))); 119 120 registrar_.Add(this, 121 chrome::NOTIFICATION_PROFILE_ADDED, 122 content::NotificationService::AllSources()); 123 } 124 125 virtual void TearDownOnMainThread() OVERRIDE { 126 RunUntilIdle(); 127 EXPECT_TRUE(responses_.empty()); 128 STLDeleteElements(&responses_); 129 EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); 130 } 131 132 virtual void Observe(int type, 133 const content::NotificationSource& source, 134 const content::NotificationDetails& details) OVERRIDE { 135 ASSERT_EQ(chrome::NOTIFICATION_PROFILE_ADDED, type); 136 ASSERT_FALSE(profile_added_); 137 profile_added_ = content::Source<Profile>(source).ptr(); 138 } 139 140 void RunUntilIdle() { 141 base::RunLoop().RunUntilIdle(); 142 } 143 144 policy::BrowserPolicyConnectorChromeOS* browser_policy_connector() { 145 return g_browser_process->platform_part() 146 ->browser_policy_connector_chromeos(); 147 } 148 149 void SkipToSigninScreen() { 150 WizardController::SkipPostLoginScreensForTesting(); 151 WizardController* wizard_controller = 152 WizardController::default_controller(); 153 ASSERT_TRUE(wizard_controller); 154 wizard_controller->SkipToLoginForTesting(LoginScreenContext()); 155 156 content::WindowedNotificationObserver( 157 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, 158 content::NotificationService::AllSources()).Wait(); 159 RunUntilIdle(); 160 } 161 162 void EnrollDevice(const std::string& username) { 163 base::RunLoop loop; 164 policy::EnterpriseInstallAttributes::LockResult result; 165 browser_policy_connector()->GetInstallAttributes()->LockDevice( 166 username, policy::DEVICE_MODE_ENTERPRISE, "100200300", 167 base::Bind(&CopyLockResult, &loop, &result)); 168 loop.Run(); 169 EXPECT_EQ(policy::EnterpriseInstallAttributes::LOCK_SUCCESS, result); 170 RunUntilIdle(); 171 } 172 173 void Login(const std::string& username) { 174 content::WindowedNotificationObserver session_started_observer( 175 chrome::NOTIFICATION_SESSION_STARTED, 176 content::NotificationService::AllSources()); 177 178 ExistingUserController* controller = 179 ExistingUserController::current_controller(); 180 ASSERT_TRUE(controller); 181 WebUILoginDisplay* login_display = 182 static_cast<WebUILoginDisplay*>(controller->login_display()); 183 ASSERT_TRUE(login_display); 184 185 login_display->ShowSigninScreenForCreds(username, "password"); 186 187 // Wait for the session to start after submitting the credentials. This 188 // will wait until all the background requests are done. 189 session_started_observer.Wait(); 190 } 191 192 // Handles an HTTP request sent to the test server. This handler either 193 // uses a canned response in |responses_| if the request path matches one 194 // of the URLs that we mock, otherwise this handler delegates to |fake_gaia_|. 195 scoped_ptr<net::test_server::HttpResponse> HandleRequest( 196 const net::test_server::HttpRequest& request) { 197 scoped_ptr<net::test_server::HttpResponse> response; 198 199 GaiaUrls* gaia = GaiaUrls::GetInstance(); 200 if (request.relative_url == gaia->client_login_to_oauth2_url().path() || 201 request.relative_url == gaia->oauth2_token_url().path() || 202 request.relative_url.find(kDMRegisterRequest) == 0 || 203 request.relative_url.find(kDMPolicyRequest) == 0) { 204 if (!responses_.empty()) { 205 response.reset(responses_.back()); 206 responses_.pop_back(); 207 } 208 } 209 210 return response.Pass(); 211 } 212 213 // Creates a new canned response that will respond with the given HTTP 214 // status |code|. That response is appended to |responses_| and will be the 215 // next response used. 216 // Returns a reference to that response, so that it can be further customized. 217 net::test_server::BasicHttpResponse& PushResponse(net::HttpStatusCode code) { 218 net::test_server::BasicHttpResponse* response = 219 new net::test_server::BasicHttpResponse(); 220 response->set_code(code); 221 responses_.push_back(response); 222 return *response; 223 } 224 225 // Returns the body of the register response from the policy server. 226 std::string GetRegisterResponse() { 227 em::DeviceManagementResponse response; 228 em::DeviceRegisterResponse* register_response = 229 response.mutable_register_response(); 230 register_response->set_device_management_token("1234"); 231 register_response->set_enrollment_type( 232 em::DeviceRegisterResponse::ENTERPRISE); 233 std::string data; 234 EXPECT_TRUE(response.SerializeToString(&data)); 235 return data; 236 } 237 238 // Returns the body of the fetch response from the policy server. 239 std::string GetPolicyResponse() { 240 em::DeviceManagementResponse response; 241 response.mutable_policy_response()->add_response(); 242 std::string data; 243 EXPECT_TRUE(response.SerializeToString(&data)); 244 return data; 245 } 246 247 protected: 248 Profile* profile_added_; 249 250 private: 251 FakeGaia fake_gaia_; 252 std::vector<net::test_server::HttpResponse*> responses_; 253 content::NotificationRegistrar registrar_; 254 255 DISALLOW_COPY_AND_ASSIGN(BlockingLoginTest); 256 }; 257 258 IN_PROC_BROWSER_TEST_P(BlockingLoginTest, LoginBlocksForUser) { 259 // Verify that there isn't a logged in user when the test starts. 260 user_manager::UserManager* user_manager = user_manager::UserManager::Get(); 261 EXPECT_FALSE(user_manager->IsUserLoggedIn()); 262 EXPECT_FALSE(browser_policy_connector()->IsEnterpriseManaged()); 263 EXPECT_FALSE(profile_added_); 264 265 // Enroll the device, if enrollment is enabled for this test instance. 266 if (GetParam().enroll_device) { 267 EnrollDevice(kUsername); 268 269 EXPECT_FALSE(user_manager->IsUserLoggedIn()); 270 EXPECT_TRUE(browser_policy_connector()->IsEnterpriseManaged()); 271 EXPECT_EQ(kDomain, browser_policy_connector()->GetEnterpriseDomain()); 272 EXPECT_FALSE(profile_added_); 273 EXPECT_EQ(policy::USER_AFFILIATION_MANAGED, 274 browser_policy_connector()->GetUserAffiliation(kUsername)); 275 RunUntilIdle(); 276 EXPECT_FALSE(user_manager->IsKnownUser(kUsername)); 277 } 278 279 // Skip the OOBE, go to the sign-in screen, and wait for the login screen to 280 // become visible. 281 SkipToSigninScreen(); 282 EXPECT_FALSE(profile_added_); 283 284 // Prepare the fake HTTP responses. 285 if (GetParam().steps < 5) { 286 // If this instance is not going to complete the entire flow successfully 287 // then the last step will fail. 288 289 // This response body is important to make the gaia fetcher skip its delayed 290 // retry behavior, which makes testing harder. If this is sent to the policy 291 // fetchers then it will make them fail too. 292 PushResponse(net::HTTP_UNAUTHORIZED).set_content("Error=AccountDeleted"); 293 } 294 295 // Push a response for each step that is going to succeed, in reverse order. 296 switch (GetParam().steps) { 297 default: 298 ADD_FAILURE() << "Invalid step number: " << GetParam().steps; 299 return; 300 301 case 5: 302 PushResponse(net::HTTP_OK).set_content(GetPolicyResponse()); 303 304 case 4: 305 PushResponse(net::HTTP_OK).set_content(GetRegisterResponse()); 306 307 case 3: 308 PushResponse(net::HTTP_OK).set_content(kOAuth2AccessTokenData); 309 310 case 2: 311 PushResponse(net::HTTP_OK).set_content(kOAuth2TokenPairData); 312 313 case 1: 314 PushResponse(net::HTTP_OK) 315 .AddCustomHeader("Set-Cookie", kOAuthCodeCookie); 316 break; 317 318 case 0: 319 break; 320 } 321 322 // Login now. This verifies that logging in with the canned responses (which 323 // may include failures) won't be blocked due to the potential failures. 324 EXPECT_FALSE(profile_added_); 325 Login(GetParam().username); 326 EXPECT_TRUE(profile_added_); 327 ASSERT_TRUE(user_manager->IsUserLoggedIn()); 328 EXPECT_TRUE(user_manager->IsCurrentUserNew()); 329 } 330 331 const BlockingLoginTestParam kBlockinLoginTestCases[] = { 332 { 0, kUsername, true }, 333 { 1, kUsername, true }, 334 { 2, kUsername, true }, 335 { 3, kUsername, true }, 336 { 4, kUsername, true }, 337 { 5, kUsername, true }, 338 { 0, kUsername, false }, 339 { 1, kUsername, false }, 340 { 2, kUsername, false }, 341 { 3, kUsername, false }, 342 { 4, kUsername, false }, 343 { 5, kUsername, false }, 344 { 0, kUsernameOtherDomain, true }, 345 { 1, kUsernameOtherDomain, true }, 346 { 2, kUsernameOtherDomain, true }, 347 { 3, kUsernameOtherDomain, true }, 348 { 4, kUsernameOtherDomain, true }, 349 { 5, kUsernameOtherDomain, true }, 350 }; 351 352 INSTANTIATE_TEST_CASE_P(BlockingLoginTestInstance, 353 BlockingLoginTest, 354 testing::ValuesIn(kBlockinLoginTestCases)); 355 356 } // namespace chromeos 357