Chromium Code Reviews| 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::Reset() { |
| 149 #if defined(OS_CHROMEOS) | 54 SetState(STATE_INACTIVE); |
| 150 return chromeos::UserManager::Get()->logged_in_user().email(); | 55 backend_.reset(); |
| 151 #else | 56 } |
| 152 return profile_->GetBrowserSignin()->GetSignedInUsername(); | 57 |
| 153 #endif | 58 void DeviceTokenFetcher::FetchToken(const std::string& auth_token, |
| 59 const std::string& device_id) { | |
|
Mattias Nissler (ping if slow)
2011/02/15 10:15:16
Should we put a call to Reset() here?
Jakob Kummerow
2011/02/21 12:12:15
No need. In fact, as discussed offline I've remove
| |
| 60 auth_token_ = auth_token; | |
| 61 device_id_ = device_id; | |
| 62 FetchTokenInternal(); | |
| 63 } | |
| 64 | |
| 65 void DeviceTokenFetcher::FetchTokenInternal() { | |
| 66 DCHECK(state_ != STATE_TOKEN_AVAILABLE); | |
| 67 DCHECK(!auth_token_.empty() && !device_id_.empty()); | |
| 68 // Construct a new backend, which will discard any previous requests. | |
| 69 backend_.reset(service_->CreateBackend()); | |
| 70 em::DeviceRegisterRequest request; | |
| 71 backend_->ProcessRegisterRequest(auth_token_, device_id_, request, this); | |
| 72 } | |
| 73 | |
| 74 const std::string& DeviceTokenFetcher::GetDeviceToken() { | |
| 75 return device_token_; | |
| 76 } | |
| 77 | |
| 78 void DeviceTokenFetcher::AddObserver(DeviceTokenFetcher::Observer* observer) { | |
| 79 observer_list_.AddObserver(observer); | |
| 80 } | |
| 81 | |
| 82 void DeviceTokenFetcher::RemoveObserver( | |
| 83 DeviceTokenFetcher::Observer* observer) { | |
| 84 observer_list_.RemoveObserver(observer); | |
| 154 } | 85 } |
| 155 | 86 |
| 156 void DeviceTokenFetcher::HandleRegisterResponse( | 87 void DeviceTokenFetcher::HandleRegisterResponse( |
| 157 const em::DeviceRegisterResponse& response) { | 88 const em::DeviceRegisterResponse& response) { |
| 158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 159 DCHECK_EQ(kStateRequestingDeviceTokenFromServer, state_); | |
| 160 if (response.has_device_management_token()) { | 89 if (response.has_device_management_token()) { |
| 161 device_token_ = response.device_management_token(); | 90 device_token_ = response.device_management_token(); |
| 162 BrowserThread::PostTask( | 91 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 { | 92 } else { |
| 171 NOTREACHED(); | 93 NOTREACHED(); |
| 172 SetState(kStateFailure); | 94 SetState(STATE_ERROR); |
| 173 } | 95 } |
| 174 } | 96 } |
| 175 | 97 |
| 176 void DeviceTokenFetcher::OnError(DeviceManagementBackend::ErrorCode code) { | 98 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) { | 99 if (code == DeviceManagementBackend::kErrorServiceManagementNotSupported) { |
| 181 device_token_ = std::string(); | 100 cache_->SetUnmanaged(); |
| 182 device_id_ = std::string(); | 101 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 } | 102 } |
| 192 SetState(kStateFailure); | 103 SetState(STATE_ERROR); |
| 193 } | 104 } |
| 194 | 105 |
| 195 void DeviceTokenFetcher::Restart() { | 106 void DeviceTokenFetcher::Initialize(DeviceManagementService* service, |
| 196 // Complain if there's currently an asynchronous operation going on. | 107 CloudPolicyCache* cache, |
| 197 DCHECK(state_ == kStateNotStarted || | 108 int64 token_fetch_error_delay_ms, |
| 198 state_ == kStateHasDeviceToken || | 109 int64 unmanaged_device_refresh_rate_ms) { |
| 199 state_ == kStateFailure || | 110 service_ = service; |
| 200 state_ == kStateNotManaged); | 111 cache_ = cache; |
| 201 device_token_.clear(); | 112 token_fetch_error_delay_ms_ = token_fetch_error_delay_ms; |
| 202 device_token_load_complete_event_.Reset(); | 113 effective_token_fetch_error_delay_ms_ = token_fetch_error_delay_ms; |
| 203 MakeReadyToRequestDeviceToken(); | 114 unmanaged_device_refresh_rate_ms_ = unmanaged_device_refresh_rate_ms; |
| 115 state_ = STATE_INACTIVE; | |
| 116 retry_task_ = NULL; | |
| 117 | |
| 118 if (cache_->is_unmanaged()) | |
| 119 SetState(STATE_UNMANAGED); | |
| 204 } | 120 } |
| 205 | 121 |
| 206 void DeviceTokenFetcher::StartFetching() { | 122 void DeviceTokenFetcher::SetState(FetcherState state) { |
| 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 123 state_ = state; |
| 208 if (state_ == kStateNotStarted) { | 124 if (state_ != STATE_ERROR) |
| 209 SetState(kStateLoadDeviceTokenFromDisk); | 125 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 | 126 |
| 211 // FILE thread. | 127 base::Time delayed_work_at; |
| 212 BrowserThread::PostTask( | 128 switch (state_) { |
| 213 BrowserThread::FILE, | 129 case STATE_INACTIVE: |
| 214 FROM_HERE, | 130 device_token_.clear(); |
| 215 NewRunnableMethod(this, | 131 auth_token_.clear(); |
| 216 &DeviceTokenFetcher::AttemptTokenLoadFromDisk)); | 132 device_id_.clear(); |
| 133 break; | |
| 134 case STATE_TOKEN_AVAILABLE: | |
| 135 FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceTokenAvailable()); | |
| 136 break; | |
| 137 case STATE_UNMANAGED: | |
| 138 delayed_work_at = cache_->last_policy_refresh_time() + | |
| 139 base::TimeDelta::FromMilliseconds(unmanaged_device_refresh_rate_ms_); | |
| 140 break; | |
| 141 case STATE_ERROR: | |
| 142 delayed_work_at = base::Time::Now() + | |
| 143 base::TimeDelta::FromMilliseconds( | |
| 144 effective_token_fetch_error_delay_ms_); | |
| 145 effective_token_fetch_error_delay_ms_ *= 2; | |
| 146 break; | |
| 147 } | |
| 148 | |
| 149 CancelRetryTask(); | |
| 150 if (!delayed_work_at.is_null()) { | |
| 151 base::Time now(base::Time::Now()); | |
| 152 int64 delay = std::max<int64>((delayed_work_at - now).InMilliseconds(), 0); | |
| 153 retry_task_ = method_factory_.NewRunnableMethod( | |
| 154 &DeviceTokenFetcher::ExecuteRetryTask); | |
| 155 MessageLoop::current()->PostDelayedTask(FROM_HERE, retry_task_, | |
| 156 delay); | |
| 217 } | 157 } |
| 218 } | 158 } |
| 219 | 159 |
| 220 void DeviceTokenFetcher::AttemptTokenLoadFromDisk() { | 160 void DeviceTokenFetcher::ExecuteRetryTask() { |
| 221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 161 DCHECK(retry_task_); |
| 222 if (file_util::PathExists(token_path_)) { | 162 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 | 163 |
| 241 BrowserThread::PostTask( | 164 switch (state_) { |
| 242 BrowserThread::UI, | 165 case STATE_INACTIVE: |
| 243 FROM_HERE, | 166 case STATE_TOKEN_AVAILABLE: |
| 244 NewRunnableMethod(this, | 167 break; |
| 245 &DeviceTokenFetcher::MakeReadyToRequestDeviceToken)); | 168 case STATE_UNMANAGED: |
| 246 } | 169 case STATE_ERROR: |
| 247 | 170 FetchTokenInternal(); |
| 248 void DeviceTokenFetcher::MakeReadyToRequestDeviceToken() { | 171 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 } | 172 } |
| 272 } | 173 } |
| 273 | 174 |
| 274 bool DeviceTokenFetcher::IsTokenPending() { | 175 void DeviceTokenFetcher::CancelRetryTask() { |
| 275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 176 if (retry_task_) { |
| 276 return !device_token_load_complete_event_.IsSignaled(); | 177 retry_task_->Cancel(); |
| 277 } | 178 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 } | 179 } |
| 309 } | 180 } |
| 310 | 181 |
| 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 | 182 } // namespace policy |
| OLD | NEW |