| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2011 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 "chrome/browser/chromeos/login/login_utils.h" |
| 6 |
| 7 #include "base/basictypes.h" |
| 8 #include "base/command_line.h" |
| 9 #include "base/message_loop.h" |
| 10 #include "base/path_service.h" |
| 11 #include "base/scoped_temp_dir.h" |
| 12 #include "base/string_util.h" |
| 13 #include "chrome/browser/chromeos/cros/cros_library.h" |
| 14 #include "chrome/browser/chromeos/cros/mock_cryptohome_library.h" |
| 15 #include "chrome/browser/chromeos/cros/mock_library_loader.h" |
| 16 #include "chrome/browser/chromeos/dbus/mock_dbus_thread_manager.h" |
| 17 #include "chrome/browser/chromeos/dbus/mock_session_manager_client.h" |
| 18 #include "chrome/browser/chromeos/login/authenticator.h" |
| 19 #include "chrome/browser/chromeos/login/login_status_consumer.h" |
| 20 #include "chrome/browser/chromeos/login/user_manager.h" |
| 21 #include "chrome/browser/io_thread.h" |
| 22 #include "chrome/browser/net/predictor.h" |
| 23 #include "chrome/browser/policy/browser_policy_connector.h" |
| 24 #include "chrome/browser/policy/proto/device_management_backend.pb.h" |
| 25 #include "chrome/browser/profiles/profile_manager.h" |
| 26 #include "chrome/common/chrome_paths.h" |
| 27 #include "chrome/common/chrome_switches.h" |
| 28 #include "chrome/common/net/gaia/gaia_urls.h" |
| 29 #include "chrome/common/pref_names.h" |
| 30 #include "chrome/common/net/gaia/gaia_auth_consumer.h" |
| 31 #include "chrome/test/base/testing_browser_process.h" |
| 32 #include "chrome/test/base/testing_pref_service.h" |
| 33 #include "content/test/test_browser_thread.h" |
| 34 #include "content/test/test_url_fetcher_factory.h" |
| 35 #include "net/url_request/url_request.h" |
| 36 #include "net/url_request/url_request_status.h" |
| 37 #include "testing/gmock/include/gmock/gmock.h" |
| 38 #include "testing/gtest/include/gtest/gtest.h" |
| 39 |
| 40 namespace chromeos { |
| 41 |
| 42 namespace { |
| 43 |
| 44 namespace em = enterprise_management; |
| 45 |
| 46 using ::testing::DoAll; |
| 47 using ::testing::Return; |
| 48 using ::testing::SetArgPointee; |
| 49 using ::testing::_; |
| 50 |
| 51 const char kTrue[] = "true"; |
| 52 const char kDomain[] = "domain.com"; |
| 53 const char kUsername[] = "user@domain.com"; |
| 54 const char kUsernameOtherDomain[] = "user@other.com"; |
| 55 const char kAttributeOwned[] = "enterprise.owned"; |
| 56 const char kAttributeOwner[] = "enterprise.user"; |
| 57 |
| 58 const char kOAuthTokenCookie[] = "oauth_token=1234"; |
| 59 const char kOAuthGetAccessTokenData[] = |
| 60 "oauth_token=1234&oauth_token_secret=1234"; |
| 61 const char kOAuthServiceTokenData[] = |
| 62 "wrap_access_token=1234&wrap_access_token_expires_in=123456789"; |
| 63 |
| 64 const char kDMServer[] = "http://server/device_management"; |
| 65 const char kDMRegisterRequest[] = |
| 66 "http://server/device_management?request=register"; |
| 67 const char kDMPolicyRequest[] = |
| 68 "http://server/device_management?request=policy"; |
| 69 |
| 70 const char kDMToken[] = "1234"; |
| 71 |
| 72 ACTION_P(MockSessionManagerClientPolicyCallback, policy) { |
| 73 arg0.Run(policy); |
| 74 } |
| 75 |
| 76 // Subclass IOThread to expose set_message_loop. |
| 77 class TestIOThread : public IOThread { |
| 78 public: |
| 79 explicit TestIOThread(PrefService* local_state) |
| 80 : IOThread(local_state, NULL, NULL) {} |
| 81 |
| 82 using IOThread::set_message_loop; |
| 83 }; |
| 84 |
| 85 // This test is linked into browser_tests because it modifies the command line |
| 86 // for the process in SetUp, and also because it overrides a PathService dir. |
| 87 template<typename TESTBASE> |
| 88 class LoginUtilsTestBase : public TESTBASE, |
| 89 public LoginUtils::Delegate, |
| 90 public LoginStatusConsumer { |
| 91 public: |
| 92 LoginUtilsTestBase() |
| 93 : loop_(MessageLoop::TYPE_IO), |
| 94 browser_process_( |
| 95 static_cast<TestingBrowserProcess*>(g_browser_process)), |
| 96 local_state_(browser_process_), |
| 97 ui_thread_(content::BrowserThread::UI, &loop_), |
| 98 file_thread_(content::BrowserThread::FILE, &loop_), |
| 99 io_thread_(local_state_.Get()), |
| 100 prepared_profile_(NULL) {} |
| 101 |
| 102 virtual void SetUp() OVERRIDE { |
| 103 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); |
| 104 // BrowserPolicyConnector makes the UserPolicyCache read relative to this |
| 105 // path. Make sure it's in a clean state. |
| 106 PathService::Override(chrome::DIR_USER_DATA, scoped_temp_dir_.path()); |
| 107 |
| 108 CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| 109 command_line->AppendSwitch(switches::kEnableDevicePolicy); |
| 110 command_line->AppendSwitchASCII(switches::kDeviceManagementUrl, kDMServer); |
| 111 command_line->AppendSwitchASCII(switches::kLoginProfile, "user"); |
| 112 |
| 113 local_state_.Get()->RegisterStringPref(prefs::kApplicationLocale, ""); |
| 114 |
| 115 browser_process_->SetIOThread(&io_thread_); |
| 116 |
| 117 DBusThreadManager::InitializeForTesting(&dbus_thread_manager_); |
| 118 |
| 119 MockSessionManagerClient* session_managed_client = |
| 120 dbus_thread_manager_.mock_session_manager_client(); |
| 121 EXPECT_CALL(*session_managed_client, RetrievePolicy(_)) |
| 122 .WillRepeatedly(MockSessionManagerClientPolicyCallback("")); |
| 123 |
| 124 CrosLibrary::TestApi* test_api = CrosLibrary::Get()->GetTestApi(); |
| 125 ASSERT_TRUE(test_api); |
| 126 |
| 127 MockLibraryLoader* loader = new MockLibraryLoader(); |
| 128 ON_CALL(*loader, Load(_)).WillByDefault(Return(true)); |
| 129 test_api->SetLibraryLoader(loader, true); |
| 130 |
| 131 cryptohome_ = new MockCryptohomeLibrary(); |
| 132 EXPECT_CALL(*cryptohome_, InstallAttributesIsReady()) |
| 133 .WillRepeatedly(Return(true)); |
| 134 EXPECT_CALL(*cryptohome_, InstallAttributesIsInvalid()) |
| 135 .WillRepeatedly(Return(false)); |
| 136 EXPECT_CALL(*cryptohome_, InstallAttributesIsFirstInstall()) |
| 137 .WillRepeatedly(Return(true)); |
| 138 EXPECT_CALL(*cryptohome_, TpmIsEnabled()) |
| 139 .WillRepeatedly(Return(false)); |
| 140 EXPECT_CALL(*cryptohome_, IsMounted()) |
| 141 .WillRepeatedly(Return(true)); |
| 142 EXPECT_CALL(*cryptohome_, InstallAttributesSet(kAttributeOwned, kTrue)) |
| 143 .WillRepeatedly(Return(true)); |
| 144 EXPECT_CALL(*cryptohome_, InstallAttributesSet(kAttributeOwner, |
| 145 kUsername)) |
| 146 .WillRepeatedly(Return(true)); |
| 147 EXPECT_CALL(*cryptohome_, InstallAttributesFinalize()) |
| 148 .WillRepeatedly(Return(true)); |
| 149 EXPECT_CALL(*cryptohome_, InstallAttributesGet(kAttributeOwned, _)) |
| 150 .WillRepeatedly(DoAll(SetArgPointee<1>(kTrue), |
| 151 Return(true))); |
| 152 EXPECT_CALL(*cryptohome_, InstallAttributesGet(kAttributeOwner, _)) |
| 153 .WillRepeatedly(DoAll(SetArgPointee<1>(kUsername), |
| 154 Return(true))); |
| 155 test_api->SetCryptohomeLibrary(cryptohome_, true); |
| 156 |
| 157 browser_process_->SetProfileManager( |
| 158 new ProfileManagerWithoutInit(scoped_temp_dir_.path())); |
| 159 connector_ = policy::BrowserPolicyConnector::Create(); |
| 160 browser_process_->SetBrowserPolicyConnector(connector_); |
| 161 |
| 162 loop_.RunAllPending(); |
| 163 } |
| 164 |
| 165 virtual void TearDown() OVERRIDE { |
| 166 loop_.RunAllPending(); |
| 167 { |
| 168 // chrome_browser_net::Predictor usually skips its shutdown routines on |
| 169 // unit_tests, but does the full thing when |
| 170 // g_browser_process->profile_manager() is valid during initialization. |
| 171 // Run a task on a temporary BrowserThread::IO that allows skipping |
| 172 // these routines. |
| 173 io_thread_.set_message_loop(&loop_); |
| 174 loop_.PostTask(FROM_HERE, |
| 175 base::Bind(&LoginUtilsTestBase::TearDownOnIO, |
| 176 base::Unretained(this))); |
| 177 loop_.RunAllPending(); |
| 178 io_thread_.set_message_loop(NULL); |
| 179 } |
| 180 |
| 181 // These trigger some tasks that have to run while BrowserThread::UI |
| 182 // exists. |
| 183 connector_ = NULL; |
| 184 browser_process_->SetBrowserPolicyConnector(NULL); |
| 185 browser_process_->SetProfileManager(NULL); |
| 186 loop_.RunAllPending(); |
| 187 } |
| 188 |
| 189 void TearDownOnIO() { |
| 190 std::vector<Profile*> profiles = |
| 191 browser_process_->profile_manager()->GetLoadedProfiles(); |
| 192 for (size_t i = 0; i < profiles.size(); ++i) { |
| 193 chrome_browser_net::Predictor* predictor = |
| 194 profiles[i]->GetNetworkPredictor(); |
| 195 if (predictor) { |
| 196 predictor->EnablePredictorOnIOThread(false); |
| 197 predictor->Shutdown(); |
| 198 } |
| 199 } |
| 200 } |
| 201 |
| 202 virtual void OnProfilePrepared(Profile* profile) OVERRIDE { |
| 203 EXPECT_FALSE(prepared_profile_); |
| 204 prepared_profile_ = profile; |
| 205 } |
| 206 |
| 207 virtual void OnLoginFailure(const LoginFailure& error) OVERRIDE { |
| 208 FAIL() << "OnLoginFailure not expected"; |
| 209 } |
| 210 |
| 211 virtual void OnLoginSuccess(const std::string& username, |
| 212 const std::string& password, |
| 213 const GaiaAuthConsumer::ClientLoginResult& creds, |
| 214 bool pending_requests, |
| 215 bool using_oauth) OVERRIDE { |
| 216 FAIL() << "OnLoginSuccess not expected"; |
| 217 } |
| 218 |
| 219 void LockDevice(const std::string& username) { |
| 220 EXPECT_CALL(*cryptohome_, InstallAttributesIsFirstInstall()) |
| 221 .WillOnce(Return(true)) |
| 222 .WillRepeatedly(Return(false)); |
| 223 EXPECT_EQ(policy::EnterpriseInstallAttributes::LOCK_SUCCESS, |
| 224 connector_->LockDevice(username)); |
| 225 loop_.RunAllPending(); |
| 226 } |
| 227 |
| 228 void PrepareProfile(const std::string& username) { |
| 229 MockSessionManagerClient* session_managed_client = |
| 230 dbus_thread_manager_.mock_session_manager_client(); |
| 231 EXPECT_CALL(*session_managed_client, StartSession(_)); |
| 232 |
| 233 // crypto::Encryptor::SetCounter wants exactly 128 bits, and |
| 234 // ParallelAuthenticator CHECKs on that. |
| 235 CryptohomeBlob blob; |
| 236 for (size_t i = 0; i < 16; ++i) |
| 237 blob.push_back(i); |
| 238 EXPECT_CALL(*cryptohome_, GetSystemSalt()) |
| 239 .WillRepeatedly(Return(blob)); |
| 240 EXPECT_CALL(*cryptohome_, AsyncMount(_, _, _, _)) |
| 241 .WillRepeatedly(Return(true)); |
| 242 |
| 243 scoped_refptr<Authenticator> authenticator = |
| 244 LoginUtils::Get()->CreateAuthenticator(this); |
| 245 authenticator->CompleteLogin(ProfileManager::GetDefaultProfile(), |
| 246 username, |
| 247 "password"); |
| 248 |
| 249 GaiaAuthConsumer::ClientLoginResult credentials; |
| 250 LoginUtils::Get()->PrepareProfile( |
| 251 username, "password", credentials, false, true, false, this); |
| 252 loop_.RunAllPending(); |
| 253 } |
| 254 |
| 255 TestURLFetcher* PrepareOAuthFetcher(const std::string& expected_url) { |
| 256 TestURLFetcher* fetcher = test_url_fetcher_factory_.GetFetcherByID(0); |
| 257 EXPECT_TRUE(fetcher); |
| 258 EXPECT_TRUE(fetcher->delegate()); |
| 259 EXPECT_TRUE(StartsWithASCII(fetcher->GetOriginalURL().spec(), |
| 260 expected_url, |
| 261 true)); |
| 262 fetcher->set_url(fetcher->GetOriginalURL()); |
| 263 fetcher->set_response_code(200); |
| 264 fetcher->set_status(net::URLRequestStatus()); |
| 265 return fetcher; |
| 266 } |
| 267 |
| 268 TestURLFetcher* PrepareDMServiceFetcher( |
| 269 const std::string& expected_url, |
| 270 const em::DeviceManagementResponse& response) { |
| 271 TestURLFetcher* fetcher = test_url_fetcher_factory_.GetFetcherByID(0); |
| 272 EXPECT_TRUE(fetcher); |
| 273 EXPECT_TRUE(fetcher->delegate()); |
| 274 EXPECT_TRUE(StartsWithASCII(fetcher->GetOriginalURL().spec(), |
| 275 expected_url, |
| 276 true)); |
| 277 fetcher->set_url(fetcher->GetOriginalURL()); |
| 278 fetcher->set_response_code(200); |
| 279 fetcher->set_status(net::URLRequestStatus()); |
| 280 std::string data; |
| 281 EXPECT_TRUE(response.SerializeToString(&data)); |
| 282 fetcher->SetResponseString(data); |
| 283 return fetcher; |
| 284 } |
| 285 |
| 286 TestURLFetcher* PrepareDMRegisterFetcher() { |
| 287 em::DeviceManagementResponse response; |
| 288 em::DeviceRegisterResponse* register_response = |
| 289 response.mutable_register_response(); |
| 290 register_response->set_device_management_token(kDMToken); |
| 291 return PrepareDMServiceFetcher(kDMRegisterRequest, response); |
| 292 } |
| 293 |
| 294 TestURLFetcher* PrepareDMPolicyFetcher() { |
| 295 em::DeviceManagementResponse response; |
| 296 response.mutable_policy_response()->add_response(); |
| 297 return PrepareDMServiceFetcher(kDMPolicyRequest, response); |
| 298 } |
| 299 |
| 300 protected: |
| 301 ScopedStubCrosEnabler stub_cros_enabler_; |
| 302 |
| 303 MessageLoop loop_; |
| 304 TestingBrowserProcess* browser_process_; |
| 305 ScopedTestingLocalState local_state_; |
| 306 |
| 307 content::TestBrowserThread ui_thread_; |
| 308 content::TestBrowserThread file_thread_; |
| 309 TestIOThread io_thread_; |
| 310 |
| 311 MockDBusThreadManager dbus_thread_manager_; |
| 312 TestURLFetcherFactory test_url_fetcher_factory_; |
| 313 |
| 314 policy::BrowserPolicyConnector* connector_; |
| 315 MockCryptohomeLibrary* cryptohome_; |
| 316 Profile* prepared_profile_; |
| 317 |
| 318 private: |
| 319 ScopedTempDir scoped_temp_dir_; |
| 320 |
| 321 DISALLOW_COPY_AND_ASSIGN(LoginUtilsTestBase); |
| 322 }; |
| 323 |
| 324 class LoginUtilsTest : public LoginUtilsTestBase<testing::Test> { |
| 325 }; |
| 326 |
| 327 class LoginUtilsBlockingLoginTest |
| 328 : public LoginUtilsTestBase<testing::TestWithParam<int> > { |
| 329 }; |
| 330 |
| 331 TEST_F(LoginUtilsTest, NormalLoginDoesntBlock) { |
| 332 UserManager* user_manager = UserManager::Get(); |
| 333 EXPECT_FALSE(user_manager->user_is_logged_in()); |
| 334 EXPECT_FALSE(connector_->IsEnterpriseManaged()); |
| 335 EXPECT_FALSE(prepared_profile_); |
| 336 |
| 337 // The profile will be created without waiting for a policy response. |
| 338 PrepareProfile(kUsername); |
| 339 |
| 340 EXPECT_TRUE(prepared_profile_); |
| 341 EXPECT_TRUE(user_manager->user_is_logged_in()); |
| 342 EXPECT_EQ(kUsername, user_manager->logged_in_user().email()); |
| 343 } |
| 344 |
| 345 TEST_F(LoginUtilsTest, EnterpriseLoginDoesntBlockForNormalUser) { |
| 346 UserManager* user_manager = UserManager::Get(); |
| 347 EXPECT_FALSE(user_manager->user_is_logged_in()); |
| 348 EXPECT_FALSE(connector_->IsEnterpriseManaged()); |
| 349 EXPECT_FALSE(prepared_profile_); |
| 350 |
| 351 // Enroll the device. |
| 352 LockDevice(kUsername); |
| 353 |
| 354 EXPECT_FALSE(user_manager->user_is_logged_in()); |
| 355 EXPECT_TRUE(connector_->IsEnterpriseManaged()); |
| 356 EXPECT_EQ(kDomain, connector_->GetEnterpriseDomain()); |
| 357 EXPECT_FALSE(prepared_profile_); |
| 358 |
| 359 // Login with a non-enterprise user shouldn't block. |
| 360 PrepareProfile(kUsernameOtherDomain); |
| 361 |
| 362 EXPECT_TRUE(prepared_profile_); |
| 363 EXPECT_TRUE(user_manager->user_is_logged_in()); |
| 364 EXPECT_EQ(kUsernameOtherDomain, user_manager->logged_in_user().email()); |
| 365 } |
| 366 |
| 367 TEST_P(LoginUtilsBlockingLoginTest, EnterpriseLoginBlocksForEnterpriseUser) { |
| 368 UserManager* user_manager = UserManager::Get(); |
| 369 EXPECT_FALSE(user_manager->user_is_logged_in()); |
| 370 EXPECT_FALSE(connector_->IsEnterpriseManaged()); |
| 371 EXPECT_FALSE(prepared_profile_); |
| 372 |
| 373 // Enroll the device. |
| 374 LockDevice(kUsername); |
| 375 |
| 376 EXPECT_FALSE(user_manager->user_is_logged_in()); |
| 377 EXPECT_TRUE(connector_->IsEnterpriseManaged()); |
| 378 EXPECT_EQ(kDomain, connector_->GetEnterpriseDomain()); |
| 379 EXPECT_FALSE(prepared_profile_); |
| 380 |
| 381 // Login with a user of the enterprise domain waits for policy. |
| 382 PrepareProfile(kUsername); |
| 383 |
| 384 EXPECT_FALSE(prepared_profile_); |
| 385 EXPECT_TRUE(user_manager->user_is_logged_in()); |
| 386 |
| 387 GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); |
| 388 TestURLFetcher* fetcher; |
| 389 |
| 390 // |steps| is the test parameter, and is the number of successful fetches. |
| 391 // The first incomplete fetch will fail. In any case, the profile creation |
| 392 // should resume. |
| 393 int steps = GetParam(); |
| 394 |
| 395 do { |
| 396 if (steps < 1) break; |
| 397 |
| 398 // Fake OAuth token retrieval: |
| 399 fetcher = PrepareOAuthFetcher(gaia_urls->get_oauth_token_url()); |
| 400 net::ResponseCookies cookies; |
| 401 cookies.push_back(kOAuthTokenCookie); |
| 402 fetcher->set_cookies(cookies); |
| 403 fetcher->delegate()->OnURLFetchComplete(fetcher); |
| 404 if (steps < 2) break; |
| 405 |
| 406 // Fake OAuth access token retrieval: |
| 407 fetcher = PrepareOAuthFetcher(gaia_urls->oauth_get_access_token_url()); |
| 408 fetcher->SetResponseString(kOAuthGetAccessTokenData); |
| 409 fetcher->delegate()->OnURLFetchComplete(fetcher); |
| 410 if (steps < 3) break; |
| 411 |
| 412 // Fake OAuth service token retrieval: |
| 413 fetcher = PrepareOAuthFetcher(gaia_urls->oauth_wrap_bridge_url()); |
| 414 fetcher->SetResponseString(kOAuthServiceTokenData); |
| 415 fetcher->delegate()->OnURLFetchComplete(fetcher); |
| 416 |
| 417 // The cloud policy subsystem is now ready to fetch the dmtoken and the user |
| 418 // policy. |
| 419 loop_.RunAllPending(); |
| 420 if (steps < 4) break; |
| 421 |
| 422 fetcher = PrepareDMRegisterFetcher(); |
| 423 fetcher->delegate()->OnURLFetchComplete(fetcher); |
| 424 // The policy fetch job has now been scheduled, run it: |
| 425 loop_.RunAllPending(); |
| 426 if (steps < 5) break; |
| 427 |
| 428 // Verify that there is no profile prepared just before the policy fetch. |
| 429 EXPECT_FALSE(prepared_profile_); |
| 430 |
| 431 fetcher = PrepareDMPolicyFetcher(); |
| 432 fetcher->delegate()->OnURLFetchComplete(fetcher); |
| 433 } while (0); |
| 434 |
| 435 if (steps < 5) { |
| 436 // Verify that the profile hasn't been created yet. |
| 437 EXPECT_FALSE(prepared_profile_); |
| 438 |
| 439 // Make the current fetcher fail. |
| 440 TestURLFetcher* fetcher = test_url_fetcher_factory_.GetFetcherByID(0); |
| 441 EXPECT_TRUE(fetcher); |
| 442 EXPECT_TRUE(fetcher->delegate()); |
| 443 fetcher->set_url(fetcher->GetOriginalURL()); |
| 444 fetcher->set_response_code(500); |
| 445 fetcher->delegate()->OnURLFetchComplete(fetcher); |
| 446 } |
| 447 |
| 448 // The profile is finally ready: |
| 449 EXPECT_TRUE(prepared_profile_); |
| 450 } |
| 451 |
| 452 INSTANTIATE_TEST_CASE_P( |
| 453 LoginUtilsBlockingLoginTestInstance, |
| 454 LoginUtilsBlockingLoginTest, |
| 455 testing::Values(0, 1, 2, 3, 4, 5)); |
| 456 |
| 457 } // namespace |
| 458 |
| 459 } |
| OLD | NEW |