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