device_local_account_browsertest.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1// Copyright (c) 2013 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 <map> 6#include <string> 7 8#include "base/basictypes.h" 9#include "base/bind.h" 10#include "base/callback.h" 11#include "base/command_line.h" 12#include "base/file_util.h" 13#include "base/files/file_path.h" 14#include "base/files/scoped_temp_dir.h" 15#include "base/message_loop.h" 16#include "base/path_service.h" 17#include "base/run_loop.h" 18#include "base/stl_util.h" 19#include "base/string_util.h" 20#include "base/utf_string_conversions.h" 21#include "chrome/browser/browser_process.h" 22#include "chrome/browser/chromeos/login/existing_user_controller.h" 23#include "chrome/browser/chromeos/login/login_display_host.h" 24#include "chrome/browser/chromeos/login/login_display_host_impl.h" 25#include "chrome/browser/chromeos/login/user.h" 26#include "chrome/browser/chromeos/login/user_manager.h" 27#include "chrome/browser/chromeos/policy/device_local_account.h" 28#include "chrome/browser/chromeos/policy/device_policy_builder.h" 29#include "chrome/browser/chromeos/policy/enterprise_install_attributes.h" 30#include "chrome/browser/lifetime/application_lifetime.h" 31#include "chrome/browser/policy/cloud/cloud_policy_constants.h" 32#include "chrome/browser/policy/cloud/policy_builder.h" 33#include "chrome/browser/policy/policy_service.h" 34#include "chrome/browser/policy/proto/chromeos/chrome_device_policy.pb.h" 35#include "chrome/browser/policy/proto/chromeos/install_attributes.pb.h" 36#include "chrome/browser/policy/test/local_policy_test_server.h" 37#include "chrome/browser/prefs/session_startup_pref.h" 38#include "chrome/browser/ui/browser.h" 39#include "chrome/browser/ui/browser_finder.h" 40#include "chrome/browser/ui/host_desktop.h" 41#include "chrome/browser/ui/tabs/tab_strip_model.h" 42#include "chrome/common/chrome_notification_types.h" 43#include "chrome/common/chrome_switches.h" 44#include "chrome/test/base/in_process_browser_test.h" 45#include "chromeos/chromeos_paths.h" 46#include "chromeos/chromeos_switches.h" 47#include "chromeos/dbus/cryptohome_client.h" 48#include "chromeos/dbus/dbus_method_call_status.h" 49#include "chromeos/dbus/dbus_thread_manager.h" 50#include "chromeos/dbus/fake_cryptohome_client.h" 51#include "chromeos/dbus/fake_session_manager_client.h" 52#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" 53#include "chromeos/dbus/session_manager_client.h" 54#include "content/public/browser/notification_observer.h" 55#include "content/public/browser/notification_registrar.h" 56#include "content/public/browser/notification_service.h" 57#include "content/public/browser/web_contents.h" 58#include "testing/gmock/include/gmock/gmock.h" 59#include "third_party/cros_system_api/dbus/service_constants.h" 60 61namespace em = enterprise_management; 62 63using testing::Return; 64 65namespace policy { 66 67namespace { 68 69const char kAccountId1[] = "dla1@example.com"; 70const char kAccountId2[] = "dla2@example.com"; 71const char kDisplayName1[] = "display name for account 1"; 72const char kDisplayName2[] = "display name for account 2"; 73const char* kStartupURLs[] = { 74 "chrome://policy", 75 "chrome://about", 76}; 77 78// Observes a specific notification type and quits the message loop once a 79// condition holds. 80class NotificationWatcher : public content::NotificationObserver { 81 public: 82 // Callback invoked on notifications. Should return true when the condition 83 // that the caller is waiting for is satisfied. 84 typedef base::Callback<bool(void)> ConditionTestCallback; 85 86 explicit NotificationWatcher(int notification_type, 87 const ConditionTestCallback& callback) 88 : type_(notification_type), 89 callback_(callback) {} 90 91 void Run() { 92 if (callback_.Run()) 93 return; 94 95 content::NotificationRegistrar registrar; 96 registrar.Add(this, type_, content::NotificationService::AllSources()); 97 run_loop_.Run(); 98 } 99 100 // content::NotificationObserver: 101 virtual void Observe(int type, 102 const content::NotificationSource& source, 103 const content::NotificationDetails& details) OVERRIDE { 104 if (callback_.Run()) 105 run_loop_.Quit(); 106 } 107 108 private: 109 int type_; 110 ConditionTestCallback callback_; 111 base::RunLoop run_loop_; 112 113 DISALLOW_COPY_AND_ASSIGN(NotificationWatcher); 114}; 115 116} // namespace 117 118class DeviceLocalAccountTest : public InProcessBrowserTest { 119 protected: 120 DeviceLocalAccountTest() 121 : user_id_1_(GenerateDeviceLocalAccountUserId( 122 kAccountId1, DeviceLocalAccount::TYPE_PUBLIC_SESSION)), 123 user_id_2_(GenerateDeviceLocalAccountUserId( 124 kAccountId2, DeviceLocalAccount::TYPE_PUBLIC_SESSION)) {} 125 126 virtual ~DeviceLocalAccountTest() {} 127 128 virtual void SetUp() OVERRIDE { 129 // Configure and start the test server. 130 scoped_ptr<crypto::RSAPrivateKey> signing_key( 131 PolicyBuilder::CreateTestSigningKey()); 132 ASSERT_TRUE(test_server_.SetSigningKey(signing_key.get())); 133 signing_key.reset(); 134 test_server_.RegisterClient(PolicyBuilder::kFakeToken, 135 PolicyBuilder::kFakeDeviceId); 136 ASSERT_TRUE(test_server_.Start()); 137 138 InProcessBrowserTest::SetUp(); 139 } 140 141 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 142 command_line->AppendSwitch(chromeos::switches::kLoginManager); 143 command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests); 144 command_line->AppendSwitchASCII( 145 switches::kDeviceManagementUrl, test_server_.GetServiceURL().spec()); 146 command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "user"); 147 } 148 149 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { 150 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 151 152 // Clear command-line arguments (but keep command-line switches) so the 153 // startup pages policy takes effect. 154 CommandLine* command_line = CommandLine::ForCurrentProcess(); 155 CommandLine::StringVector argv(command_line->argv()); 156 argv.erase(argv.begin() + argv.size() - command_line->GetArgs().size(), 157 argv.end()); 158 command_line->InitFromArgv(argv); 159 160 // Mark the device enterprise-enrolled. 161 SetUpInstallAttributes(); 162 163 // Redirect session_manager DBus calls to FakeSessionManagerClient. 164 chromeos::MockDBusThreadManagerWithoutGMock* dbus_thread_manager = 165 new chromeos::MockDBusThreadManagerWithoutGMock(); 166 session_manager_client_ = 167 dbus_thread_manager->fake_session_manager_client(); 168 chromeos::DBusThreadManager::InitializeForTesting(dbus_thread_manager); 169 170 SetUpPolicy(); 171 } 172 173 virtual void CleanUpOnMainThread() OVERRIDE { 174 // This shuts down the login UI. 175 base::MessageLoop::current()->PostTask(FROM_HERE, 176 base::Bind(&chrome::AttemptExit)); 177 base::RunLoop().RunUntilIdle(); 178 } 179 180 void SetUpInstallAttributes() { 181 cryptohome::SerializedInstallAttributes install_attrs_proto; 182 cryptohome::SerializedInstallAttributes::Attribute* attribute = NULL; 183 184 attribute = install_attrs_proto.add_attributes(); 185 attribute->set_name(EnterpriseInstallAttributes::kAttrEnterpriseOwned); 186 attribute->set_value("true"); 187 188 attribute = install_attrs_proto.add_attributes(); 189 attribute->set_name(EnterpriseInstallAttributes::kAttrEnterpriseUser); 190 attribute->set_value(PolicyBuilder::kFakeUsername); 191 192 base::FilePath install_attrs_file = 193 temp_dir_.path().AppendASCII("install_attributes.pb"); 194 const std::string install_attrs_blob( 195 install_attrs_proto.SerializeAsString()); 196 ASSERT_EQ(static_cast<int>(install_attrs_blob.size()), 197 file_util::WriteFile(install_attrs_file, 198 install_attrs_blob.c_str(), 199 install_attrs_blob.size())); 200 ASSERT_TRUE(PathService::Override(chromeos::FILE_INSTALL_ATTRIBUTES, 201 install_attrs_file)); 202 } 203 204 void SetUpPolicy() { 205 // Configure two device-local accounts in device settings. 206 DevicePolicyBuilder device_policy; 207 device_policy.policy_data().set_public_key_version(1); 208 em::ChromeDeviceSettingsProto& proto(device_policy.payload()); 209 proto.mutable_show_user_names()->set_show_user_names(true); 210 em::DeviceLocalAccountInfoProto* account1 = 211 proto.mutable_device_local_accounts()->add_account(); 212 account1->set_account_id(kAccountId1); 213 account1->set_type( 214 em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION); 215 em::DeviceLocalAccountInfoProto* account2 = 216 proto.mutable_device_local_accounts()->add_account(); 217 account2->set_account_id(kAccountId2); 218 account2->set_type( 219 em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION); 220 device_policy.Build(); 221 session_manager_client_->set_device_policy(device_policy.GetBlob()); 222 test_server_.UpdatePolicy(dm_protocol::kChromeDevicePolicyType, 223 std::string(), proto.SerializeAsString()); 224 225 // Install the owner key. 226 base::FilePath owner_key_file = temp_dir_.path().AppendASCII("owner.key"); 227 std::vector<uint8> owner_key_bits; 228 ASSERT_TRUE(device_policy.signing_key()->ExportPublicKey(&owner_key_bits)); 229 ASSERT_EQ( 230 static_cast<int>(owner_key_bits.size()), 231 file_util::WriteFile( 232 owner_key_file, 233 reinterpret_cast<const char*>(vector_as_array(&owner_key_bits)), 234 owner_key_bits.size())); 235 ASSERT_TRUE( 236 PathService::Override(chromeos::FILE_OWNER_KEY, owner_key_file)); 237 238 // Configure device-local account policy for the first device-local account. 239 UserPolicyBuilder device_local_account_policy; 240 device_local_account_policy.policy_data().set_policy_type( 241 dm_protocol::kChromePublicAccountPolicyType); 242 device_local_account_policy.policy_data().set_username(kAccountId1); 243 device_local_account_policy.policy_data().set_settings_entity_id( 244 kAccountId1); 245 device_local_account_policy.policy_data().set_public_key_version(1); 246 device_local_account_policy.payload().mutable_restoreonstartup()->set_value( 247 SessionStartupPref::kPrefValueURLs); 248 em::StringListPolicyProto* startup_urls_proto = 249 device_local_account_policy.payload().mutable_restoreonstartupurls(); 250 for (size_t i = 0; i < arraysize(kStartupURLs); ++i) 251 startup_urls_proto->mutable_value()->add_entries(kStartupURLs[i]); 252 device_local_account_policy.payload().mutable_userdisplayname()->set_value( 253 kDisplayName1); 254 device_local_account_policy.Build(); 255 session_manager_client_->set_device_local_account_policy( 256 kAccountId1, device_local_account_policy.GetBlob()); 257 test_server_.UpdatePolicy( 258 dm_protocol::kChromePublicAccountPolicyType, kAccountId1, 259 device_local_account_policy.payload().SerializeAsString()); 260 261 // Make policy for the second account available from the server. 262 device_local_account_policy.payload().mutable_userdisplayname()->set_value( 263 kDisplayName2); 264 test_server_.UpdatePolicy( 265 dm_protocol::kChromePublicAccountPolicyType, kAccountId2, 266 device_local_account_policy.payload().SerializeAsString()); 267 268 // Don't install policy for |kAccountId2| yet so initial download gets 269 // test coverage. 270 ASSERT_TRUE(session_manager_client_->device_local_account_policy( 271 kAccountId2).empty()); 272 } 273 274 void CheckPublicSessionPresent(const std::string& id) { 275 const chromeos::User* user = chromeos::UserManager::Get()->FindUser(id); 276 ASSERT_TRUE(user); 277 EXPECT_EQ(id, user->email()); 278 EXPECT_EQ(chromeos::User::USER_TYPE_PUBLIC_ACCOUNT, user->GetType()); 279 } 280 281 const std::string user_id_1_; 282 const std::string user_id_2_; 283 284 LocalPolicyTestServer test_server_; 285 base::ScopedTempDir temp_dir_; 286 287 chromeos::FakeSessionManagerClient* session_manager_client_; 288}; 289 290static bool IsKnownUser(const std::string& account_id) { 291 return chromeos::UserManager::Get()->IsKnownUser(account_id); 292} 293 294IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, LoginScreen) { 295 NotificationWatcher(chrome::NOTIFICATION_USER_LIST_CHANGED, 296 base::Bind(&IsKnownUser, user_id_1_)).Run(); 297 NotificationWatcher(chrome::NOTIFICATION_USER_LIST_CHANGED, 298 base::Bind(&IsKnownUser, user_id_2_)).Run(); 299 300 CheckPublicSessionPresent(user_id_1_); 301 CheckPublicSessionPresent(user_id_2_); 302} 303 304static bool DisplayNameMatches(const std::string& account_id, 305 const std::string& display_name) { 306 const chromeos::User* user = 307 chromeos::UserManager::Get()->FindUser(account_id); 308 if (!user || user->display_name().empty()) 309 return false; 310 EXPECT_EQ(UTF8ToUTF16(display_name), user->display_name()); 311 return true; 312} 313 314IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, DisplayName) { 315 NotificationWatcher( 316 chrome::NOTIFICATION_USER_LIST_CHANGED, 317 base::Bind(&DisplayNameMatches, user_id_1_, kDisplayName1)).Run(); 318} 319 320IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, PolicyDownload) { 321 // Policy for kAccountId2 is not installed in session_manager_client, make 322 // sure it gets fetched from the server. Note that the test setup doesn't set 323 // up policy for kAccountId2, so the presence of the display name can be used 324 // as signal to indicate successful policy download. 325 NotificationWatcher( 326 chrome::NOTIFICATION_USER_LIST_CHANGED, 327 base::Bind(&DisplayNameMatches, user_id_2_, kDisplayName2)).Run(); 328 329 // Sanity check: The policy should be present now. 330 ASSERT_FALSE(session_manager_client_->device_local_account_policy( 331 kAccountId2).empty()); 332} 333 334static bool IsNotKnownUser(const std::string& account_id) { 335 return !IsKnownUser(account_id); 336} 337 338IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, DevicePolicyChange) { 339 // Wait until the login screen is up. 340 NotificationWatcher(chrome::NOTIFICATION_USER_LIST_CHANGED, 341 base::Bind(&IsKnownUser, user_id_1_)).Run(); 342 NotificationWatcher(chrome::NOTIFICATION_USER_LIST_CHANGED, 343 base::Bind(&IsKnownUser, user_id_2_)).Run(); 344 345 // Update policy to remove kAccountId2. 346 em::ChromeDeviceSettingsProto policy; 347 policy.mutable_show_user_names()->set_show_user_names(true); 348 em::DeviceLocalAccountInfoProto* account1 = 349 policy.mutable_device_local_accounts()->add_account(); 350 account1->set_account_id(kAccountId1); 351 account1->set_type( 352 em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION); 353 354 test_server_.UpdatePolicy(dm_protocol::kChromeDevicePolicyType, std::string(), 355 policy.SerializeAsString()); 356 g_browser_process->policy_service()->RefreshPolicies(base::Closure()); 357 358 // Make sure the second device-local account disappears. 359 NotificationWatcher(chrome::NOTIFICATION_USER_LIST_CHANGED, 360 base::Bind(&IsNotKnownUser, user_id_2_)).Run(); 361} 362 363static bool IsSessionStarted() { 364 return chromeos::UserManager::Get()->IsSessionStarted(); 365} 366 367IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, StartSession) { 368 // This observes the display name becoming available as this indicates 369 // device-local account policy is fully loaded, which is a prerequisite for 370 // successful login. 371 NotificationWatcher( 372 chrome::NOTIFICATION_USER_LIST_CHANGED, 373 base::Bind(&DisplayNameMatches, user_id_1_, kDisplayName1)).Run(); 374 375 chromeos::LoginDisplayHost* host = 376 chromeos::LoginDisplayHostImpl::default_host(); 377 ASSERT_TRUE(host); 378 host->StartSignInScreen(); 379 chromeos::ExistingUserController* controller = 380 chromeos::ExistingUserController::current_controller(); 381 ASSERT_TRUE(controller); 382 controller->LoginAsPublicAccount(user_id_1_); 383 384 // Wait for the session to start. 385 NotificationWatcher(chrome::NOTIFICATION_SESSION_STARTED, 386 base::Bind(IsSessionStarted)).Run(); 387 388 // Check that the startup pages specified in policy were opened. 389 EXPECT_EQ(1U, chrome::GetTotalBrowserCount()); 390 Browser* browser = 391 chrome::FindLastActiveWithHostDesktopType(chrome::HOST_DESKTOP_TYPE_ASH); 392 ASSERT_TRUE(browser); 393 394 TabStripModel* tabs = browser->tab_strip_model(); 395 ASSERT_TRUE(tabs); 396 int expected_tab_count = static_cast<int>(arraysize(kStartupURLs)); 397 EXPECT_EQ(expected_tab_count, tabs->count()); 398 for (int i = 0; i < expected_tab_count && i < tabs->count(); ++i) 399 EXPECT_EQ(GURL(kStartupURLs[i]), tabs->GetWebContentsAt(i)->GetURL()); 400} 401 402} // namespace policy 403