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