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