1// Copyright (c) 2011 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 "chrome/browser/policy/device_token_fetcher.h"
6
7#include <algorithm>
8
9#include "base/message_loop.h"
10#include "chrome/browser/policy/cloud_policy_cache_base.h"
11#include "chrome/browser/policy/device_management_service.h"
12#include "chrome/browser/policy/proto/device_management_constants.h"
13#include "chrome/browser/policy/proto/device_management_local.pb.h"
14
15namespace {
16
17// Retry after 5 minutes (with exponential backoff) after token fetch errors.
18const int64 kTokenFetchErrorDelayMilliseconds = 5 * 60 * 1000;
19// Retry after max 3 hours after token fetch errors.
20const int64 kTokenFetchErrorMaxDelayMilliseconds = 3 * 60 * 60 * 1000;
21// For unmanaged devices, check once per day whether they're still unmanaged.
22const int64 kUnmanagedDeviceRefreshRateMilliseconds = 24 * 60 * 60 * 1000;
23
24}  // namespace
25
26namespace policy {
27
28namespace em = enterprise_management;
29
30DeviceTokenFetcher::DeviceTokenFetcher(
31    DeviceManagementService* service,
32    CloudPolicyCacheBase* cache,
33    PolicyNotifier* notifier)
34    : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
35  Initialize(service,
36             cache,
37             notifier,
38             kTokenFetchErrorDelayMilliseconds,
39             kTokenFetchErrorMaxDelayMilliseconds,
40             kUnmanagedDeviceRefreshRateMilliseconds);
41}
42
43DeviceTokenFetcher::DeviceTokenFetcher(
44    DeviceManagementService* service,
45    CloudPolicyCacheBase* cache,
46    PolicyNotifier* notifier,
47    int64 token_fetch_error_delay_ms,
48    int64 token_fetch_error_max_delay_ms,
49    int64 unmanaged_device_refresh_rate_ms)
50    : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
51  Initialize(service,
52             cache,
53             notifier,
54             token_fetch_error_delay_ms,
55             token_fetch_error_max_delay_ms,
56             unmanaged_device_refresh_rate_ms);
57}
58
59DeviceTokenFetcher::~DeviceTokenFetcher() {
60  CancelRetryTask();
61}
62
63void DeviceTokenFetcher::FetchToken(
64    const std::string& auth_token,
65    const std::string& device_id,
66    em::DeviceRegisterRequest_Type policy_type,
67    const std::string& machine_id,
68    const std::string& machine_model) {
69  SetState(STATE_INACTIVE);
70  auth_token_ = auth_token;
71  device_id_ = device_id;
72  policy_type_ = policy_type;
73  machine_id_ = machine_id;
74  machine_model_ = machine_model;
75  FetchTokenInternal();
76}
77
78void DeviceTokenFetcher::FetchTokenInternal() {
79  DCHECK(state_ != STATE_TOKEN_AVAILABLE);
80  if (auth_token_.empty() || device_id_.empty()) {
81    // Maybe this device is unmanaged, just exit. The CloudPolicyController
82    // will call FetchToken() again if something changes.
83    return;
84  }
85  // Construct a new backend, which will discard any previous requests.
86  backend_.reset(service_->CreateBackend());
87  em::DeviceRegisterRequest request;
88  request.set_type(policy_type_);
89  if (!machine_id_.empty())
90    request.set_machine_id(machine_id_);
91  if (!machine_model_.empty())
92    request.set_machine_model(machine_model_);
93  backend_->ProcessRegisterRequest(auth_token_, device_id_, request, this);
94}
95
96void DeviceTokenFetcher::SetUnmanagedState() {
97  // The call to |cache_->SetUnmanaged()| has to happen first because it sets
98  // the timestamp that |SetState()| needs to determine the correct refresh
99  // time.
100  cache_->SetUnmanaged();
101  SetState(STATE_UNMANAGED);
102}
103
104const std::string& DeviceTokenFetcher::GetDeviceToken() {
105  return device_token_;
106}
107
108void DeviceTokenFetcher::StopAutoRetry() {
109  CancelRetryTask();
110  backend_.reset();
111  device_token_.clear();
112  auth_token_.clear();
113  device_id_.clear();
114}
115
116void DeviceTokenFetcher::AddObserver(DeviceTokenFetcher::Observer* observer) {
117  observer_list_.AddObserver(observer);
118}
119
120void DeviceTokenFetcher::RemoveObserver(
121    DeviceTokenFetcher::Observer* observer) {
122  observer_list_.RemoveObserver(observer);
123}
124
125void DeviceTokenFetcher::HandleRegisterResponse(
126    const em::DeviceRegisterResponse& response) {
127  if (response.has_device_management_token()) {
128    device_token_ = response.device_management_token();
129    SetState(STATE_TOKEN_AVAILABLE);
130  } else {
131    NOTREACHED();
132    SetState(STATE_ERROR);
133  }
134}
135
136void DeviceTokenFetcher::OnError(DeviceManagementBackend::ErrorCode code) {
137  switch (code) {
138    case DeviceManagementBackend::kErrorServiceManagementNotSupported:
139      cache_->SetUnmanaged();
140      SetState(STATE_UNMANAGED);
141      break;
142    case DeviceManagementBackend::kErrorRequestFailed:
143    case DeviceManagementBackend::kErrorTemporaryUnavailable:
144    case DeviceManagementBackend::kErrorServiceDeviceNotFound:
145      SetState(STATE_TEMPORARY_ERROR);
146      break;
147    case DeviceManagementBackend::kErrorServiceManagementTokenInvalid:
148      // Most probably the GAIA auth cookie has expired. We can not do anything
149      // until the user logs-in again.
150      SetState(STATE_BAD_AUTH);
151      break;
152    default:
153      SetState(STATE_ERROR);
154  }
155}
156
157void DeviceTokenFetcher::Initialize(DeviceManagementService* service,
158                                    CloudPolicyCacheBase* cache,
159                                    PolicyNotifier* notifier,
160                                    int64 token_fetch_error_delay_ms,
161                                    int64 token_fetch_error_max_delay_ms,
162                                    int64 unmanaged_device_refresh_rate_ms) {
163  service_ = service;
164  cache_ = cache;
165  notifier_ = notifier;
166  token_fetch_error_delay_ms_ = token_fetch_error_delay_ms;
167  token_fetch_error_max_delay_ms_ = token_fetch_error_max_delay_ms;
168  effective_token_fetch_error_delay_ms_ = token_fetch_error_delay_ms;
169  unmanaged_device_refresh_rate_ms_ = unmanaged_device_refresh_rate_ms;
170  state_ = STATE_INACTIVE;
171  retry_task_ = NULL;
172
173  if (cache_->is_unmanaged())
174    SetState(STATE_UNMANAGED);
175}
176
177void DeviceTokenFetcher::SetState(FetcherState state) {
178  state_ = state;
179  if (state_ != STATE_ERROR)
180    effective_token_fetch_error_delay_ms_ = token_fetch_error_delay_ms_;
181
182  base::Time delayed_work_at;
183  switch (state_) {
184    case STATE_INACTIVE:
185      device_token_.clear();
186      auth_token_.clear();
187      device_id_.clear();
188      notifier_->Inform(CloudPolicySubsystem::UNENROLLED,
189                        CloudPolicySubsystem::NO_DETAILS,
190                        PolicyNotifier::TOKEN_FETCHER);
191      break;
192    case STATE_TOKEN_AVAILABLE:
193      FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceTokenAvailable());
194      notifier_->Inform(CloudPolicySubsystem::SUCCESS,
195                        CloudPolicySubsystem::NO_DETAILS,
196                        PolicyNotifier::TOKEN_FETCHER);
197      break;
198    case STATE_UNMANAGED:
199      delayed_work_at = cache_->last_policy_refresh_time() +
200          base::TimeDelta::FromMilliseconds(unmanaged_device_refresh_rate_ms_);
201      notifier_->Inform(CloudPolicySubsystem::UNMANAGED,
202                        CloudPolicySubsystem::NO_DETAILS,
203                        PolicyNotifier::TOKEN_FETCHER);
204      break;
205    case STATE_TEMPORARY_ERROR:
206      delayed_work_at = base::Time::Now() +
207          base::TimeDelta::FromMilliseconds(
208              effective_token_fetch_error_delay_ms_);
209      effective_token_fetch_error_delay_ms_ =
210          std::min(effective_token_fetch_error_delay_ms_ * 2,
211                   token_fetch_error_max_delay_ms_);
212      notifier_->Inform(CloudPolicySubsystem::NETWORK_ERROR,
213                        CloudPolicySubsystem::DMTOKEN_NETWORK_ERROR,
214                        PolicyNotifier::TOKEN_FETCHER);
215      break;
216    case STATE_ERROR:
217      effective_token_fetch_error_delay_ms_ = token_fetch_error_max_delay_ms_;
218      delayed_work_at = base::Time::Now() +
219          base::TimeDelta::FromMilliseconds(
220              effective_token_fetch_error_delay_ms_);
221      notifier_->Inform(CloudPolicySubsystem::NETWORK_ERROR,
222                        CloudPolicySubsystem::DMTOKEN_NETWORK_ERROR,
223                        PolicyNotifier::TOKEN_FETCHER);
224      break;
225    case STATE_BAD_AUTH:
226      // Can't do anything, need to wait for new credentials.
227      notifier_->Inform(CloudPolicySubsystem::BAD_GAIA_TOKEN,
228                        CloudPolicySubsystem::NO_DETAILS,
229                        PolicyNotifier::TOKEN_FETCHER);
230      break;
231  }
232
233  CancelRetryTask();
234  if (!delayed_work_at.is_null()) {
235    base::Time now(base::Time::Now());
236    int64 delay = std::max<int64>((delayed_work_at - now).InMilliseconds(), 0);
237    retry_task_ = method_factory_.NewRunnableMethod(
238            &DeviceTokenFetcher::ExecuteRetryTask);
239    MessageLoop::current()->PostDelayedTask(FROM_HERE, retry_task_,
240                                            delay);
241  }
242}
243
244void DeviceTokenFetcher::ExecuteRetryTask() {
245  DCHECK(retry_task_);
246  retry_task_ = NULL;
247
248  switch (state_) {
249    case STATE_INACTIVE:
250    case STATE_TOKEN_AVAILABLE:
251      break;
252    case STATE_UNMANAGED:
253    case STATE_ERROR:
254    case STATE_TEMPORARY_ERROR:
255    case STATE_BAD_AUTH:
256      FetchTokenInternal();
257      break;
258  }
259}
260
261void DeviceTokenFetcher::CancelRetryTask() {
262  if (retry_task_) {
263    retry_task_->Cancel();
264    retry_task_ = NULL;
265  }
266}
267
268}  // namespace policy
269