blocking_login_browsertest.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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
40namespace chromeos {
41
42namespace {
43
44namespace em = enterprise_management;
45
46const char kDomain[] = "domain.com";
47const char kUsername[] = "user@domain.com";
48const char kUsernameOtherDomain[] = "user@other.com";
49
50const char kOAuthCodeCookie[] = "oauth_code=1234; Secure; HttpOnly";
51
52const char kOAuth2TokenPairData[] =
53    "{"
54    "  \"refresh_token\": \"1234\","
55    "  \"access_token\": \"5678\","
56    "  \"expires_in\": 3600"
57    "}";
58
59const char kOAuth2AccessTokenData[] =
60    "{"
61    "  \"access_token\": \"5678\","
62    "  \"expires_in\": 3600"
63    "}";
64
65const char kDMRegisterRequest[] = "/device_management?request=register";
66const char kDMPolicyRequest[] = "/device_management?request=policy";
67
68void 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
77struct BlockingLoginTestParam {
78  const int steps;
79  const char* username;
80  const bool enroll_device;
81};
82
83class 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
258IN_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
331const 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
352INSTANTIATE_TEST_CASE_P(BlockingLoginTestInstance,
353                        BlockingLoginTest,
354                        testing::ValuesIn(kBlockinLoginTestCases));
355
356}  // namespace chromeos
357