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 "base/message_loop.h" |
8 #include "base/path_service.h" | 8 #include "chrome/browser/policy/cloud_policy_cache.h" |
9 #include "base/singleton.h" | 9 #include "chrome/browser/policy/device_management_service.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" | 10 #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 | 11 |
28 namespace { | 12 namespace { |
29 | 13 |
30 // Domain names that are known not to be managed. | 14 // Retry after 3 seconds (with exponential backoff) after token fetch errors. |
31 // We don't register the device when such a user logs in. | 15 const int64 kTokenFetchErrorDelayMilliseconds = 3 * 1000; |
32 const char* kNonManagedDomains[] = { | 16 // For unmanaged devices, check once per day whether they're still unmanaged. |
33 "@googlemail.com", | 17 const int64 kUnmanagedDeviceRefreshRateMilliseconds = 24 * 60 * 60 * 1000; |
34 "@gmail.com" | |
35 }; | |
36 | 18 |
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 } | 19 } |
53 | 20 |
54 } // namespace | |
55 | |
56 namespace policy { | 21 namespace policy { |
57 | 22 |
58 namespace em = enterprise_management; | 23 namespace em = enterprise_management; |
59 | 24 |
60 DeviceTokenFetcher::ObserverRegistrar::ObserverRegistrar() {} | 25 DeviceTokenFetcher::DeviceTokenFetcher( |
61 | 26 DeviceManagementService* service, |
62 DeviceTokenFetcher::ObserverRegistrar::~ObserverRegistrar() { | 27 CloudPolicyCache* cache) |
63 RemoveAll(); | 28 : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { |
64 } | 29 Initialize(service, |
65 | 30 cache, |
66 void DeviceTokenFetcher::ObserverRegistrar::Init( | 31 kTokenFetchErrorDelayMilliseconds, |
67 DeviceTokenFetcher* token_fetcher) { | 32 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 } | 33 } |
85 | 34 |
86 DeviceTokenFetcher::DeviceTokenFetcher( | 35 DeviceTokenFetcher::DeviceTokenFetcher( |
87 DeviceManagementBackend* backend, | 36 DeviceManagementService* service, |
88 Profile* profile, | 37 CloudPolicyCache* cache, |
89 const FilePath& token_path) | 38 int64 token_fetch_error_delay_ms, |
90 : profile_(profile), | 39 int64 unmanaged_device_refresh_rate_ms) |
91 token_path_(token_path), | 40 : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { |
92 backend_(backend), | 41 Initialize(service, |
93 state_(kStateNotStarted), | 42 cache, |
94 device_token_load_complete_event_(true, false) { | 43 token_fetch_error_delay_ms, |
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 44 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 } | 45 } |
116 | 46 |
117 DeviceTokenFetcher::~DeviceTokenFetcher() {} | 47 DeviceTokenFetcher::~DeviceTokenFetcher() { |
118 | 48 CancelDelayedWork(); |
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 } | 49 } |
147 | 50 |
148 std::string DeviceTokenFetcher::GetCurrentUser() { | 51 void DeviceTokenFetcher::Reset() { |
149 #if defined(OS_CHROMEOS) | 52 SetState(STATE_INACTIVE); |
150 return chromeos::UserManager::Get()->logged_in_user().email(); | 53 backend_.reset(); |
151 #else | 54 } |
152 return profile_->GetBrowserSignin()->GetSignedInUsername(); | 55 |
153 #endif | 56 void DeviceTokenFetcher::FetchToken(const std::string& auth_token, |
57 const std::string& device_id) { | |
58 // Construct a new backend, which will discard any previous requests. | |
danno
2011/02/04 16:01:33
You should validate that the state machine is in t
Jakob Kummerow
2011/02/14 13:50:34
Done.
| |
59 backend_.reset(service_->CreateBackend()); | |
60 auth_token_ = auth_token; | |
61 device_id_ = device_id; | |
62 | |
63 em::DeviceRegisterRequest request; | |
64 backend_->ProcessRegisterRequest(auth_token, device_id, request, this); | |
65 } | |
66 | |
67 const std::string& DeviceTokenFetcher::GetDeviceToken() { | |
68 return device_token_; | |
69 } | |
70 | |
71 void DeviceTokenFetcher::AddObserver(DeviceTokenFetcher::Observer* observer) { | |
72 observer_list_.AddObserver(observer); | |
73 } | |
74 | |
75 void DeviceTokenFetcher::RemoveObserver( | |
76 DeviceTokenFetcher::Observer* observer) { | |
77 observer_list_.RemoveObserver(observer); | |
154 } | 78 } |
155 | 79 |
156 void DeviceTokenFetcher::HandleRegisterResponse( | 80 void DeviceTokenFetcher::HandleRegisterResponse( |
157 const em::DeviceRegisterResponse& response) { | 81 const em::DeviceRegisterResponse& response) { |
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
159 DCHECK_EQ(kStateRequestingDeviceTokenFromServer, state_); | |
160 if (response.has_device_management_token()) { | 82 if (response.has_device_management_token()) { |
161 device_token_ = response.device_management_token(); | 83 device_token_ = response.device_management_token(); |
162 BrowserThread::PostTask( | 84 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 { | 85 } else { |
171 NOTREACHED(); | 86 NOTREACHED(); |
172 SetState(kStateFailure); | 87 SetState(STATE_ERROR); |
173 } | 88 } |
174 } | 89 } |
175 | 90 |
176 void DeviceTokenFetcher::OnError(DeviceManagementBackend::ErrorCode code) { | 91 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) { | 92 if (code == DeviceManagementBackend::kErrorServiceManagementNotSupported) { |
181 device_token_ = std::string(); | 93 cache_->SetDeviceUnmanaged(); |
182 device_id_ = std::string(); | 94 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 } | 95 } |
192 SetState(kStateFailure); | 96 SetState(STATE_ERROR); |
193 } | 97 } |
194 | 98 |
195 void DeviceTokenFetcher::Restart() { | 99 void DeviceTokenFetcher::Initialize(DeviceManagementService* service, |
196 // Complain if there's currently an asynchronous operation going on. | 100 CloudPolicyCache* cache, |
197 DCHECK(state_ == kStateNotStarted || | 101 int64 token_fetch_error_delay_ms, |
198 state_ == kStateHasDeviceToken || | 102 int64 unmanaged_device_refresh_rate_ms) { |
199 state_ == kStateFailure || | 103 service_ = service; |
200 state_ == kStateNotManaged); | 104 cache_ = cache; |
201 device_token_.clear(); | 105 token_fetch_error_delay_ms_ = token_fetch_error_delay_ms; |
202 device_token_load_complete_event_.Reset(); | 106 effective_token_fetch_error_delay_ms_ = token_fetch_error_delay_ms; |
203 MakeReadyToRequestDeviceToken(); | 107 unmanaged_device_refresh_rate_ms_ = unmanaged_device_refresh_rate_ms; |
108 state_ = STATE_INACTIVE; | |
109 delayed_work_task_ = NULL; | |
110 | |
111 if (cache_->is_device_unmanaged()) | |
112 SetState(STATE_UNMANAGED); | |
204 } | 113 } |
205 | 114 |
206 void DeviceTokenFetcher::StartFetching() { | 115 void DeviceTokenFetcher::SetState(FetcherState state) { |
danno
2011/02/04 16:01:33
Please include state diagram in header documentati
Jakob Kummerow
2011/02/14 13:50:34
Done.
| |
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 116 state_ = state; |
208 if (state_ == kStateNotStarted) { | 117 if (state_ != STATE_ERROR) |
209 SetState(kStateLoadDeviceTokenFromDisk); | 118 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 | 119 |
211 // FILE thread. | 120 base::Time delayed_work_at; |
212 BrowserThread::PostTask( | 121 switch (state_) { |
213 BrowserThread::FILE, | 122 case STATE_INACTIVE: |
214 FROM_HERE, | 123 device_token_.clear(); |
215 NewRunnableMethod(this, | 124 auth_token_.clear(); |
216 &DeviceTokenFetcher::AttemptTokenLoadFromDisk)); | 125 device_id_.clear(); |
126 break; | |
127 case STATE_TOKEN_AVAILABLE: | |
128 FOR_EACH_OBSERVER(Observer, observer_list_, OnTokenAvailable()); | |
129 break; | |
130 case STATE_UNMANAGED: | |
131 delayed_work_at = cache_->last_policy_refresh_time() + | |
132 base::TimeDelta::FromMilliseconds(unmanaged_device_refresh_rate_ms_); | |
133 break; | |
134 case STATE_ERROR: | |
135 delayed_work_at = cache_->last_policy_refresh_time() + | |
Jakob Kummerow
2011/02/07 16:00:09
I think this should be s/cache_->last_policy_refre
Jakob Kummerow
2011/02/14 13:50:34
Done.
| |
136 base::TimeDelta::FromMilliseconds( | |
137 effective_token_fetch_error_delay_ms_); | |
138 effective_token_fetch_error_delay_ms_ *= 2; | |
139 break; | |
140 } | |
141 | |
142 CancelDelayedWork(); | |
143 if (!delayed_work_at.is_null()) { | |
144 base::Time now(base::Time::Now()); | |
145 int64 delay = std::max<int64>((delayed_work_at - now).InMilliseconds(), 0); | |
146 delayed_work_task_ = | |
147 method_factory_.NewRunnableMethod(&DeviceTokenFetcher::DoDelayedWork); | |
148 MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_work_task_, | |
149 delay); | |
217 } | 150 } |
218 } | 151 } |
219 | 152 |
220 void DeviceTokenFetcher::AttemptTokenLoadFromDisk() { | 153 void DeviceTokenFetcher::DoDelayedWork() { |
danno
2011/02/04 16:01:33
Please include state diagram in header documentati
Jakob Kummerow
2011/02/14 13:50:34
Done.
| |
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 154 DCHECK(delayed_work_task_); |
222 if (file_util::PathExists(token_path_)) { | 155 delayed_work_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 | 156 |
241 BrowserThread::PostTask( | 157 switch (state_) { |
242 BrowserThread::UI, | 158 case STATE_INACTIVE: |
243 FROM_HERE, | 159 case STATE_TOKEN_AVAILABLE: |
244 NewRunnableMethod(this, | 160 break; |
245 &DeviceTokenFetcher::MakeReadyToRequestDeviceToken)); | 161 case STATE_UNMANAGED: |
246 } | 162 case STATE_ERROR: |
247 | 163 if (!auth_token_.empty() && !device_id_.empty()) |
248 void DeviceTokenFetcher::MakeReadyToRequestDeviceToken() { | 164 FetchToken(auth_token_, device_id_); |
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 165 else |
250 SetState(kStateReadyToRequestDeviceTokenFromServer); | 166 NOTREACHED(); |
251 SendServerRequestIfPossible(); | 167 break; |
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::CancelDelayedWork() { |
275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 172 if (delayed_work_task_) { |
276 return !device_token_load_complete_event_.IsSignaled(); | 173 delayed_work_task_->Cancel(); |
277 } | 174 delayed_work_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 |