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