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/user_policy_identity_strategy.h"
6
7#include "base/file_util.h"
8#include "chrome/browser/browser_signin.h"
9#include "chrome/browser/net/gaia/token_service.h"
10#include "chrome/browser/policy/proto/device_management_backend.pb.h"
11#include "chrome/browser/policy/proto/device_management_constants.h"
12#include "chrome/browser/policy/proto/device_management_local.pb.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/common/guid.h"
15#include "chrome/common/net/gaia/gaia_constants.h"
16#include "content/browser/browser_thread.h"
17#include "content/common/notification_details.h"
18#include "content/common/notification_service.h"
19#include "content/common/notification_source.h"
20
21#if defined(OS_CHROMEOS)
22#include "chrome/browser/chromeos/login/user_manager.h"
23#endif
24
25namespace policy {
26
27namespace em = enterprise_management;
28
29// Responsible for managing the on-disk token cache.
30class UserPolicyIdentityStrategy::TokenCache
31    : public base::RefCountedThreadSafe<
32          UserPolicyIdentityStrategy::TokenCache> {
33 public:
34  TokenCache(const base::WeakPtr<UserPolicyIdentityStrategy>& identity_strategy,
35             const FilePath& cache_file);
36
37  void Load();
38  void Store(const std::string& token, const std::string& device_id);
39
40 private:
41  friend class base::RefCountedThreadSafe<
42      UserPolicyIdentityStrategy::TokenCache>;
43  ~TokenCache() {}
44  void LoadOnFileThread();
45  void NotifyOnUIThread(const std::string& token,
46                        const std::string& device_id);
47  void StoreOnFileThread(const std::string& token,
48                         const std::string& device_id);
49
50  const base::WeakPtr<UserPolicyIdentityStrategy> identity_strategy_;
51  const FilePath cache_file_;
52
53  DISALLOW_COPY_AND_ASSIGN(TokenCache);
54};
55
56UserPolicyIdentityStrategy::TokenCache::TokenCache(
57    const base::WeakPtr<UserPolicyIdentityStrategy>& identity_strategy,
58    const FilePath& cache_file)
59    : identity_strategy_(identity_strategy),
60      cache_file_(cache_file) {}
61
62void UserPolicyIdentityStrategy::TokenCache::Load() {
63  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
64  BrowserThread::PostTask(
65      BrowserThread::FILE, FROM_HERE,
66      NewRunnableMethod(
67          this, &UserPolicyIdentityStrategy::TokenCache::LoadOnFileThread));
68}
69
70void UserPolicyIdentityStrategy::TokenCache::Store(
71    const std::string& token,
72    const std::string& device_id) {
73  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
74  BrowserThread::PostTask(
75      BrowserThread::FILE, FROM_HERE,
76      NewRunnableMethod(
77          this,
78          &UserPolicyIdentityStrategy::TokenCache::StoreOnFileThread,
79          token,
80          device_id));
81}
82
83void UserPolicyIdentityStrategy::TokenCache::LoadOnFileThread() {
84  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
85  std::string device_token;
86  std::string device_id;
87
88  if (file_util::PathExists(cache_file_)) {
89    std::string data;
90    em::DeviceCredentials device_credentials;
91    if (file_util::ReadFileToString(cache_file_, &data) &&
92        device_credentials.ParseFromArray(data.c_str(), data.size())) {
93      device_token = device_credentials.device_token();
94      device_id = device_credentials.device_id();
95    }
96  }
97
98  BrowserThread::PostTask(
99      BrowserThread::UI, FROM_HERE,
100      NewRunnableMethod(
101          this,
102          &UserPolicyIdentityStrategy::TokenCache::NotifyOnUIThread,
103          device_token,
104          device_id));
105}
106
107void UserPolicyIdentityStrategy::TokenCache::NotifyOnUIThread(
108    const std::string& token,
109    const std::string& device_id) {
110  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
111  if (identity_strategy_.get())
112    identity_strategy_->OnCacheLoaded(token, device_id);
113}
114
115void UserPolicyIdentityStrategy::TokenCache::StoreOnFileThread(
116    const std::string& token,
117    const std::string& device_id) {
118  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
119  em::DeviceCredentials device_credentials;
120  device_credentials.set_device_token(token);
121  device_credentials.set_device_id(device_id);
122  std::string data;
123  bool success = device_credentials.SerializeToString(&data);
124  if (!success) {
125    LOG(WARNING) << "Failed serialize device token data, will not write "
126                 << cache_file_.value();
127    return;
128  }
129
130  file_util::WriteFile(cache_file_, data.c_str(), data.length());
131}
132
133UserPolicyIdentityStrategy::UserPolicyIdentityStrategy(
134    Profile* profile,
135    const FilePath& cache_file)
136    : profile_(profile),
137      ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
138  cache_ = new TokenCache(weak_ptr_factory_.GetWeakPtr(), cache_file);
139  registrar_.Add(this,
140                 NotificationType::TOKEN_AVAILABLE,
141                 Source<TokenService>(profile->GetTokenService()));
142
143  // Register for the event of user login. The device management token won't
144  // be fetched until we know the domain of the currently logged in user.
145#if defined(OS_CHROMEOS)
146  registrar_.Add(this,
147                 NotificationType::LOGIN_USER_CHANGED,
148                 NotificationService::AllSources());
149#else
150  registrar_.Add(this,
151                 NotificationType::GOOGLE_SIGNIN_SUCCESSFUL,
152                 Source<Profile>(profile_));
153#endif
154
155  cache_->Load();
156}
157
158UserPolicyIdentityStrategy::~UserPolicyIdentityStrategy() {}
159
160std::string UserPolicyIdentityStrategy::GetDeviceToken() {
161  return device_token_;
162}
163
164std::string UserPolicyIdentityStrategy::GetDeviceID() {
165  return device_id_;
166}
167
168std::string UserPolicyIdentityStrategy::GetMachineID() {
169  return std::string();
170}
171
172std::string UserPolicyIdentityStrategy::GetMachineModel() {
173  return std::string();
174}
175
176em::DeviceRegisterRequest_Type
177UserPolicyIdentityStrategy::GetPolicyRegisterType() {
178  return em::DeviceRegisterRequest::USER;
179}
180
181std::string UserPolicyIdentityStrategy::GetPolicyType() {
182  return kChromeUserPolicyType;
183}
184
185bool UserPolicyIdentityStrategy::GetCredentials(std::string* username,
186                                                std::string* auth_token) {
187  *username = GetCurrentUser();
188  *auth_token = profile_->GetTokenService()->GetTokenForService(
189      GaiaConstants::kDeviceManagementService);
190
191  return !username->empty() && !auth_token->empty() && !device_id_.empty();
192}
193
194void UserPolicyIdentityStrategy::OnDeviceTokenAvailable(
195    const std::string& token) {
196  DCHECK(!device_id_.empty());
197  device_token_ = token;
198  cache_->Store(device_token_, device_id_);
199  NotifyDeviceTokenChanged();
200}
201
202std::string UserPolicyIdentityStrategy::GetCurrentUser() {
203#if defined(OS_CHROMEOS)
204  // TODO(mnissler) On CrOS it seems impossible to figure out what user belongs
205  // to a profile. Revisit after multi-profile support landed.
206  return chromeos::UserManager::Get()->logged_in_user().email();
207#else
208  return profile_->GetBrowserSignin()->GetSignedInUsername();
209#endif
210}
211
212void UserPolicyIdentityStrategy::CheckAndTriggerFetch() {
213  if (!GetCurrentUser().empty() &&
214      profile_->GetTokenService()->HasTokenForService(
215          GaiaConstants::kDeviceManagementService)) {
216    // For user tokens, there is no actual identifier. We generate a random
217    // identifier instead each time we ask for the token.
218    device_id_ = guid::GenerateGUID();
219    NotifyAuthChanged();
220  }
221}
222
223void UserPolicyIdentityStrategy::OnCacheLoaded(const std::string& token,
224                                               const std::string& device_id) {
225  if (!token.empty() && !device_id.empty()) {
226    device_token_ = token;
227    device_id_ = device_id;
228    NotifyDeviceTokenChanged();
229  } else {
230    CheckAndTriggerFetch();
231  }
232}
233
234void UserPolicyIdentityStrategy::Observe(NotificationType type,
235                                         const NotificationSource& source,
236                                         const NotificationDetails& details) {
237  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
238  if (type == NotificationType::TOKEN_AVAILABLE) {
239    if (Source<TokenService>(source).ptr() == profile_->GetTokenService()) {
240      const TokenService::TokenAvailableDetails* token_details =
241          Details<const TokenService::TokenAvailableDetails>(details).ptr();
242      if (token_details->service() == GaiaConstants::kDeviceManagementService)
243        if (device_token_.empty()) {
244          // Request a new device management server token, but only in case we
245          // don't already have it.
246          CheckAndTriggerFetch();
247        }
248    }
249#if defined(OS_CHROMEOS)
250  } else if (type == NotificationType::LOGIN_USER_CHANGED) {
251    CheckAndTriggerFetch();
252#else
253  } else if (type == NotificationType::GOOGLE_SIGNIN_SUCCESSFUL) {
254    if (profile_ == Source<Profile>(source).ptr())
255      CheckAndTriggerFetch();
256#endif
257  } else {
258    NOTREACHED();
259  }
260}
261
262}  // namespace policy
263