enrollment_handler_chromeos.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
151a8d8528135ba4e3e4cf7cd711a9e47b19078a3Chris Lattner// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2ea61c358720aa6c7a159d51658b34276316aa841Misha Brukman// Use of this source code is governed by a BSD-style license that can be
36fbcc26f1460eaee4e0eb8b426fc1ff0c7af11beJohn Criswell// found in the LICENSE file.
46fbcc26f1460eaee4e0eb8b426fc1ff0c7af11beJohn Criswell
57ed47a13356daed2a34cd2209a31f92552e3bdd8Chris Lattner#include "chrome/browser/chromeos/policy/enrollment_handler_chromeos.h"
67ed47a13356daed2a34cd2209a31f92552e3bdd8Chris Lattner
7ea61c358720aa6c7a159d51658b34276316aa841Misha Brukman#include "base/bind.h"
86fbcc26f1460eaee4e0eb8b426fc1ff0c7af11beJohn Criswell#include "base/command_line.h"
9ea61c358720aa6c7a159d51658b34276316aa841Misha Brukman#include "base/logging.h"
10e8b5413e5d0c7c0fc5b384e975c4ca87f4c00699Chris Lattner#include "base/message_loop/message_loop.h"
11e8b5413e5d0c7c0fc5b384e975c4ca87f4c00699Chris Lattner#include "chrome/browser/browser_process.h"
1251a8d8528135ba4e3e4cf7cd711a9e47b19078a3Chris Lattner#include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h"
13deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve#include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
14fce1143bcfa73f61845002fa50473d1a01384202Misha Brukman#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
15fce1143bcfa73f61845002fa50473d1a01384202Misha Brukman#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
16deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve#include "chromeos/chromeos_switches.h"
17c0b9dc5be79f009d260edb5cd5e1d8346587aaa2Alkis Evlogimenos#include "components/policy/core/common/cloud/cloud_policy_constants.h"
18551ccae044b0ff658fe629dd67edd5ffe75d10e8Reid Spencer#include "google_apis/gaia/gaia_urls.h"
19d0fde30ce850b78371fd1386338350591f9ff494Brian Gaeke#include "net/http/http_status_code.h"
20d0fde30ce850b78371fd1386338350591f9ff494Brian Gaeke#include "policy/proto/device_management_backend.pb.h"
21fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman
22853d3fb8d24fab2258e9cd5dce3ec8ff4189eedaDan Gohmannamespace em = enterprise_management;
23fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman
24fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohmannamespace policy {
25a655f088b88cf015fc48721fd9869787d1b8ce13Chris Lattner
26a655f088b88cf015fc48721fd9869787d1b8ce13Chris Lattnernamespace {
271cd1d98232c3c3a0bd3810c3bf6c2572ea02f208Daniel Dunbar
28d0fde30ce850b78371fd1386338350591f9ff494Brian Gaeke// Retry for InstallAttrs initialization every 500ms.
2994dc07728f091c652f0a8059aba6dce5018485eeAlkis Evlogimenosconst int kLockRetryIntervalMs = 500;
308e4018e2de52c534405d7155c7009d0b35afb861Cedric Venet// Maximum time to retry InstallAttrs initialization before we give up.
318e4018e2de52c534405d7155c7009d0b35afb861Cedric Venetconst int kLockRetryTimeoutMs = 10 * 60 * 1000;  // 10 minutes.
327309be6735666143bd9835b275dc8501617a2591Gabor Greif
33fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman// Testing token used when the enrollment-skip-robot-auth is set to skip talking
348e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman// to GAIA for an actual token. This is needed to be able to run against the
3594dc07728f091c652f0a8059aba6dce5018485eeAlkis Evlogimenos// testing DMServer implementations.
368e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohmanconst char kTestingRobotToken[] = "test-token";
3794dc07728f091c652f0a8059aba6dce5018485eeAlkis Evlogimenos
388e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman}  // namespace
39c7f6b8c5d40e17bf43fd3a1549d7d89c9da735e1Gabor Greif
40c7f6b8c5d40e17bf43fd3a1549d7d89c9da735e1Gabor GreifEnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS(
41c7f6b8c5d40e17bf43fd3a1549d7d89c9da735e1Gabor Greif    DeviceCloudPolicyStoreChromeOS* store,
42fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman    EnterpriseInstallAttributes* install_attributes,
4394dc07728f091c652f0a8059aba6dce5018485eeAlkis Evlogimenos    scoped_ptr<CloudPolicyClient> client,
44c23b8719ef9d6b1220e854b37d40e9e1c48a82bcGabor Greif    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
45c23b8719ef9d6b1220e854b37d40e9e1c48a82bcGabor Greif    const std::string& auth_token,
46f3841fcbd587c31aa9842b3f33bd57de40c9f443Gabor Greif    const std::string& client_id,
47c23b8719ef9d6b1220e854b37d40e9e1c48a82bcGabor Greif    bool is_auto_enrollment,
48aad5c0505183a5b7913f1a443a1f0650122551ccAlkis Evlogimenos    const std::string& requisition,
49aad5c0505183a5b7913f1a443a1f0650122551ccAlkis Evlogimenos    const AllowedDeviceModes& allowed_device_modes,
50fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman    const EnrollmentCallback& completion_callback)
51fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman    : store_(store),
52fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman      install_attributes_(install_attributes),
538e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman      client_(client.Pass()),
54fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman      background_task_runner_(background_task_runner),
55fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman      auth_token_(auth_token),
5694dc07728f091c652f0a8059aba6dce5018485eeAlkis Evlogimenos      client_id_(client_id),
5794dc07728f091c652f0a8059aba6dce5018485eeAlkis Evlogimenos      is_auto_enrollment_(is_auto_enrollment),
58fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman      requisition_(requisition),
59fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman      allowed_device_modes_(allowed_device_modes),
60c0b9dc5be79f009d260edb5cd5e1d8346587aaa2Alkis Evlogimenos      completion_callback_(completion_callback),
611194e9501984daf0d3237ed1bf18a156173e7fd4Chris Lattner      device_mode_(DEVICE_MODE_NOT_SET),
62c07d8d8a26f63dfc54dbd0e1ff776763ec6443adBrian Gaeke      enrollment_step_(STEP_PENDING),
63f20c1a497fe3922ac718429d65a5fe396890575eChris Lattner      lockbox_init_duration_(0),
64f20c1a497fe3922ac718429d65a5fe396890575eChris Lattner      weak_ptr_factory_(this) {
6513d828567812041c1ca1817f4b66fce840903a1fEvan Cheng  CHECK(!client_->is_registered());
6613d828567812041c1ca1817f4b66fce840903a1fEvan Cheng  CHECK_EQ(DM_STATUS_SUCCESS, client_->status());
6713d828567812041c1ca1817f4b66fce840903a1fEvan Cheng  store_->AddObserver(this);
6813d828567812041c1ca1817f4b66fce840903a1fEvan Cheng  client_->AddObserver(this);
6913d828567812041c1ca1817f4b66fce840903a1fEvan Cheng  client_->AddNamespaceToFetch(PolicyNamespaceKey(
7013d828567812041c1ca1817f4b66fce840903a1fEvan Cheng      dm_protocol::kChromeDevicePolicyType, std::string()));
7113d828567812041c1ca1817f4b66fce840903a1fEvan Cheng}
7213d828567812041c1ca1817f4b66fce840903a1fEvan Cheng
73fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan ChengEnrollmentHandlerChromeOS::~EnrollmentHandlerChromeOS() {
74fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan Cheng  Stop();
75fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan Cheng  store_->RemoveObserver(this);
76fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan Cheng}
7730b8e51addc23fb317c03d093a25828d3d5be45aJim Laskey
7830b8e51addc23fb317c03d093a25828d3d5be45aJim Laskeyvoid EnrollmentHandlerChromeOS::StartEnrollment() {
7930b8e51addc23fb317c03d093a25828d3d5be45aJim Laskey  CHECK_EQ(STEP_PENDING, enrollment_step_);
8030b8e51addc23fb317c03d093a25828d3d5be45aJim Laskey  enrollment_step_ = STEP_LOADING_STORE;
8113d828567812041c1ca1817f4b66fce840903a1fEvan Cheng  AttemptRegistration();
828c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohman}
838c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohman
848c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohmanscoped_ptr<CloudPolicyClient> EnrollmentHandlerChromeOS::ReleaseClient() {
858c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohman  Stop();
86fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman  return client_.Pass();
87fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman}
88fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman
898e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohmanvoid EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient* client) {
908e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman  DCHECK_EQ(client_.get(), client);
912c3f7ae3843bdc9dcfe85393e178211976c1f9bdDan Gohman  CHECK_EQ(STEP_POLICY_FETCH, enrollment_step_);
9217fb34bf8cd10a798c9206eeef3bff151b4d3688Tanya Lattner
938e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman  enrollment_step_ = STEP_VALIDATION;
948e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman
95ea61c358720aa6c7a159d51658b34276316aa841Misha Brukman  // Validate the policy.
968e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman  const em::PolicyFetchResponse* policy = client_->GetPolicyFor(
97d0aa0cdbc6fee00f2b2019633a9b9d00d301ac68Chris Lattner      PolicyNamespaceKey(dm_protocol::kChromeDevicePolicyType, std::string()));
98324da7647cfc3025e0c987176f0a300f9f780e6fJakob Stoklund Olesen  if (!policy) {
99324da7647cfc3025e0c987176f0a300f9f780e6fJakob Stoklund Olesen    ReportResult(EnrollmentStatus::ForFetchError(
100d0aa0cdbc6fee00f2b2019633a9b9d00d301ac68Chris Lattner        DM_STATUS_RESPONSE_DECODING_ERROR));
1011194e9501984daf0d3237ed1bf18a156173e7fd4Chris Lattner    return;
1025e61fa95196b85281eec655787e9c73267532bd1Chris Lattner  }
103324da7647cfc3025e0c987176f0a300f9f780e6fJakob Stoklund Olesen
104324da7647cfc3025e0c987176f0a300f9f780e6fJakob Stoklund Olesen  scoped_ptr<DeviceCloudPolicyValidator> validator(
105324da7647cfc3025e0c987176f0a300f9f780e6fJakob Stoklund Olesen      DeviceCloudPolicyValidator::Create(
106324da7647cfc3025e0c987176f0a300f9f780e6fJakob Stoklund Olesen          scoped_ptr<em::PolicyFetchResponse>(
1078c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohman              new em::PolicyFetchResponse(*policy)),
1088c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohman          background_task_runner_));
1098c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohman
1108c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohman  validator->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(),
1118c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohman                               CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
1128c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohman
1138c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohman  // If this is re-enrollment, make sure that the new policy matches the
1148c2b52552c90f39e4b2fed43e309e599e742b6acDan Gohman  // previously-enrolled domain.
1155e61fa95196b85281eec655787e9c73267532bd1Chris Lattner  std::string domain;
1165e61fa95196b85281eec655787e9c73267532bd1Chris Lattner  if (install_attributes_->IsEnterpriseDevice()) {
117f20c1a497fe3922ac718429d65a5fe396890575eChris Lattner    domain = install_attributes_->GetDomain();
118f20c1a497fe3922ac718429d65a5fe396890575eChris Lattner    validator->ValidateDomain(domain);
1195e61fa95196b85281eec655787e9c73267532bd1Chris Lattner  }
1208e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman  validator->ValidateDMToken(client->dm_token(),
1218e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman                             CloudPolicyValidatorBase::DM_TOKEN_REQUIRED);
122deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve  validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType);
123deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve  validator->ValidatePayload();
124deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve  // If |domain| is empty here, the policy validation code will just use the
12534cd4a484e532cc463fd5a4bf59b88d13c5467c1Evan Cheng  // domain from the username field in the policy itself to do key validation.
126deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve  // TODO(mnissler): Plumb the enrolling user's username into this object so
127deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve  // we can validate the username on the resulting policy, and use the domain
128c0b9dc5be79f009d260edb5cd5e1d8346587aaa2Alkis Evlogimenos  // from that username to validate the key below (http://crbug.com/343074).
129c0b9dc5be79f009d260edb5cd5e1d8346587aaa2Alkis Evlogimenos  validator->ValidateInitialKey(GetPolicyVerificationKey(), domain);
1308e8b8a223c2b0e69f44c0639f846260c8011668fDan Gohman  validator.release()->StartValidation(
1318e8b8a223c2b0e69f44c0639f846260c8011668fDan Gohman      base::Bind(&EnrollmentHandlerChromeOS::PolicyValidated,
132deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve                 weak_ptr_factory_.GetWeakPtr()));
133deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve}
134deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve
135deb9654056939a12981446f6ed1139dca3412746Vikram S. Advevoid EnrollmentHandlerChromeOS::OnRegistrationStateChanged(
136deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve    CloudPolicyClient* client) {
137deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve  DCHECK_EQ(client_.get(), client);
138deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve
139deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve  if (enrollment_step_ == STEP_REGISTRATION && client_->is_registered()) {
140deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve    enrollment_step_ = STEP_POLICY_FETCH,
141deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve    device_mode_ = client_->device_mode();
14276456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke    if (device_mode_ == DEVICE_MODE_NOT_SET)
14376456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke      device_mode_ = DEVICE_MODE_ENTERPRISE;
14476456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke    if (!allowed_device_modes_.test(device_mode_)) {
14576456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke      LOG(ERROR) << "Bad device mode " << device_mode_;
14676456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke      ReportResult(EnrollmentStatus::ForStatus(
1476603d7ec67e64b987451975771759ade4e9f19baEvan Cheng          EnrollmentStatus::STATUS_REGISTRATION_BAD_MODE));
1486603d7ec67e64b987451975771759ade4e9f19baEvan Cheng      return;
1496603d7ec67e64b987451975771759ade4e9f19baEvan Cheng    }
1506603d7ec67e64b987451975771759ade4e9f19baEvan Cheng    client_->FetchPolicy();
1516603d7ec67e64b987451975771759ade4e9f19baEvan Cheng  } else {
1526603d7ec67e64b987451975771759ade4e9f19baEvan Cheng    LOG(FATAL) << "Registration state changed to " << client_->is_registered()
1536603d7ec67e64b987451975771759ade4e9f19baEvan Cheng               << " in step " << enrollment_step_;
1546603d7ec67e64b987451975771759ade4e9f19baEvan Cheng  }
155ea61c358720aa6c7a159d51658b34276316aa841Misha Brukman}
156cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattner
157cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattnervoid EnrollmentHandlerChromeOS::OnClientError(CloudPolicyClient* client) {
158cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattner  DCHECK_EQ(client_.get(), client);
159cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattner
1606603d7ec67e64b987451975771759ade4e9f19baEvan Cheng  if (enrollment_step_ == STEP_ROBOT_AUTH_FETCH) {
1616603d7ec67e64b987451975771759ade4e9f19baEvan Cheng    LOG(ERROR) << "API authentication code fetch failed: "
1626603d7ec67e64b987451975771759ade4e9f19baEvan Cheng               << client_->status();
1636603d7ec67e64b987451975771759ade4e9f19baEvan Cheng    ReportResult(EnrollmentStatus::ForRobotAuthFetchError(client_->status()));
1646603d7ec67e64b987451975771759ade4e9f19baEvan Cheng  } else if (enrollment_step_ < STEP_POLICY_FETCH) {
1656603d7ec67e64b987451975771759ade4e9f19baEvan Cheng    ReportResult(EnrollmentStatus::ForRegistrationError(client_->status()));
1666603d7ec67e64b987451975771759ade4e9f19baEvan Cheng  } else {
1676603d7ec67e64b987451975771759ade4e9f19baEvan Cheng    ReportResult(EnrollmentStatus::ForFetchError(client_->status()));
16834cd4a484e532cc463fd5a4bf59b88d13c5467c1Evan Cheng  }
16934cd4a484e532cc463fd5a4bf59b88d13c5467c1Evan Cheng}
17034cd4a484e532cc463fd5a4bf59b88d13c5467c1Evan Cheng
171cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattnervoid EnrollmentHandlerChromeOS::OnStoreLoaded(CloudPolicyStore* store) {
172cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattner  DCHECK_EQ(store_, store);
173cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattner
174cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattner  if (enrollment_step_ == STEP_LOADING_STORE) {
175cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattner    // If the |store_| wasn't initialized when StartEnrollment() was
1766603d7ec67e64b987451975771759ade4e9f19baEvan Cheng    // called, then AttemptRegistration() bails silently.  This gets
1776603d7ec67e64b987451975771759ade4e9f19baEvan Cheng    // registration rolling again after the store finishes loading.
1786603d7ec67e64b987451975771759ade4e9f19baEvan Cheng    AttemptRegistration();
1796603d7ec67e64b987451975771759ade4e9f19baEvan Cheng  } else if (enrollment_step_ == STEP_STORE_POLICY) {
1806603d7ec67e64b987451975771759ade4e9f19baEvan Cheng    ReportResult(EnrollmentStatus::ForStatus(EnrollmentStatus::STATUS_SUCCESS));
1816603d7ec67e64b987451975771759ade4e9f19baEvan Cheng  }
1826603d7ec67e64b987451975771759ade4e9f19baEvan Cheng}
1836603d7ec67e64b987451975771759ade4e9f19baEvan Cheng
18434cd4a484e532cc463fd5a4bf59b88d13c5467c1Evan Chengvoid EnrollmentHandlerChromeOS::OnStoreError(CloudPolicyStore* store) {
18534cd4a484e532cc463fd5a4bf59b88d13c5467c1Evan Cheng  DCHECK_EQ(store_, store);
18634cd4a484e532cc463fd5a4bf59b88d13c5467c1Evan Cheng  ReportResult(EnrollmentStatus::ForStoreError(store_->status(),
187cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattner                                               store_->validation_status()));
18876456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke}
18913d828567812041c1ca1817f4b66fce840903a1fEvan Cheng
19013d828567812041c1ca1817f4b66fce840903a1fEvan Chengvoid EnrollmentHandlerChromeOS::AttemptRegistration() {
19113d828567812041c1ca1817f4b66fce840903a1fEvan Cheng  CHECK_EQ(STEP_LOADING_STORE, enrollment_step_);
19213d828567812041c1ca1817f4b66fce840903a1fEvan Cheng  if (store_->is_initialized()) {
19313d828567812041c1ca1817f4b66fce840903a1fEvan Cheng    enrollment_step_ = STEP_REGISTRATION;
19413d828567812041c1ca1817f4b66fce840903a1fEvan Cheng    client_->Register(em::DeviceRegisterRequest::DEVICE,
195b371f457b0ea4a652a9f526ba4375c80ae542252Evan Cheng                      auth_token_, client_id_, is_auto_enrollment_,
196b371f457b0ea4a652a9f526ba4375c80ae542252Evan Cheng                      requisition_);
197b371f457b0ea4a652a9f526ba4375c80ae542252Evan Cheng  }
198b371f457b0ea4a652a9f526ba4375c80ae542252Evan Cheng}
199a971dbdde27fd4ff53dbebdd4aaf87826d081aa2Evan Cheng
200a971dbdde27fd4ff53dbebdd4aaf87826d081aa2Evan Chengvoid EnrollmentHandlerChromeOS::PolicyValidated(
201a971dbdde27fd4ff53dbebdd4aaf87826d081aa2Evan Cheng    DeviceCloudPolicyValidator* validator) {
202a971dbdde27fd4ff53dbebdd4aaf87826d081aa2Evan Cheng  CHECK_EQ(STEP_VALIDATION, enrollment_step_);
20313d828567812041c1ca1817f4b66fce840903a1fEvan Cheng  if (validator->success()) {
20413d828567812041c1ca1817f4b66fce840903a1fEvan Cheng    policy_ = validator->policy().Pass();
20581bf03eb5cd68243eabb52505105aa5f4a831bf3Dan Gohman    username_ = validator->policy_data()->username();
20681bf03eb5cd68243eabb52505105aa5f4a831bf3Dan Gohman    device_id_ = validator->policy_data()->device_id();
20781bf03eb5cd68243eabb52505105aa5f4a831bf3Dan Gohman
20813d828567812041c1ca1817f4b66fce840903a1fEvan Cheng    if (CommandLine::ForCurrentProcess()->HasSwitch(
20913d828567812041c1ca1817f4b66fce840903a1fEvan Cheng            chromeos::switches::kEnterpriseEnrollmentSkipRobotAuth)) {
210fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan Cheng      // For test purposes we allow enrollment to succeed without proper robot
211fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan Cheng      // account and use the provided value as a token.
212fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan Cheng      refresh_token_ = kTestingRobotToken;
213fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan Cheng      enrollment_step_ = STEP_LOCK_DEVICE,
214fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan Cheng      StartLockDevice(username_, device_mode_, device_id_);
215fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan Cheng      return;
216fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan Cheng    }
217fb8075d03f5c87bd57dcc9c5f2304f6b13c55aadEvan Cheng
21830b8e51addc23fb317c03d093a25828d3d5be45aJim Laskey    enrollment_step_ = STEP_ROBOT_AUTH_FETCH;
21930b8e51addc23fb317c03d093a25828d3d5be45aJim Laskey    client_->FetchRobotAuthCodes(auth_token_);
22030b8e51addc23fb317c03d093a25828d3d5be45aJim Laskey  } else {
22130b8e51addc23fb317c03d093a25828d3d5be45aJim Laskey    ReportResult(EnrollmentStatus::ForValidationError(validator->status()));
22230b8e51addc23fb317c03d093a25828d3d5be45aJim Laskey  }
22330b8e51addc23fb317c03d093a25828d3d5be45aJim Laskey}
22430b8e51addc23fb317c03d093a25828d3d5be45aJim Laskey
22530b8e51addc23fb317c03d093a25828d3d5be45aJim Laskeyvoid EnrollmentHandlerChromeOS::OnRobotAuthCodesFetched(
22634ea07692f4cd7c007f1c5062bcebb6d03131077Chris Lattner    CloudPolicyClient* client) {
22734ea07692f4cd7c007f1c5062bcebb6d03131077Chris Lattner  DCHECK_EQ(client_.get(), client);
22834ea07692f4cd7c007f1c5062bcebb6d03131077Chris Lattner  CHECK_EQ(STEP_ROBOT_AUTH_FETCH, enrollment_step_);
22934ea07692f4cd7c007f1c5062bcebb6d03131077Chris Lattner
23034ea07692f4cd7c007f1c5062bcebb6d03131077Chris Lattner  enrollment_step_ = STEP_ROBOT_AUTH_REFRESH;
23134ea07692f4cd7c007f1c5062bcebb6d03131077Chris Lattner
23234ea07692f4cd7c007f1c5062bcebb6d03131077Chris Lattner  gaia::OAuthClientInfo client_info;
2337707a0df5b00c8326a581205639d6b2871f182e9Jim Grosbach  client_info.client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
2347707a0df5b00c8326a581205639d6b2871f182e9Jim Grosbach  client_info.client_secret =
2357707a0df5b00c8326a581205639d6b2871f182e9Jim Grosbach      GaiaUrls::GetInstance()->oauth2_chrome_client_secret();
2367707a0df5b00c8326a581205639d6b2871f182e9Jim Grosbach  client_info.redirect_uri = "oob";
2377707a0df5b00c8326a581205639d6b2871f182e9Jim Grosbach
2387707a0df5b00c8326a581205639d6b2871f182e9Jim Grosbach  // Use the system request context to avoid sending user cookies.
2397707a0df5b00c8326a581205639d6b2871f182e9Jim Grosbach  gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(
24076456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke      g_browser_process->system_request_context()));
24134ea07692f4cd7c007f1c5062bcebb6d03131077Chris Lattner  gaia_oauth_client_->GetTokensFromAuthCode(client_info,
24276456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke                                            client->robot_api_auth_code(),
24376456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke                                            0 /* max_retries */,
24476456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke                                            this);
2453dfbb558b5e74554104710ed16807fe74ec220a3Chris Lattner}
24676456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke
247dc4a4922d32b470acd68498d41e3b8130cf1e74eChris Lattner// GaiaOAuthClient::Delegate callback for OAuth2 refresh token fetched.
24876456bc40c79fcae4da52d34f96c079d9759257cBrian Gaekevoid EnrollmentHandlerChromeOS::OnGetTokensResponse(
24976456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke    const std::string& refresh_token,
2503dfbb558b5e74554104710ed16807fe74ec220a3Chris Lattner    const std::string& access_token,
251dc4a4922d32b470acd68498d41e3b8130cf1e74eChris Lattner    int expires_in_seconds) {
252dc4a4922d32b470acd68498d41e3b8130cf1e74eChris Lattner  CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
253dc4a4922d32b470acd68498d41e3b8130cf1e74eChris Lattner
2548a46d342d8cbca7c9c7be6c66007d41329babad0David Greene  refresh_token_ = refresh_token;
255dc4a4922d32b470acd68498d41e3b8130cf1e74eChris Lattner
2568a46d342d8cbca7c9c7be6c66007d41329babad0David Greene  enrollment_step_ = STEP_LOCK_DEVICE,
257cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattner  StartLockDevice(username_, device_mode_, device_id_);
25863307c335aa08b0d6a75f81d64d79af7e90eb78bMon P Wang}
25963307c335aa08b0d6a75f81d64d79af7e90eb78bMon P Wang
2601edb8e0910db4cda46a81a4abad80a2a755442aaDan Gohman// GaiaOAuthClient::Delegate
26163307c335aa08b0d6a75f81d64d79af7e90eb78bMon P Wangvoid EnrollmentHandlerChromeOS::OnRefreshTokenResponse(
26214152b480d09c7ca912af7c06d00b0ff3912e4f5Dan Gohman    const std::string& access_token,
26314152b480d09c7ca912af7c06d00b0ff3912e4f5Dan Gohman    int expires_in_seconds) {
26414152b480d09c7ca912af7c06d00b0ff3912e4f5Dan Gohman  // We never use the code that should trigger this callback.
26514152b480d09c7ca912af7c06d00b0ff3912e4f5Dan Gohman  LOG(FATAL) << "Unexpected callback invoked";
26614152b480d09c7ca912af7c06d00b0ff3912e4f5Dan Gohman}
26763307c335aa08b0d6a75f81d64d79af7e90eb78bMon P Wang
268cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattner// GaiaOAuthClient::Delegate OAuth2 error when fetching refresh token request.
269cd5bad37112ba22f4c546e5443714570b4104bb6Chris Lattnervoid EnrollmentHandlerChromeOS::OnOAuthError() {
2706d1b89e74f98470d05666ca9f59a8ec5d04b5eb4Dan Gohman  CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
27176456bc40c79fcae4da52d34f96c079d9759257cBrian Gaeke  // OnOAuthError is only called if the request is bad (malformed) or the
2726ade6f55a836129af634074e96f660ff23f59a30Dan Gohman  // response is bad (empty access token returned).
2736ade6f55a836129af634074e96f660ff23f59a30Dan Gohman  LOG(ERROR) << "OAuth protocol error while fetching API refresh token.";
2746ade6f55a836129af634074e96f660ff23f59a30Dan Gohman  ReportResult(
2756ade6f55a836129af634074e96f660ff23f59a30Dan Gohman      EnrollmentStatus::ForRobotRefreshFetchError(net::HTTP_BAD_REQUEST));
2766ade6f55a836129af634074e96f660ff23f59a30Dan Gohman}
2776d1b89e74f98470d05666ca9f59a8ec5d04b5eb4Dan Gohman
2786ade6f55a836129af634074e96f660ff23f59a30Dan Gohman// GaiaOAuthClient::Delegate network error when fetching refresh token.
27915acadde5f87703da5f36721a19c09a7e3f97f53Bob Wilsonvoid EnrollmentHandlerChromeOS::OnNetworkError(int response_code) {
28015acadde5f87703da5f36721a19c09a7e3f97f53Bob Wilson  CHECK_EQ(STEP_ROBOT_AUTH_REFRESH, enrollment_step_);
28115acadde5f87703da5f36721a19c09a7e3f97f53Bob Wilson  LOG(ERROR) << "Network error while fetching API refresh token: "
28215acadde5f87703da5f36721a19c09a7e3f97f53Bob Wilson             << response_code;
28315acadde5f87703da5f36721a19c09a7e3f97f53Bob Wilson  ReportResult(
28415acadde5f87703da5f36721a19c09a7e3f97f53Bob Wilson      EnrollmentStatus::ForRobotRefreshFetchError(response_code));
285d463a7446402f0771465fe66fe0a7d9f72534902Dan Gohman}
286d463a7446402f0771465fe66fe0a7d9f72534902Dan Gohman
287d463a7446402f0771465fe66fe0a7d9f72534902Dan Gohmanvoid EnrollmentHandlerChromeOS::StartLockDevice(
288d463a7446402f0771465fe66fe0a7d9f72534902Dan Gohman    const std::string& user,
289d463a7446402f0771465fe66fe0a7d9f72534902Dan Gohman    DeviceMode device_mode,
290d463a7446402f0771465fe66fe0a7d9f72534902Dan Gohman    const std::string& device_id) {
291d463a7446402f0771465fe66fe0a7d9f72534902Dan Gohman  CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_);
292743d0a1f831f1d5a3141a6ca730558f40c35690aAlkis Evlogimenos  // Since this method is also called directly.
293743d0a1f831f1d5a3141a6ca730558f40c35690aAlkis Evlogimenos  weak_ptr_factory_.InvalidateWeakPtrs();
294743d0a1f831f1d5a3141a6ca730558f40c35690aAlkis Evlogimenos
295743d0a1f831f1d5a3141a6ca730558f40c35690aAlkis Evlogimenos  install_attributes_->LockDevice(
296743d0a1f831f1d5a3141a6ca730558f40c35690aAlkis Evlogimenos      user, device_mode, device_id,
297853d3fb8d24fab2258e9cd5dce3ec8ff4189eedaDan Gohman      base::Bind(&EnrollmentHandlerChromeOS::HandleLockDeviceResult,
298853d3fb8d24fab2258e9cd5dce3ec8ff4189eedaDan Gohman                 weak_ptr_factory_.GetWeakPtr(),
299853d3fb8d24fab2258e9cd5dce3ec8ff4189eedaDan Gohman                 user,
300853d3fb8d24fab2258e9cd5dce3ec8ff4189eedaDan Gohman                 device_mode,
301853d3fb8d24fab2258e9cd5dce3ec8ff4189eedaDan Gohman                 device_id));
302853d3fb8d24fab2258e9cd5dce3ec8ff4189eedaDan Gohman}
303853d3fb8d24fab2258e9cd5dce3ec8ff4189eedaDan Gohman
304853d3fb8d24fab2258e9cd5dce3ec8ff4189eedaDan Gohmanvoid EnrollmentHandlerChromeOS::HandleLockDeviceResult(
3050bbcd6bfd1a90c2aa7386a88ba9fc9aba47d03a7Brian Gaeke    const std::string& user,
306f24d879520a615a03f19ced3c7491f7b51934d1cChris Lattner    DeviceMode device_mode,
307deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve    const std::string& device_id,
308deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve    EnterpriseInstallAttributes::LockResult lock_result) {
309deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve  CHECK_EQ(STEP_LOCK_DEVICE, enrollment_step_);
310deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve  switch (lock_result) {
311deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve    case EnterpriseInstallAttributes::LOCK_SUCCESS:
31246d6a1aeb549a2e4ccd982a1a2cefda541d79c52Vikram S. Adve      // Get the token service so we can store our robot refresh token.
313c0b9dc5be79f009d260edb5cd5e1d8346587aaa2Alkis Evlogimenos      enrollment_step_ = STEP_STORE_ROBOT_AUTH;
314deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve      chromeos::DeviceOAuth2TokenServiceFactory::Get()->SetAndSaveRefreshToken(
31546d6a1aeb549a2e4ccd982a1a2cefda541d79c52Vikram S. Adve          refresh_token_,
316deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve          base::Bind(&EnrollmentHandlerChromeOS::HandleRobotAuthTokenStored,
31783706a5a3a6f19451765b743c5a72b62f74eb71aChris Lattner                     weak_ptr_factory_.GetWeakPtr()));
318da44b151259525abc9c299f89b9532f3a9883b4eBrian Gaeke      return;
319ea61c358720aa6c7a159d51658b34276316aa841Misha Brukman    case EnterpriseInstallAttributes::LOCK_NOT_READY:
320c475c3608a5f0fc0c6bd43da04ae786649690070Dan Gohman      // We wait up to |kLockRetryTimeoutMs| milliseconds and if it hasn't
321c475c3608a5f0fc0c6bd43da04ae786649690070Dan Gohman      // succeeded by then show an error to the user and stop the enrollment.
322c475c3608a5f0fc0c6bd43da04ae786649690070Dan Gohman      if (lockbox_init_duration_ < kLockRetryTimeoutMs) {
323c475c3608a5f0fc0c6bd43da04ae786649690070Dan Gohman        // InstallAttributes not ready yet, retry later.
324c475c3608a5f0fc0c6bd43da04ae786649690070Dan Gohman        LOG(WARNING) << "Install Attributes not ready yet will retry in "
325c475c3608a5f0fc0c6bd43da04ae786649690070Dan Gohman                     << kLockRetryIntervalMs << "ms.";
326f24d879520a615a03f19ced3c7491f7b51934d1cChris Lattner        base::MessageLoop::current()->PostDelayedTask(
327f24d879520a615a03f19ced3c7491f7b51934d1cChris Lattner            FROM_HERE,
328f24d879520a615a03f19ced3c7491f7b51934d1cChris Lattner            base::Bind(&EnrollmentHandlerChromeOS::StartLockDevice,
329f24d879520a615a03f19ced3c7491f7b51934d1cChris Lattner                       weak_ptr_factory_.GetWeakPtr(),
330f24d879520a615a03f19ced3c7491f7b51934d1cChris Lattner                       user, device_mode, device_id),
331f24d879520a615a03f19ced3c7491f7b51934d1cChris Lattner            base::TimeDelta::FromMilliseconds(kLockRetryIntervalMs));
3328e7ae9860bd1f29c95e4e10fe151a22aaafafef9Chris Lattner        lockbox_init_duration_ += kLockRetryIntervalMs;
3338e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman      } else {
3348e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman        ReportResult(EnrollmentStatus::ForStatus(
3358e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman            EnrollmentStatus::STATUS_LOCK_TIMEOUT));
3368e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman      }
3378e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman      return;
3388e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman    case EnterpriseInstallAttributes::LOCK_BACKEND_ERROR:
3398e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman      ReportResult(EnrollmentStatus::ForStatus(
3408e5f2c6f65841542e2a7092553fe42a00048e4c7Dan Gohman          EnrollmentStatus::STATUS_LOCK_ERROR));
3410370fad74b48388412c52d1325512f2c218487faEvan Cheng      return;
3420370fad74b48388412c52d1325512f2c218487faEvan Cheng    case EnterpriseInstallAttributes::LOCK_WRONG_USER:
3430370fad74b48388412c52d1325512f2c218487faEvan Cheng      LOG(ERROR) << "Enrollment cannot proceed because the InstallAttrs "
3440370fad74b48388412c52d1325512f2c218487faEvan Cheng                 << "has been locked already!";
3452bdb7d0cc88881857073b36f4a09ebe2f2008c24Evan Cheng      ReportResult(EnrollmentStatus::ForStatus(
3462bdb7d0cc88881857073b36f4a09ebe2f2008c24Evan Cheng          EnrollmentStatus::STATUS_LOCK_WRONG_USER));
3472bdb7d0cc88881857073b36f4a09ebe2f2008c24Evan Cheng      return;
3482bdb7d0cc88881857073b36f4a09ebe2f2008c24Evan Cheng  }
3492bdb7d0cc88881857073b36f4a09ebe2f2008c24Evan Cheng
3502bdb7d0cc88881857073b36f4a09ebe2f2008c24Evan Cheng  NOTREACHED() << "Invalid lock result " << lock_result;
3512bdb7d0cc88881857073b36f4a09ebe2f2008c24Evan Cheng  ReportResult(EnrollmentStatus::ForStatus(
3522bdb7d0cc88881857073b36f4a09ebe2f2008c24Evan Cheng      EnrollmentStatus::STATUS_LOCK_ERROR));
3532bdb7d0cc88881857073b36f4a09ebe2f2008c24Evan Cheng}
3542bdb7d0cc88881857073b36f4a09ebe2f2008c24Evan Cheng
35573e884bb3e971b1e794ba2501df15138f73b8b1aDale Johannesenvoid EnrollmentHandlerChromeOS::HandleRobotAuthTokenStored(bool result) {
3563b1906f48d0b541e5be0bc1918c94a454e908bc0Dale Johannesen  CHECK_EQ(STEP_STORE_ROBOT_AUTH, enrollment_step_);
35773e884bb3e971b1e794ba2501df15138f73b8b1aDale Johannesen
35873e884bb3e971b1e794ba2501df15138f73b8b1aDale Johannesen  if (!result) {
359f13a3f4dd1eaa89ca9a64a1e820b089facca3366Brian Gaeke    LOG(ERROR) << "Failed to store API refresh token.";
360f13a3f4dd1eaa89ca9a64a1e820b089facca3366Brian Gaeke    ReportResult(EnrollmentStatus::ForStatus(
3612d8e3d20be377112999670f210200b3658762571Chris Lattner        EnrollmentStatus::STATUS_ROBOT_REFRESH_STORE_FAILED));
362f13a3f4dd1eaa89ca9a64a1e820b089facca3366Brian Gaeke    return;
363da86bdc75c8d36cb7b1f4e785a3749d7c8f8e638Brian Gaeke  }
364da86bdc75c8d36cb7b1f4e785a3749d7c8f8e638Brian Gaeke
365da86bdc75c8d36cb7b1f4e785a3749d7c8f8e638Brian Gaeke  enrollment_step_ = STEP_STORE_POLICY;
366da86bdc75c8d36cb7b1f4e785a3749d7c8f8e638Brian Gaeke  store_->InstallInitialPolicy(*policy_);
367c07d8d8a26f63dfc54dbd0e1ff776763ec6443adBrian Gaeke}
36820a6d8e63056cc2025e7143a5f7226e079e0bc80Chris Lattner
369c07d8d8a26f63dfc54dbd0e1ff776763ec6443adBrian Gaekevoid EnrollmentHandlerChromeOS::Stop() {
370f71cb015c1386ff8adc9ef0aa03fc0f0fc4a6e3eChris Lattner  if (client_.get())
371f71cb015c1386ff8adc9ef0aa03fc0f0fc4a6e3eChris Lattner    client_->RemoveObserver(this);
3721b2eb0e8a6aaf034675b17be6d853cb1c666200fChris Lattner  enrollment_step_ = STEP_FINISHED;
373f71cb015c1386ff8adc9ef0aa03fc0f0fc4a6e3eChris Lattner  weak_ptr_factory_.InvalidateWeakPtrs();
3748e7ae9860bd1f29c95e4e10fe151a22aaafafef9Chris Lattner  completion_callback_.Reset();
375fed90b6d097d50881afb45e4d79f430db66dd741Dan Gohman}
3768560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke
3778560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaekevoid EnrollmentHandlerChromeOS::ReportResult(EnrollmentStatus status) {
3788560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke  EnrollmentCallback callback = completion_callback_;
3798560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke  Stop();
3808560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke
3818560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke  if (status.status() != EnrollmentStatus::STATUS_SUCCESS) {
3828560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke    LOG(WARNING) << "Enrollment failed: " << status.status()
3833dfbb558b5e74554104710ed16807fe74ec220a3Chris Lattner                 << " " << status.client_status()
3848560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke                 << " " << status.validation_status()
3858560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke                 << " " << status.store_status();
3868560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke  }
3878560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke
3888560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke  if (!callback.is_null())
3898560af4f5fe589cf792bd44617c2308c4f087ba8Brian Gaeke    callback.Run(status);
3903dfbb558b5e74554104710ed16807fe74ec220a3Chris Lattner}
391deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve
392deb9654056939a12981446f6ed1139dca3412746Vikram S. Adve}  // namespace policy
3931cd1d98232c3c3a0bd3810c3bf6c2572ea02f208Daniel Dunbar