device_token_fetcher.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
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 "base/file_util.h"
8#include "base/path_service.h"
9#include "base/singleton.h"
10#include "base/string_util.h"
11#include "chrome/browser/net/gaia/token_service.h"
12#include "chrome/browser/policy/proto/device_management_local.pb.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/common/chrome_paths.h"
15#include "chrome/common/guid.h"
16#include "chrome/common/net/gaia/gaia_constants.h"
17#include "chrome/common/notification_details.h"
18#include "chrome/common/notification_service.h"
19#include "chrome/common/notification_source.h"
20#include "chrome/common/notification_type.h"
21
22#if defined(OS_CHROMEOS)
23#include "chrome/browser/chromeos/login/user_manager.h"
24#else
25#include "chrome/browser/browser_signin.h"
26#endif
27
28namespace {
29
30// Domain names that are known not to be managed.
31// We don't register the device when such a user logs in.
32const char* kNonManagedDomains[] = {
33  "@googlemail.com",
34  "@gmail.com"
35};
36
37// Checks the domain part of the given username against the list of known
38// non-managed domain names. Returns false if |username| is empty or its
39// in a domain known not to be managed.
40bool CanBeInManagedDomain(const std::string& username) {
41  if (username.empty()) {
42    // This means incognito user in case of ChromiumOS and
43    // no logged-in user in case of Chromium (SigninService).
44    return false;
45  }
46  for (size_t i = 0; i < arraysize(kNonManagedDomains); i++) {
47    if (EndsWith(username, kNonManagedDomains[i], true)) {
48      return false;
49    }
50  }
51  return true;
52}
53
54}  // namespace
55
56namespace policy {
57
58namespace em = enterprise_management;
59
60DeviceTokenFetcher::ObserverRegistrar::ObserverRegistrar() {}
61
62DeviceTokenFetcher::ObserverRegistrar::~ObserverRegistrar() {
63  RemoveAll();
64}
65
66void DeviceTokenFetcher::ObserverRegistrar::Init(
67    DeviceTokenFetcher* token_fetcher) {
68  token_fetcher_ = token_fetcher;
69}
70
71void DeviceTokenFetcher::ObserverRegistrar::AddObserver(
72    DeviceTokenFetcher::Observer* observer) {
73  observers_.push_back(observer);
74  token_fetcher_->AddObserver(observer);
75}
76
77void DeviceTokenFetcher::ObserverRegistrar::RemoveAll() {
78  for (std::vector<DeviceTokenFetcher::Observer*>::iterator it =
79           observers_.begin(); it != observers_.end(); ++it) {
80    token_fetcher_->RemoveObserver(*it);
81  }
82  observers_.clear();
83}
84
85DeviceTokenFetcher::DeviceTokenFetcher(
86    DeviceManagementBackend* backend,
87    Profile* profile,
88    const FilePath& token_path)
89    : profile_(profile),
90      token_path_(token_path),
91      backend_(backend),
92      state_(kStateNotStarted),
93      device_token_load_complete_event_(true, false) {
94  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
95
96  TokenService* token_service = profile_->GetTokenService();
97  auth_token_ = token_service->GetTokenForService(
98      GaiaConstants::kDeviceManagementService);
99
100  registrar_.Add(this,
101                 NotificationType::TOKEN_AVAILABLE,
102                 Source<TokenService>(token_service));
103  // Register for the event of user login. The device management token won't
104  // be fetched until we know the domain of the currently logged in user.
105#if defined(OS_CHROMEOS)
106  registrar_.Add(this,
107                 NotificationType::LOGIN_USER_CHANGED,
108                 NotificationService::AllSources());
109#else
110  registrar_.Add(this,
111                 NotificationType::GOOGLE_SIGNIN_SUCCESSFUL,
112                 Source<Profile>(profile_));
113#endif
114}
115
116DeviceTokenFetcher::~DeviceTokenFetcher() {}
117
118void DeviceTokenFetcher::Observe(NotificationType type,
119                                 const NotificationSource& source,
120                                 const NotificationDetails& details) {
121  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
122  if (type == NotificationType::TOKEN_AVAILABLE) {
123    if (Source<TokenService>(source).ptr() == profile_->GetTokenService()) {
124      const TokenService::TokenAvailableDetails* token_details =
125          Details<const TokenService::TokenAvailableDetails>(details).ptr();
126      if (token_details->service() == GaiaConstants::kDeviceManagementService) {
127        if (!HasAuthToken()) {
128          auth_token_ = token_details->token();
129          SendServerRequestIfPossible();
130        }
131      }
132    }
133#if defined(OS_CHROMEOS)
134  } else if (type == NotificationType::LOGIN_USER_CHANGED) {
135    SendServerRequestIfPossible();
136#else
137  } else if (type == NotificationType::GOOGLE_SIGNIN_SUCCESSFUL) {
138    if (profile_ == Source<Profile>(source).ptr()) {
139      SendServerRequestIfPossible();
140    }
141#endif
142  } else {
143    NOTREACHED();
144  }
145}
146
147std::string DeviceTokenFetcher::GetCurrentUser() {
148#if defined(OS_CHROMEOS)
149  return chromeos::UserManager::Get()->logged_in_user().email();
150#else
151  return profile_->GetBrowserSignin()->GetSignedInUsername();
152#endif
153}
154
155void DeviceTokenFetcher::HandleRegisterResponse(
156    const em::DeviceRegisterResponse& response) {
157  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
158  DCHECK_EQ(kStateRequestingDeviceTokenFromServer, state_);
159  if (response.has_device_management_token()) {
160    device_token_ = response.device_management_token();
161    BrowserThread::PostTask(
162        BrowserThread::FILE,
163        FROM_HERE,
164        NewRunnableFunction(&WriteDeviceTokenToDisk,
165                            token_path_,
166                            device_token_,
167                            device_id_));
168    SetState(kStateHasDeviceToken);
169  } else {
170    NOTREACHED();
171    SetState(kStateFailure);
172  }
173}
174
175void DeviceTokenFetcher::OnError(DeviceManagementBackend::ErrorCode code) {
176  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
177  // For privacy reasons, delete all identifying data when this device is not
178  // managed.
179  if (code == DeviceManagementBackend::kErrorServiceManagementNotSupported) {
180    device_token_ = std::string();
181    device_id_ = std::string();
182    BrowserThread::PostTask(
183        BrowserThread::FILE,
184        FROM_HERE,
185        // The Windows compiler needs explicit template instantiation.
186        NewRunnableFunction<bool(*)(const FilePath&, bool), FilePath, bool>(
187            &file_util::Delete, token_path_, false));
188    SetState(kStateNotManaged);
189    return;
190  }
191  SetState(kStateFailure);
192}
193
194void DeviceTokenFetcher::Restart() {
195  DCHECK(!IsTokenPending());
196  device_token_.clear();
197  device_token_load_complete_event_.Reset();
198  MakeReadyToRequestDeviceToken();
199}
200
201void DeviceTokenFetcher::StartFetching() {
202  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
203  if (state_ == kStateNotStarted) {
204    SetState(kStateLoadDeviceTokenFromDisk);
205    // The file calls for loading the persisted token must be deferred to the
206    // FILE thread.
207    BrowserThread::PostTask(
208        BrowserThread::FILE,
209        FROM_HERE,
210        NewRunnableMethod(this,
211                          &DeviceTokenFetcher::AttemptTokenLoadFromDisk));
212  }
213}
214
215void DeviceTokenFetcher::AttemptTokenLoadFromDisk() {
216  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
217  if (file_util::PathExists(token_path_)) {
218    std::string data;
219    em::DeviceCredentials device_credentials;
220    if (file_util::ReadFileToString(token_path_, &data) &&
221        device_credentials.ParseFromArray(data.c_str(), data.size())) {
222      device_token_ = device_credentials.device_token();
223      device_id_ = device_credentials.device_id();
224      if (!device_token_.empty() && !device_id_.empty()) {
225        BrowserThread::PostTask(
226            BrowserThread::UI,
227            FROM_HERE,
228            NewRunnableMethod(this,
229                              &DeviceTokenFetcher::SetState,
230                              kStateHasDeviceToken));
231        return;
232      }
233    }
234  }
235
236  BrowserThread::PostTask(
237      BrowserThread::UI,
238      FROM_HERE,
239      NewRunnableMethod(this,
240                        &DeviceTokenFetcher::MakeReadyToRequestDeviceToken));
241}
242
243void DeviceTokenFetcher::MakeReadyToRequestDeviceToken() {
244  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
245  SetState(kStateReadyToRequestDeviceTokenFromServer);
246  SendServerRequestIfPossible();
247}
248
249void DeviceTokenFetcher::SendServerRequestIfPossible() {
250  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
251  std::string username = GetCurrentUser();
252  if (state_ == kStateReadyToRequestDeviceTokenFromServer
253      && HasAuthToken()
254      && backend_
255      && !username.empty()) {
256    if (CanBeInManagedDomain(username)) {
257      em::DeviceRegisterRequest register_request;
258      SetState(kStateRequestingDeviceTokenFromServer);
259      backend_->ProcessRegisterRequest(auth_token_,
260                                       GetDeviceID(),
261                                       register_request,
262                                       this);
263    } else {
264      SetState(kStateNotManaged);
265    }
266  }
267}
268
269bool DeviceTokenFetcher::IsTokenPending() {
270  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271  return !device_token_load_complete_event_.IsSignaled();
272}
273
274std::string DeviceTokenFetcher::GetDeviceToken() {
275  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
276  device_token_load_complete_event_.Wait();
277  return device_token_;
278}
279
280std::string DeviceTokenFetcher::GetDeviceID() {
281  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
282  // As long as access to this is only allowed from the UI thread, no explicit
283  // locking is necessary to prevent the ID from being generated twice.
284  if (device_id_.empty())
285    device_id_ = GenerateNewDeviceID();
286  return device_id_;
287}
288
289void DeviceTokenFetcher::SetState(FetcherState state) {
290  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
291  if (state_ == state)
292    return;
293  state_ = state;
294  if (state == kStateFailure) {
295    device_token_load_complete_event_.Signal();
296    NotifyTokenError();
297  } else if (state == kStateNotManaged) {
298    device_token_load_complete_event_.Signal();
299    NotifyNotManaged();
300  } else if (state == kStateHasDeviceToken) {
301    device_token_load_complete_event_.Signal();
302    NotifyTokenSuccess();
303  }
304}
305
306void DeviceTokenFetcher::GetDeviceTokenPath(FilePath* token_path) const {
307  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
308  *token_path = token_path_;
309}
310
311bool DeviceTokenFetcher::IsTokenValid() const {
312  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
313  return state_ == kStateHasDeviceToken;
314}
315
316// static
317void DeviceTokenFetcher::WriteDeviceTokenToDisk(
318    const FilePath& path,
319    const std::string& device_token,
320    const std::string& device_id) {
321  em::DeviceCredentials device_credentials;
322  device_credentials.set_device_token(device_token);
323  device_credentials.set_device_id(device_id);
324  std::string data;
325  bool no_error = device_credentials.SerializeToString(&data);
326  DCHECK(no_error);
327  file_util::WriteFile(path, data.c_str(), data.length());
328}
329
330// static
331std::string DeviceTokenFetcher::GenerateNewDeviceID() {
332  return guid::GenerateGUID();
333}
334
335}  // namespace policy
336