| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013 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/component_cloud_policy_service.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/location.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/message_loop_proxy.h" | |
| 12 #include "base/pickle.h" | |
| 13 #include "base/sequenced_task_runner.h" | |
| 14 #include "base/stl_util.h" | |
| 15 #include "chrome/browser/policy/component_cloud_policy_store.h" | |
| 16 #include "chrome/browser/policy/component_cloud_policy_updater.h" | |
| 17 #include "chrome/browser/policy/proto/device_management_backend.pb.h" | |
| 18 #include "chrome/browser/policy/resource_cache.h" | |
| 19 #include "content/public/browser/browser_thread.h" | |
| 20 #include "net/url_request/url_request_context_getter.h" | |
| 21 | |
| 22 namespace em = enterprise_management; | |
| 23 | |
| 24 namespace policy { | |
| 25 | |
| 26 const char ComponentCloudPolicyService::kComponentNamespaceCache[] = | |
| 27 "component-namespace-cache"; | |
| 28 | |
| 29 ComponentCloudPolicyService::Delegate::~Delegate() {} | |
| 30 | |
| 31 // Owns the objects that live on the background thread, and posts back to UI | |
| 32 // to the service whenever the policy changes. | |
| 33 class ComponentCloudPolicyService::Backend | |
| 34 : public ComponentCloudPolicyStore::Delegate { | |
| 35 public: | |
| 36 Backend(base::WeakPtr<ComponentCloudPolicyService> service, | |
| 37 scoped_refptr<base::SequencedTaskRunner> task_runner, | |
| 38 scoped_ptr<ResourceCache> cache); | |
| 39 ~Backend(); | |
| 40 | |
| 41 // This is invoked right after the constructor but on the backend background | |
| 42 // thread. Used to create the store on the right thread. | |
| 43 void Init(); | |
| 44 | |
| 45 // Reads the initial list of components and the initial policy. | |
| 46 void FinalizeInit(); | |
| 47 | |
| 48 // Creates the backend updater. | |
| 49 void Connect(scoped_refptr<net::URLRequestContextGetter> request_context); | |
| 50 | |
| 51 // Stops updating remote data. Cached policies are still served. | |
| 52 void Disconnect(); | |
| 53 | |
| 54 // Loads the initial policies from the store. |username| and |dm_token| are | |
| 55 // used to validate the cached policies. | |
| 56 void SetCredentials(const std::string& username, const std::string& dm_token); | |
| 57 | |
| 58 // Passes a policy protobuf to the backend, to start its validation and | |
| 59 // eventual download of the policy data on the background thread. | |
| 60 // This is ignored if the backend isn't connected. | |
| 61 void UpdateExternalPolicy(scoped_ptr<em::PolicyFetchResponse> response); | |
| 62 | |
| 63 // ComponentCloudPolicyStore::Delegate implementation: | |
| 64 virtual void OnComponentCloudPolicyStoreUpdated() OVERRIDE; | |
| 65 | |
| 66 // Passes the current list of components in |domain|, so that the disk cache | |
| 67 // can purge components that aren't being tracked anymore. | |
| 68 void SetCurrentComponents(PolicyDomain domain, const StringSet* components); | |
| 69 | |
| 70 private: | |
| 71 scoped_ptr<ComponentMap> ReadCachedComponents(); | |
| 72 | |
| 73 base::WeakPtr<ComponentCloudPolicyService> service_; | |
| 74 scoped_refptr<base::SequencedTaskRunner> task_runner_; | |
| 75 scoped_ptr<ResourceCache> cache_; | |
| 76 scoped_ptr<ComponentCloudPolicyStore> store_; | |
| 77 scoped_ptr<ComponentCloudPolicyUpdater> updater_; | |
| 78 | |
| 79 DISALLOW_COPY_AND_ASSIGN(Backend); | |
| 80 }; | |
| 81 | |
| 82 ComponentCloudPolicyService::Backend::Backend( | |
| 83 base::WeakPtr<ComponentCloudPolicyService> service, | |
| 84 scoped_refptr<base::SequencedTaskRunner> task_runner, | |
| 85 scoped_ptr<ResourceCache> cache) | |
| 86 : service_(service), | |
| 87 task_runner_(task_runner), | |
| 88 cache_(cache.Pass()) {} | |
| 89 | |
| 90 ComponentCloudPolicyService::Backend::~Backend() {} | |
| 91 | |
| 92 void ComponentCloudPolicyService::Backend::Init() { | |
| 93 DCHECK(!store_); | |
| 94 store_.reset(new ComponentCloudPolicyStore(this, cache_.get())); | |
| 95 } | |
| 96 | |
| 97 void ComponentCloudPolicyService::Backend::FinalizeInit() { | |
| 98 // Read the components that were cached in the last SetCurrentComponents() | |
| 99 // calls for each domain. | |
| 100 scoped_ptr<ComponentMap> components = ReadCachedComponents(); | |
| 101 | |
| 102 // Read the initial policy. | |
| 103 store_->Load(); | |
| 104 scoped_ptr<PolicyBundle> policy(new PolicyBundle); | |
| 105 policy->CopyFrom(store_->policy()); | |
| 106 | |
| 107 content::BrowserThread::PostTask( | |
| 108 content::BrowserThread::UI, FROM_HERE, | |
| 109 base::Bind(&ComponentCloudPolicyService::OnBackendInitialized, | |
| 110 service_, | |
| 111 base::Passed(&components), | |
| 112 base::Passed(&policy))); | |
| 113 } | |
| 114 | |
| 115 void ComponentCloudPolicyService::Backend::SetCredentials( | |
| 116 const std::string& username, | |
| 117 const std::string& dm_token) { | |
| 118 store_->SetCredentials(username, dm_token); | |
| 119 } | |
| 120 | |
| 121 void ComponentCloudPolicyService::Backend::Connect( | |
| 122 scoped_refptr<net::URLRequestContextGetter> request_context) { | |
| 123 updater_.reset(new ComponentCloudPolicyUpdater( | |
| 124 task_runner_, request_context, store_.get())); | |
| 125 } | |
| 126 | |
| 127 void ComponentCloudPolicyService::Backend::Disconnect() { | |
| 128 updater_.reset(); | |
| 129 } | |
| 130 | |
| 131 void ComponentCloudPolicyService::Backend::UpdateExternalPolicy( | |
| 132 scoped_ptr<em::PolicyFetchResponse> response) { | |
| 133 if (updater_) | |
| 134 updater_->UpdateExternalPolicy(response.Pass()); | |
| 135 } | |
| 136 | |
| 137 void ComponentCloudPolicyService::Backend:: | |
| 138 OnComponentCloudPolicyStoreUpdated() { | |
| 139 scoped_ptr<PolicyBundle> bundle(new PolicyBundle); | |
| 140 bundle->CopyFrom(store_->policy()); | |
| 141 content::BrowserThread::PostTask( | |
| 142 content::BrowserThread::UI, FROM_HERE, | |
| 143 base::Bind(&ComponentCloudPolicyService::OnPolicyUpdated, | |
| 144 service_, | |
| 145 base::Passed(&bundle))); | |
| 146 } | |
| 147 | |
| 148 void ComponentCloudPolicyService::Backend::SetCurrentComponents( | |
| 149 PolicyDomain domain, | |
| 150 const StringSet* components) { | |
| 151 // Store the current list of components in the cache. | |
| 152 std::string policy_type; | |
| 153 if (ComponentCloudPolicyStore::GetPolicyType(domain, &policy_type)) { | |
| 154 Pickle pickle; | |
| 155 for (StringSet::const_iterator it = components->begin(); | |
| 156 it != components->end(); ++it) { | |
| 157 pickle.WriteString(*it); | |
| 158 } | |
| 159 std::string data(reinterpret_cast<const char*>(pickle.data()), | |
| 160 pickle.size()); | |
| 161 cache_->Store(kComponentNamespaceCache, policy_type, data); | |
| 162 } | |
| 163 | |
| 164 // Purge any components that have been removed. | |
| 165 if (store_) | |
| 166 store_->Purge(domain, *components); | |
| 167 } | |
| 168 | |
| 169 scoped_ptr<ComponentCloudPolicyService::ComponentMap> | |
| 170 ComponentCloudPolicyService::Backend::ReadCachedComponents() { | |
| 171 scoped_ptr<ComponentMap> components(new ComponentMap); | |
| 172 std::map<std::string, std::string> contents; | |
| 173 cache_->LoadAllSubkeys(kComponentNamespaceCache, &contents); | |
| 174 for (std::map<std::string, std::string>::iterator it = contents.begin(); | |
| 175 it != contents.end(); ++it) { | |
| 176 PolicyDomain domain; | |
| 177 if (ComponentCloudPolicyStore::GetPolicyDomain(it->first, &domain)) { | |
| 178 StringSet& set = (*components)[domain]; | |
| 179 const Pickle pickle(it->second.data(), it->second.size()); | |
| 180 PickleIterator pickit(pickle); | |
| 181 std::string id; | |
| 182 while (pickit.ReadString(&id)) | |
| 183 set.insert(id); | |
| 184 } else { | |
| 185 cache_->Delete(kComponentNamespaceCache, it->first); | |
| 186 } | |
| 187 } | |
| 188 return components.Pass(); | |
| 189 } | |
| 190 | |
| 191 ComponentCloudPolicyService::ComponentCloudPolicyService( | |
| 192 Delegate* delegate, | |
| 193 CloudPolicyStore* store, | |
| 194 scoped_ptr<ResourceCache> cache) | |
| 195 : delegate_(delegate), | |
| 196 backend_(NULL), | |
| 197 client_(NULL), | |
| 198 store_(store), | |
| 199 is_initialized_(false), | |
| 200 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
| 201 store_->AddObserver(this); | |
| 202 | |
| 203 // TODO(joaodasilva): this can't currently live on the blocking pool because | |
| 204 // creating URLFetchers requires a MessageLoop. | |
| 205 backend_task_runner_ = content::BrowserThread::GetMessageLoopProxyForThread( | |
| 206 content::BrowserThread::FILE); | |
| 207 backend_ = new Backend(weak_ptr_factory_.GetWeakPtr(), | |
| 208 backend_task_runner_, | |
| 209 cache.Pass()); | |
| 210 backend_task_runner_->PostTask( | |
| 211 FROM_HERE, base::Bind(&Backend::Init, base::Unretained(backend_))); | |
| 212 | |
| 213 if (store_->is_initialized()) | |
| 214 InitializeBackend(); | |
| 215 } | |
| 216 | |
| 217 ComponentCloudPolicyService::~ComponentCloudPolicyService() { | |
| 218 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 219 store_->RemoveObserver(this); | |
| 220 if (client_) | |
| 221 client_->RemoveObserver(this); | |
| 222 backend_task_runner_->DeleteSoon(FROM_HERE, backend_); | |
| 223 backend_ = NULL; | |
| 224 } | |
| 225 | |
| 226 // static | |
| 227 bool ComponentCloudPolicyService::SupportsDomain(PolicyDomain domain) { | |
| 228 return ComponentCloudPolicyStore::SupportsDomain(domain); | |
| 229 } | |
| 230 | |
| 231 void ComponentCloudPolicyService::Connect( | |
| 232 CloudPolicyClient* client, | |
| 233 scoped_refptr<net::URLRequestContextGetter> request_context) { | |
| 234 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 235 DCHECK(!client_); | |
| 236 client_ = client; | |
| 237 client_->AddObserver(this); | |
| 238 // Create the updater in the backend. | |
| 239 backend_task_runner_->PostTask(FROM_HERE, | |
| 240 base::Bind(&Backend::Connect, | |
| 241 base::Unretained(backend_), | |
| 242 request_context)); | |
| 243 if (is_initialized()) | |
| 244 InitializeClient(); | |
| 245 } | |
| 246 | |
| 247 void ComponentCloudPolicyService::Disconnect() { | |
| 248 if (client_) { | |
| 249 // Unregister all the current components. | |
| 250 for (ComponentMap::iterator it = registered_components_.begin(); | |
| 251 it != registered_components_.end(); ++it) { | |
| 252 RemoveNamespacesToFetch(it->first, it->second); | |
| 253 } | |
| 254 | |
| 255 client_->RemoveObserver(this); | |
| 256 client_ = NULL; | |
| 257 | |
| 258 backend_task_runner_->PostTask( | |
| 259 FROM_HERE, | |
| 260 base::Bind(&Backend::Disconnect, base::Unretained(backend_))); | |
| 261 } | |
| 262 } | |
| 263 | |
| 264 void ComponentCloudPolicyService::RegisterPolicyDomain( | |
| 265 PolicyDomain domain, | |
| 266 const std::set<std::string>& current_ids) { | |
| 267 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 268 DCHECK(SupportsDomain(domain)); | |
| 269 | |
| 270 // Send the new set to the backend, to purge the cache. | |
| 271 backend_task_runner_->PostTask( | |
| 272 FROM_HERE, | |
| 273 base::Bind(&Backend::SetCurrentComponents, | |
| 274 base::Unretained(backend_), | |
| 275 domain, | |
| 276 base::Owned(new StringSet(current_ids)))); | |
| 277 | |
| 278 // Register the current list of components for |domain| at the |client_|. | |
| 279 StringSet& registered_ids = registered_components_[domain]; | |
| 280 if (client_ && is_initialized()) { | |
| 281 if (UpdateClientNamespaces(domain, registered_ids, current_ids)) | |
| 282 delegate_->OnComponentCloudPolicyRefreshNeeded(); | |
| 283 } | |
| 284 registered_ids = current_ids; | |
| 285 } | |
| 286 | |
| 287 void ComponentCloudPolicyService::OnPolicyFetched(CloudPolicyClient* client) { | |
| 288 DCHECK_EQ(client_, client); | |
| 289 // Pass each PolicyFetchResponse whose policy type is registered to the | |
| 290 // Backend. | |
| 291 const CloudPolicyClient::ResponseMap& responses = client_->responses(); | |
| 292 for (CloudPolicyClient::ResponseMap::const_iterator it = responses.begin(); | |
| 293 it != responses.end(); ++it) { | |
| 294 const PolicyNamespaceKey& key(it->first); | |
| 295 PolicyDomain domain; | |
| 296 if (ComponentCloudPolicyStore::GetPolicyDomain(key.first, &domain) && | |
| 297 ContainsKey(registered_components_[domain], key.second)) { | |
| 298 scoped_ptr<em::PolicyFetchResponse> response( | |
| 299 new em::PolicyFetchResponse(*it->second)); | |
| 300 backend_task_runner_->PostTask(FROM_HERE, | |
| 301 base::Bind(&Backend::UpdateExternalPolicy, | |
| 302 base::Unretained(backend_), | |
| 303 base::Passed(&response))); | |
| 304 } | |
| 305 } | |
| 306 } | |
| 307 | |
| 308 void ComponentCloudPolicyService::OnRegistrationStateChanged( | |
| 309 CloudPolicyClient* client) { | |
| 310 // Ignored. | |
| 311 } | |
| 312 | |
| 313 void ComponentCloudPolicyService::OnClientError(CloudPolicyClient* client) { | |
| 314 // Ignored. | |
| 315 } | |
| 316 | |
| 317 void ComponentCloudPolicyService::OnStoreLoaded(CloudPolicyStore* store) { | |
| 318 DCHECK_EQ(store_, store); | |
| 319 if (store_->is_initialized()) { | |
| 320 if (is_initialized()) { | |
| 321 // The backend is already initialized; update the credentials, in case | |
| 322 // a new dmtoken or server key became available. | |
| 323 SetCredentialsAndReloadClient(); | |
| 324 } else { | |
| 325 // The |store_| just became ready; initialize the backend now. | |
| 326 InitializeBackend(); | |
| 327 } | |
| 328 } | |
| 329 } | |
| 330 | |
| 331 void ComponentCloudPolicyService::OnStoreError(CloudPolicyStore* store) { | |
| 332 OnStoreLoaded(store); | |
| 333 } | |
| 334 | |
| 335 void ComponentCloudPolicyService::InitializeBackend() { | |
| 336 DCHECK(!is_initialized()); | |
| 337 DCHECK(store_->is_initialized()); | |
| 338 | |
| 339 // Set the credentials for the initial policy load, if available. | |
| 340 SetCredentialsAndReloadClient(); | |
| 341 | |
| 342 backend_task_runner_->PostTask( | |
| 343 FROM_HERE, | |
| 344 base::Bind(&Backend::FinalizeInit, base::Unretained(backend_))); | |
| 345 } | |
| 346 | |
| 347 void ComponentCloudPolicyService::OnBackendInitialized( | |
| 348 scoped_ptr<ComponentMap> cached_components, | |
| 349 scoped_ptr<PolicyBundle> initial_policy) { | |
| 350 // InitializeBackend() may be called multiple times if the |store_| fires | |
| 351 // events while the backend is loading. | |
| 352 if (is_initialized()) | |
| 353 return; | |
| 354 | |
| 355 // RegisterPolicyDomain() may have been called while the backend was | |
| 356 // initializing; only update |registered_components_| from |cached_components| | |
| 357 // for domains that haven't registered yet. | |
| 358 for (ComponentMap::iterator it = cached_components->begin(); | |
| 359 it != cached_components->end(); ++it) { | |
| 360 // Lookup without inserting an empty set. | |
| 361 if (registered_components_.find(it->first) != registered_components_.end()) | |
| 362 continue; // Ignore the cached list if a more recent one was registered. | |
| 363 registered_components_[it->first].swap(it->second); | |
| 364 } | |
| 365 | |
| 366 // A client may have already connected while the backend was initializing. | |
| 367 if (client_) | |
| 368 InitializeClient(); | |
| 369 | |
| 370 // Set the initial policy, and send the initial update callback. | |
| 371 is_initialized_ = true; | |
| 372 OnPolicyUpdated(initial_policy.Pass()); | |
| 373 } | |
| 374 | |
| 375 void ComponentCloudPolicyService::InitializeClient() { | |
| 376 // Register all the current components. | |
| 377 bool added = false; | |
| 378 for (ComponentMap::iterator it = registered_components_.begin(); | |
| 379 it != registered_components_.end(); ++it) { | |
| 380 added |= !it->second.empty(); | |
| 381 AddNamespacesToFetch(it->first, it->second); | |
| 382 } | |
| 383 // The client may already have PolicyFetchResponses for registered components; | |
| 384 // load them now. | |
| 385 OnPolicyFetched(client_); | |
| 386 if (added && is_initialized()) | |
| 387 delegate_->OnComponentCloudPolicyRefreshNeeded(); | |
| 388 } | |
| 389 | |
| 390 void ComponentCloudPolicyService::OnPolicyUpdated( | |
| 391 scoped_ptr<PolicyBundle> policy) { | |
| 392 policy_.Swap(policy.get()); | |
| 393 // Don't propagate updates until the initial store Load() has been done. | |
| 394 if (is_initialized()) | |
| 395 delegate_->OnComponentCloudPolicyUpdated(); | |
| 396 } | |
| 397 | |
| 398 void ComponentCloudPolicyService::SetCredentialsAndReloadClient() { | |
| 399 const em::PolicyData* policy = store_->policy(); | |
| 400 if (!policy || !policy->has_username() || !policy->has_request_token()) | |
| 401 return; | |
| 402 backend_task_runner_->PostTask(FROM_HERE, | |
| 403 base::Bind(&Backend::SetCredentials, | |
| 404 base::Unretained(backend_), | |
| 405 policy->username(), | |
| 406 policy->request_token())); | |
| 407 // If this was the initial register, or if the signing key changed, then the | |
| 408 // previous OnPolicyFetched() call had its PolicyFetchResponses rejected | |
| 409 // because the credentials weren't updated yet. Reload all the responses in | |
| 410 // the client now to handle those cases; if those responses have already been | |
| 411 // validated then they will be ignored. | |
| 412 if (client_) | |
| 413 OnPolicyFetched(client_); | |
| 414 } | |
| 415 | |
| 416 bool ComponentCloudPolicyService::UpdateClientNamespaces( | |
| 417 PolicyDomain domain, | |
| 418 const StringSet& old_set, | |
| 419 const StringSet& new_set) { | |
| 420 StringSet added = base::STLSetDifference<StringSet>(new_set, old_set); | |
| 421 StringSet removed = base::STLSetDifference<StringSet>(old_set, new_set); | |
| 422 AddNamespacesToFetch(domain, added); | |
| 423 RemoveNamespacesToFetch(domain, removed); | |
| 424 return !added.empty(); | |
| 425 } | |
| 426 | |
| 427 void ComponentCloudPolicyService::AddNamespacesToFetch(PolicyDomain domain, | |
| 428 const StringSet& set) { | |
| 429 std::string policy_type; | |
| 430 if (ComponentCloudPolicyStore::GetPolicyType(domain, &policy_type)) { | |
| 431 for (StringSet::const_iterator it = set.begin(); it != set.end(); ++it) | |
| 432 client_->AddNamespaceToFetch(PolicyNamespaceKey(policy_type, *it)); | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 void ComponentCloudPolicyService::RemoveNamespacesToFetch( | |
| 437 PolicyDomain domain, | |
| 438 const StringSet& set) { | |
| 439 std::string policy_type; | |
| 440 if (ComponentCloudPolicyStore::GetPolicyType(domain, &policy_type)) { | |
| 441 for (StringSet::const_iterator it = set.begin(); it != set.end(); ++it) | |
| 442 client_->RemoveNamespaceToFetch(PolicyNamespaceKey(policy_type, *it)); | |
| 443 } | |
| 444 } | |
| 445 | |
| 446 } // namespace policy | |
| OLD | NEW |