Index: chrome/browser/chromeos/login/login_utils_browsertest.cc |
diff --git a/chrome/browser/chromeos/login/login_utils_browsertest.cc b/chrome/browser/chromeos/login/login_utils_browsertest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..06673dc8adcef408bef4e02dd86f883a076c390a |
--- /dev/null |
+++ b/chrome/browser/chromeos/login/login_utils_browsertest.cc |
@@ -0,0 +1,457 @@ |
+// Copyright (c) 2011 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/login_utils.h" |
+ |
+#include "base/basictypes.h" |
+#include "base/command_line.h" |
+#include "base/message_loop.h" |
+#include "base/path_service.h" |
+#include "base/scoped_temp_dir.h" |
+#include "base/string_util.h" |
+#include "chrome/browser/chromeos/cros/cros_library.h" |
+#include "chrome/browser/chromeos/cros/mock_cryptohome_library.h" |
+#include "chrome/browser/chromeos/cros/mock_library_loader.h" |
+#include "chrome/browser/chromeos/dbus/mock_dbus_thread_manager.h" |
+#include "chrome/browser/chromeos/dbus/mock_session_manager_client.h" |
+#include "chrome/browser/chromeos/login/authenticator.h" |
+#include "chrome/browser/chromeos/login/login_status_consumer.h" |
+#include "chrome/browser/chromeos/login/user_manager.h" |
+#include "chrome/browser/io_thread.h" |
+#include "chrome/browser/net/predictor.h" |
+#include "chrome/browser/policy/browser_policy_connector.h" |
+#include "chrome/browser/policy/proto/device_management_backend.pb.h" |
+#include "chrome/browser/profiles/profile_manager.h" |
+#include "chrome/common/chrome_paths.h" |
+#include "chrome/common/chrome_switches.h" |
+#include "chrome/common/net/gaia/gaia_urls.h" |
+#include "chrome/common/pref_names.h" |
+#include "chrome/common/net/gaia/gaia_auth_consumer.h" |
+#include "chrome/test/base/testing_browser_process.h" |
+#include "chrome/test/base/testing_pref_service.h" |
+#include "content/test/test_browser_thread.h" |
+#include "content/test/test_url_fetcher_factory.h" |
+#include "net/url_request/url_request.h" |
+#include "net/url_request/url_request_status.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace chromeos { |
+ |
+namespace { |
+ |
+namespace em = enterprise_management; |
+ |
+using ::testing::DoAll; |
+using ::testing::Return; |
+using ::testing::SetArgPointee; |
+using ::testing::_; |
+ |
+const char kTrue[] = "true"; |
+const char kDomain[] = "domain.com"; |
+const char kUsername[] = "user@domain.com"; |
+const char kUsernameOtherDomain[] = "user@other.com"; |
+const char kAttributeOwned[] = "enterprise.owned"; |
+const char kAttributeOwner[] = "enterprise.user"; |
+ |
+const char kOAuthTokenCookie[] = "oauth_token=1234"; |
+const char kOAuthGetAccessTokenData[] = |
+ "oauth_token=1234&oauth_token_secret=1234"; |
+const char kOAuthServiceTokenData[] = |
+ "wrap_access_token=1234&wrap_access_token_expires_in=123456789"; |
+ |
+const char kDMServer[] = "http://server/device_management"; |
+const char kDMRegisterRequest[] = |
+ "http://server/device_management?request=register"; |
+const char kDMPolicyRequest[] = |
+ "http://server/device_management?request=policy"; |
+ |
+const char kDMToken[] = "1234"; |
+ |
+ACTION_P(MockSessionManagerClientPolicyCallback, policy) { |
+ arg0.Run(policy); |
+} |
+ |
+// Subclass IOThread to expose set_message_loop. |
+class TestIOThread : public IOThread { |
+ public: |
+ explicit TestIOThread(PrefService* local_state) |
+ : IOThread(local_state, NULL, NULL) {} |
+ |
+ using IOThread::set_message_loop; |
+}; |
+ |
+template<typename TESTBASE> |
+class LoginUtilsTestBase : public TESTBASE, |
+ public LoginUtils::Delegate, |
+ public LoginStatusConsumer { |
+ public: |
+ LoginUtilsTestBase() |
+ : loop_(MessageLoop::TYPE_IO), |
+ browser_process_( |
+ static_cast<TestingBrowserProcess*>(g_browser_process)), |
+ local_state_(browser_process_), |
+ ui_thread_(content::BrowserThread::UI, &loop_), |
+ file_thread_(content::BrowserThread::FILE, &loop_), |
+ io_thread_(local_state_.Get()), |
+ prepared_profile_(NULL) {} |
+ |
+ virtual void SetUp() OVERRIDE { |
+ ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); |
+ // BrowserPolicyConnector makes the UserPolicyCache read relative to this |
+ // path. Make sure it's in a clean state. |
+ PathService::Override(chrome::DIR_USER_DATA, scoped_temp_dir_.path()); |
+ |
+ CommandLine* command_line = CommandLine::ForCurrentProcess(); |
+ command_line->AppendSwitch(switches::kEnableDevicePolicy); |
+ command_line->AppendSwitchASCII(switches::kDeviceManagementUrl, kDMServer); |
+ command_line->AppendSwitchASCII(switches::kLoginProfile, "user"); |
+ |
+ local_state_.Get()->RegisterStringPref(prefs::kApplicationLocale, ""); |
+ |
+ browser_process_->SetIOThread(&io_thread_); |
+ |
+ DBusThreadManager::InitializeForTesting(&dbus_thread_manager_); |
+ |
+ MockSessionManagerClient* session_managed_client = |
+ dbus_thread_manager_.mock_session_manager_client(); |
+ EXPECT_CALL(*session_managed_client, RetrievePolicy(_)) |
+ .WillRepeatedly(MockSessionManagerClientPolicyCallback("")); |
+ |
+ CrosLibrary::TestApi* test_api = CrosLibrary::Get()->GetTestApi(); |
+ ASSERT_TRUE(test_api); |
+ |
+ MockLibraryLoader* loader = new MockLibraryLoader(); |
+ ON_CALL(*loader, Load(_)).WillByDefault(Return(true)); |
+ test_api->SetLibraryLoader(loader, true); |
+ |
+ cryptohome_ = new MockCryptohomeLibrary(); |
+ EXPECT_CALL(*cryptohome_, InstallAttributesIsReady()) |
+ .WillRepeatedly(Return(true)); |
+ EXPECT_CALL(*cryptohome_, InstallAttributesIsInvalid()) |
+ .WillRepeatedly(Return(false)); |
+ EXPECT_CALL(*cryptohome_, InstallAttributesIsFirstInstall()) |
+ .WillRepeatedly(Return(true)); |
+ EXPECT_CALL(*cryptohome_, TpmIsEnabled()) |
+ .WillRepeatedly(Return(false)); |
+ EXPECT_CALL(*cryptohome_, IsMounted()) |
+ .WillRepeatedly(Return(true)); |
+ EXPECT_CALL(*cryptohome_, InstallAttributesSet(kAttributeOwned, kTrue)) |
+ .WillRepeatedly(Return(true)); |
+ EXPECT_CALL(*cryptohome_, InstallAttributesSet(kAttributeOwner, |
+ kUsername)) |
+ .WillRepeatedly(Return(true)); |
+ EXPECT_CALL(*cryptohome_, InstallAttributesFinalize()) |
+ .WillRepeatedly(Return(true)); |
+ EXPECT_CALL(*cryptohome_, InstallAttributesGet(kAttributeOwned, _)) |
+ .WillRepeatedly(DoAll(SetArgPointee<1>(kTrue), |
+ Return(true))); |
+ EXPECT_CALL(*cryptohome_, InstallAttributesGet(kAttributeOwner, _)) |
+ .WillRepeatedly(DoAll(SetArgPointee<1>(kUsername), |
+ Return(true))); |
+ test_api->SetCryptohomeLibrary(cryptohome_, true); |
+ |
+ browser_process_->SetProfileManager( |
+ new ProfileManagerWithoutInit(scoped_temp_dir_.path())); |
+ connector_ = policy::BrowserPolicyConnector::Create(); |
+ browser_process_->SetBrowserPolicyConnector(connector_); |
+ |
+ loop_.RunAllPending(); |
+ } |
+ |
+ virtual void TearDown() OVERRIDE { |
+ loop_.RunAllPending(); |
+ { |
+ // chrome_browser_net::Predictor usually skips its shutdown routines on |
+ // unit_tests, but does the full thing when |
+ // g_browser_process->profile_manager() is valid during initialization. |
+ // Run a task on a temporary BrowserThread::IO that allows skipping |
+ // these routines. |
+ io_thread_.set_message_loop(&loop_); |
+ loop_.PostTask(FROM_HERE, |
+ base::Bind(&LoginUtilsTestBase::TearDownOnIO, |
+ base::Unretained(this))); |
+ loop_.RunAllPending(); |
+ io_thread_.set_message_loop(NULL); |
+ } |
+ |
+ // These trigger some tasks that have to run while BrowserThread::UI |
+ // exists. |
+ connector_ = NULL; |
+ browser_process_->SetBrowserPolicyConnector(NULL); |
+ browser_process_->SetProfileManager(NULL); |
+ loop_.RunAllPending(); |
+ } |
+ |
+ void TearDownOnIO() { |
+ std::vector<Profile*> profiles = |
+ browser_process_->profile_manager()->GetLoadedProfiles(); |
+ for (size_t i = 0; i < profiles.size(); ++i) { |
+ chrome_browser_net::Predictor* predictor = |
+ profiles[i]->GetNetworkPredictor(); |
+ if (predictor) { |
+ predictor->EnablePredictorOnIOThread(false); |
+ predictor->Shutdown(); |
+ } |
+ } |
+ } |
+ |
+ virtual void OnProfilePrepared(Profile* profile) OVERRIDE { |
+ EXPECT_FALSE(prepared_profile_); |
+ prepared_profile_ = profile; |
+ } |
+ |
+ virtual void OnLoginFailure(const LoginFailure& error) OVERRIDE { |
+ FAIL() << "OnLoginFailure not expected"; |
+ } |
+ |
+ virtual void OnLoginSuccess(const std::string& username, |
+ const std::string& password, |
+ const GaiaAuthConsumer::ClientLoginResult& creds, |
+ bool pending_requests, |
+ bool using_oauth) OVERRIDE { |
+ FAIL() << "OnLoginSuccess not expected"; |
+ } |
+ |
+ void LockDevice(const std::string& username) { |
+ EXPECT_CALL(*cryptohome_, InstallAttributesIsFirstInstall()) |
+ .WillOnce(Return(true)) |
+ .WillRepeatedly(Return(false)); |
+ EXPECT_EQ(policy::EnterpriseInstallAttributes::LOCK_SUCCESS, |
+ connector_->LockDevice(username)); |
+ loop_.RunAllPending(); |
+ } |
+ |
+ void PrepareProfile(const std::string& username) { |
+ MockSessionManagerClient* session_managed_client = |
+ dbus_thread_manager_.mock_session_manager_client(); |
+ EXPECT_CALL(*session_managed_client, StartSession(_)); |
+ |
+ // crypto::Encryptor::SetCounter wants exactly 128 bits, and |
+ // ParallelAuthenticator CHECKs on that. |
+ CryptohomeBlob blob; |
+ for (size_t i = 0; i < 16; ++i) |
+ blob.push_back(i); |
+ EXPECT_CALL(*cryptohome_, GetSystemSalt()) |
+ .WillRepeatedly(Return(blob)); |
+ EXPECT_CALL(*cryptohome_, AsyncMount(_, _, _, _)) |
+ .WillRepeatedly(Return(true)); |
+ |
+ scoped_refptr<Authenticator> authenticator = |
+ LoginUtils::Get()->CreateAuthenticator(this); |
+ authenticator->CompleteLogin(ProfileManager::GetDefaultProfile(), |
+ username, |
+ "password"); |
+ |
+ GaiaAuthConsumer::ClientLoginResult credentials; |
+ LoginUtils::Get()->PrepareProfile( |
+ username, "password", credentials, false, true, false, this); |
+ loop_.RunAllPending(); |
+ } |
+ |
+ TestURLFetcher* PrepareOAuthFetcher(const std::string& expected_url) { |
+ TestURLFetcher* fetcher = test_url_fetcher_factory_.GetFetcherByID(0); |
+ EXPECT_TRUE(fetcher); |
+ EXPECT_TRUE(fetcher->delegate()); |
+ EXPECT_TRUE(StartsWithASCII(fetcher->GetOriginalURL().spec(), |
+ expected_url, |
+ true)); |
+ fetcher->set_url(fetcher->GetOriginalURL()); |
+ fetcher->set_response_code(200); |
+ fetcher->set_status(net::URLRequestStatus()); |
+ return fetcher; |
+ } |
+ |
+ TestURLFetcher* PrepareDMServiceFetcher( |
+ const std::string& expected_url, |
+ const em::DeviceManagementResponse& response) { |
+ TestURLFetcher* fetcher = test_url_fetcher_factory_.GetFetcherByID(0); |
+ EXPECT_TRUE(fetcher); |
+ EXPECT_TRUE(fetcher->delegate()); |
+ EXPECT_TRUE(StartsWithASCII(fetcher->GetOriginalURL().spec(), |
+ expected_url, |
+ true)); |
+ fetcher->set_url(fetcher->GetOriginalURL()); |
+ fetcher->set_response_code(200); |
+ fetcher->set_status(net::URLRequestStatus()); |
+ std::string data; |
+ EXPECT_TRUE(response.SerializeToString(&data)); |
+ fetcher->SetResponseString(data); |
+ return fetcher; |
+ } |
+ |
+ TestURLFetcher* PrepareDMRegisterFetcher() { |
+ em::DeviceManagementResponse response; |
+ em::DeviceRegisterResponse* register_response = |
+ response.mutable_register_response(); |
+ register_response->set_device_management_token(kDMToken); |
+ return PrepareDMServiceFetcher(kDMRegisterRequest, response); |
+ } |
+ |
+ TestURLFetcher* PrepareDMPolicyFetcher() { |
+ em::DeviceManagementResponse response; |
+ response.mutable_policy_response()->add_response(); |
+ return PrepareDMServiceFetcher(kDMPolicyRequest, response); |
+ } |
+ |
+ protected: |
+ ScopedStubCrosEnabler stub_cros_enabler_; |
+ |
+ MessageLoop loop_; |
+ TestingBrowserProcess* browser_process_; |
+ ScopedTestingLocalState local_state_; |
+ |
+ content::TestBrowserThread ui_thread_; |
+ content::TestBrowserThread file_thread_; |
+ TestIOThread io_thread_; |
+ |
+ MockDBusThreadManager dbus_thread_manager_; |
+ TestURLFetcherFactory test_url_fetcher_factory_; |
+ |
+ policy::BrowserPolicyConnector* connector_; |
+ MockCryptohomeLibrary* cryptohome_; |
+ Profile* prepared_profile_; |
+ |
+ private: |
+ ScopedTempDir scoped_temp_dir_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(LoginUtilsTestBase); |
+}; |
+ |
+class LoginUtilsTest : public LoginUtilsTestBase<testing::Test> { |
+}; |
+ |
+class LoginUtilsBlockingLoginTest |
+ : public LoginUtilsTestBase<testing::TestWithParam<int> > { |
+}; |
+ |
+TEST_F(LoginUtilsTest, NormalLoginDoesntBlock) { |
+ UserManager* user_manager = UserManager::Get(); |
+ EXPECT_FALSE(user_manager->user_is_logged_in()); |
+ EXPECT_FALSE(connector_->IsEnterpriseManaged()); |
+ EXPECT_FALSE(prepared_profile_); |
+ |
+ // The profile will be created without waiting for a policy response. |
+ PrepareProfile(kUsername); |
+ |
+ EXPECT_TRUE(prepared_profile_); |
+ EXPECT_TRUE(user_manager->user_is_logged_in()); |
+ EXPECT_EQ(kUsername, user_manager->logged_in_user().email()); |
+} |
+ |
+TEST_F(LoginUtilsTest, EnterpriseLoginDoesntBlockForNormalUser) { |
+ UserManager* user_manager = UserManager::Get(); |
+ EXPECT_FALSE(user_manager->user_is_logged_in()); |
+ EXPECT_FALSE(connector_->IsEnterpriseManaged()); |
+ EXPECT_FALSE(prepared_profile_); |
+ |
+ // Enroll the device. |
+ LockDevice(kUsername); |
+ |
+ EXPECT_FALSE(user_manager->user_is_logged_in()); |
+ EXPECT_TRUE(connector_->IsEnterpriseManaged()); |
+ EXPECT_EQ(kDomain, connector_->GetEnterpriseDomain()); |
+ EXPECT_FALSE(prepared_profile_); |
+ |
+ // Login with a non-enterprise user shouldn't block. |
+ PrepareProfile(kUsernameOtherDomain); |
+ |
+ EXPECT_TRUE(prepared_profile_); |
+ EXPECT_TRUE(user_manager->user_is_logged_in()); |
+ EXPECT_EQ(kUsernameOtherDomain, user_manager->logged_in_user().email()); |
+} |
+ |
+TEST_P(LoginUtilsBlockingLoginTest, EnterpriseLoginBlocksForEnterpriseUser) { |
+ UserManager* user_manager = UserManager::Get(); |
+ EXPECT_FALSE(user_manager->user_is_logged_in()); |
+ EXPECT_FALSE(connector_->IsEnterpriseManaged()); |
+ EXPECT_FALSE(prepared_profile_); |
+ |
+ // Enroll the device. |
+ LockDevice(kUsername); |
+ |
+ EXPECT_FALSE(user_manager->user_is_logged_in()); |
+ EXPECT_TRUE(connector_->IsEnterpriseManaged()); |
+ EXPECT_EQ(kDomain, connector_->GetEnterpriseDomain()); |
+ EXPECT_FALSE(prepared_profile_); |
+ |
+ // Login with a user of the enterprise domain waits for policy. |
+ PrepareProfile(kUsername); |
+ |
+ EXPECT_FALSE(prepared_profile_); |
+ EXPECT_TRUE(user_manager->user_is_logged_in()); |
+ |
+ GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); |
+ TestURLFetcher* fetcher; |
+ |
+ // |steps| is the test parameter, and is the number of successful fetches. |
+ // The first incomplete fetch will fail. In any case, the profile creation |
+ // should resume. |
+ int steps = GetParam(); |
+ |
+ do { |
+ if (steps < 1) break; |
+ |
+ // Fake OAuth token retrieval: |
+ fetcher = PrepareOAuthFetcher(gaia_urls->get_oauth_token_url()); |
+ net::ResponseCookies cookies; |
+ cookies.push_back(kOAuthTokenCookie); |
+ fetcher->set_cookies(cookies); |
+ fetcher->delegate()->OnURLFetchComplete(fetcher); |
+ if (steps < 2) break; |
+ |
+ // Fake OAuth access token retrieval: |
+ fetcher = PrepareOAuthFetcher(gaia_urls->oauth_get_access_token_url()); |
+ fetcher->SetResponseString(kOAuthGetAccessTokenData); |
+ fetcher->delegate()->OnURLFetchComplete(fetcher); |
+ if (steps < 3) break; |
+ |
+ // Fake OAuth service token retrieval: |
+ fetcher = PrepareOAuthFetcher(gaia_urls->oauth_wrap_bridge_url()); |
+ fetcher->SetResponseString(kOAuthServiceTokenData); |
+ fetcher->delegate()->OnURLFetchComplete(fetcher); |
+ |
+ // The cloud policy subsystem is now ready to fetch the dmtoken and the user |
+ // policy. |
+ loop_.RunAllPending(); |
+ if (steps < 4) break; |
+ |
+ fetcher = PrepareDMRegisterFetcher(); |
+ fetcher->delegate()->OnURLFetchComplete(fetcher); |
+ // The policy fetch job has now been scheduled, run it: |
+ loop_.RunAllPending(); |
+ if (steps < 5) break; |
+ |
+ // Verify that there is no profile prepared just before the policy fetch. |
+ EXPECT_FALSE(prepared_profile_); |
+ |
+ fetcher = PrepareDMPolicyFetcher(); |
+ fetcher->delegate()->OnURLFetchComplete(fetcher); |
+ } while (0); |
+ |
+ if (steps < 5) { |
+ // Verify that the profile hasn't been created yet. |
+ EXPECT_FALSE(prepared_profile_); |
+ |
+ // Make the current fetcher fail. |
+ TestURLFetcher* fetcher = test_url_fetcher_factory_.GetFetcherByID(0); |
+ EXPECT_TRUE(fetcher); |
+ EXPECT_TRUE(fetcher->delegate()); |
+ fetcher->set_url(fetcher->GetOriginalURL()); |
+ fetcher->set_response_code(500); |
+ fetcher->delegate()->OnURLFetchComplete(fetcher); |
+ } |
+ |
+ // The profile is finally ready: |
+ EXPECT_TRUE(prepared_profile_); |
+} |
+ |
+INSTANTIATE_TEST_CASE_P( |
+ LoginUtilsBlockingLoginTestInstance, |
+ LoginUtilsBlockingLoginTest, |
+ testing::Values(0, 1, 2, 3, 4, 5)); |
+ |
+} // namespace |
+ |
+} |