component_cloud_policy_service.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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 "components/policy/core/common/cloud/component_cloud_policy_service.h" 6 7#include <string> 8 9#include "base/bind.h" 10#include "base/bind_helpers.h" 11#include "base/location.h" 12#include "base/logging.h" 13#include "base/message_loop/message_loop_proxy.h" 14#include "base/sequenced_task_runner.h" 15#include "components/policy/core/common/cloud/cloud_policy_constants.h" 16#include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h" 17#include "components/policy/core/common/cloud/component_cloud_policy_store.h" 18#include "components/policy/core/common/cloud/component_cloud_policy_updater.h" 19#include "components/policy/core/common/cloud/external_policy_data_fetcher.h" 20#include "components/policy/core/common/cloud/resource_cache.h" 21#include "components/policy/core/common/schema.h" 22#include "components/policy/core/common/schema_map.h" 23#include "net/url_request/url_request_context_getter.h" 24#include "policy/proto/device_management_backend.pb.h" 25 26namespace em = enterprise_management; 27 28namespace policy { 29 30namespace { 31 32bool NotInSchemaMap(const scoped_refptr<SchemaMap> schema_map, 33 PolicyDomain domain, 34 const std::string& component_id) { 35 return schema_map->GetSchema(PolicyNamespace(domain, component_id)) == NULL; 36} 37 38bool ToPolicyNamespace(const PolicyNamespaceKey& key, PolicyNamespace* ns) { 39 if (!ComponentCloudPolicyStore::GetPolicyDomain(key.first, &ns->domain)) 40 return false; 41 ns->component_id = key.second; 42 return true; 43} 44 45} // namespace 46 47ComponentCloudPolicyService::Delegate::~Delegate() {} 48 49// Owns the objects that live on the background thread, and posts back to the 50// thread that the ComponentCloudPolicyService runs on whenever the policy 51// changes. 52class ComponentCloudPolicyService::Backend 53 : public ComponentCloudPolicyStore::Delegate { 54 public: 55 // This class can be instantiated on any thread but from then on, may be 56 // accessed via the |task_runner_| only. Policy changes are posted to the 57 // |service| via the |service_task_runner|. The |cache| is used to load and 58 // store local copies of the downloaded policies. 59 Backend(base::WeakPtr<ComponentCloudPolicyService> service, 60 scoped_refptr<base::SequencedTaskRunner> task_runner, 61 scoped_refptr<base::SequencedTaskRunner> service_task_runner, 62 scoped_ptr<ResourceCache> cache, 63 scoped_ptr<ExternalPolicyDataFetcher> external_policy_data_fetcher); 64 65 virtual ~Backend(); 66 67 // |username| and |dm_token| will be used to validate the cached policies. 68 void SetCredentials(const std::string& username, const std::string& dm_token); 69 70 // Loads the |store_| and starts downloading updates. 71 void Init(scoped_refptr<SchemaMap> schema_map); 72 73 // Passes a policy protobuf to the backend, to start its validation and 74 // eventual download of the policy data on the background thread. 75 void UpdateExternalPolicy(scoped_ptr<em::PolicyFetchResponse> response); 76 77 // ComponentCloudPolicyStore::Delegate implementation: 78 virtual void OnComponentCloudPolicyStoreUpdated() OVERRIDE; 79 80 // Passes the current SchemaMap so that the disk cache can purge components 81 // that aren't being tracked anymore. 82 // |removed| is a list of namespaces that were present in the previous 83 // schema and have been removed in the updated version. 84 void OnSchemasUpdated(scoped_refptr<SchemaMap> schema_map, 85 scoped_ptr<PolicyNamespaceList> removed); 86 87 private: 88 // The ComponentCloudPolicyService that owns |this|. Used to inform the 89 // |service_| when policy changes. 90 base::WeakPtr<ComponentCloudPolicyService> service_; 91 92 // The thread that |this| runs on. Used to post tasks to be run by |this|. 93 scoped_refptr<base::SequencedTaskRunner> task_runner_; 94 95 // The thread that the |service_| runs on. Used to post policy changes to the 96 // right thread. 97 scoped_refptr<base::SequencedTaskRunner> service_task_runner_; 98 99 scoped_ptr<ResourceCache> cache_; 100 scoped_ptr<ExternalPolicyDataFetcher> external_policy_data_fetcher_; 101 ComponentCloudPolicyStore store_; 102 scoped_ptr<ComponentCloudPolicyUpdater> updater_; 103 scoped_refptr<SchemaMap> schema_map_; 104 105 DISALLOW_COPY_AND_ASSIGN(Backend); 106}; 107 108ComponentCloudPolicyService::Backend::Backend( 109 base::WeakPtr<ComponentCloudPolicyService> service, 110 scoped_refptr<base::SequencedTaskRunner> task_runner, 111 scoped_refptr<base::SequencedTaskRunner> service_task_runner, 112 scoped_ptr<ResourceCache> cache, 113 scoped_ptr<ExternalPolicyDataFetcher> external_policy_data_fetcher) 114 : service_(service), 115 task_runner_(task_runner), 116 service_task_runner_(service_task_runner), 117 cache_(cache.Pass()), 118 external_policy_data_fetcher_(external_policy_data_fetcher.Pass()), 119 store_(this, cache_.get()) {} 120 121ComponentCloudPolicyService::Backend::~Backend() {} 122 123void ComponentCloudPolicyService::Backend::SetCredentials( 124 const std::string& username, 125 const std::string& dm_token) { 126 if (username.empty() || dm_token.empty()) { 127 // No sign-in credentials, so drop any cached policy. 128 store_.Clear(); 129 } else { 130 store_.SetCredentials(username, dm_token); 131 } 132} 133 134void ComponentCloudPolicyService::Backend::Init( 135 scoped_refptr<SchemaMap> schema_map) { 136 DCHECK(!schema_map_); 137 138 OnSchemasUpdated(schema_map, scoped_ptr<PolicyNamespaceList>()); 139 140 // Read the initial policy. Note that this does not trigger notifications 141 // through OnComponentCloudPolicyStoreUpdated. Note also that the cached 142 // data may contain names or values that don't match the schema for that 143 // component; the data must be cached without modifications so that its 144 // integrity can be verified using the hash, but it must also be filtered 145 // right after a Load(). 146 store_.Load(); 147 scoped_ptr<PolicyBundle> bundle(new PolicyBundle); 148 bundle->CopyFrom(store_.policy()); 149 schema_map_->FilterBundle(bundle.get()); 150 151 // Start downloading any pending data. 152 updater_.reset(new ComponentCloudPolicyUpdater( 153 task_runner_, external_policy_data_fetcher_.Pass(), &store_)); 154 155 service_task_runner_->PostTask( 156 FROM_HERE, 157 base::Bind(&ComponentCloudPolicyService::OnBackendInitialized, 158 service_, 159 base::Passed(&bundle))); 160} 161 162void ComponentCloudPolicyService::Backend::UpdateExternalPolicy( 163 scoped_ptr<em::PolicyFetchResponse> response) { 164 updater_->UpdateExternalPolicy(response.Pass()); 165} 166 167void ComponentCloudPolicyService::Backend:: 168 OnComponentCloudPolicyStoreUpdated() { 169 if (!schema_map_) { 170 // Ignore notifications triggered by the initial Purge or Clear. 171 return; 172 } 173 174 scoped_ptr<PolicyBundle> bundle(new PolicyBundle); 175 bundle->CopyFrom(store_.policy()); 176 schema_map_->FilterBundle(bundle.get()); 177 service_task_runner_->PostTask( 178 FROM_HERE, 179 base::Bind(&ComponentCloudPolicyService::OnPolicyUpdated, 180 service_, 181 base::Passed(&bundle))); 182} 183 184void ComponentCloudPolicyService::Backend::OnSchemasUpdated( 185 scoped_refptr<SchemaMap> schema_map, 186 scoped_ptr<PolicyNamespaceList> removed) { 187 // Purge any components that have been removed. 188 const DomainMap& domains = schema_map->GetDomains(); 189 for (DomainMap::const_iterator domain = domains.begin(); 190 domain != domains.end(); ++domain) { 191 store_.Purge(domain->first, 192 base::Bind(&NotInSchemaMap, schema_map, domain->first)); 193 } 194 195 // Set |schema_map_| after purging so that the notifications from the store 196 // are ignored on the first OnSchemasUpdated() call from Init(). 197 schema_map_ = schema_map; 198 199 if (removed) { 200 for (size_t i = 0; i < removed->size(); ++i) 201 updater_->CancelUpdate((*removed)[i]); 202 } 203} 204 205ComponentCloudPolicyService::ComponentCloudPolicyService( 206 Delegate* delegate, 207 SchemaRegistry* schema_registry, 208 CloudPolicyCore* core, 209 scoped_ptr<ResourceCache> cache, 210 scoped_refptr<net::URLRequestContextGetter> request_context, 211 scoped_refptr<base::SequencedTaskRunner> backend_task_runner, 212 scoped_refptr<base::SequencedTaskRunner> io_task_runner) 213 : delegate_(delegate), 214 schema_registry_(schema_registry), 215 core_(core), 216 request_context_(request_context), 217 backend_task_runner_(backend_task_runner), 218 io_task_runner_(io_task_runner), 219 current_schema_map_(new SchemaMap), 220 started_loading_initial_policy_(false), 221 loaded_initial_policy_(false), 222 is_registered_for_cloud_policy_(false), 223 weak_ptr_factory_(this) { 224 external_policy_data_fetcher_backend_.reset( 225 new ExternalPolicyDataFetcherBackend(io_task_runner_, request_context)); 226 227 backend_.reset( 228 new Backend(weak_ptr_factory_.GetWeakPtr(), 229 backend_task_runner_, 230 base::MessageLoopProxy::current(), 231 cache.Pass(), 232 external_policy_data_fetcher_backend_->CreateFrontend( 233 backend_task_runner_))); 234 235 schema_registry_->AddObserver(this); 236 core_->store()->AddObserver(this); 237 238 // Wait for the store and the schema registry to become ready before 239 // initializing the backend, so that it can get the initial list of 240 // components and the cached credentials (if any) to validate the cached 241 // policies. 242 if (core_->store()->is_initialized()) 243 OnStoreLoaded(core_->store()); 244} 245 246ComponentCloudPolicyService::~ComponentCloudPolicyService() { 247 DCHECK(CalledOnValidThread()); 248 249 schema_registry_->RemoveObserver(this); 250 core_->store()->RemoveObserver(this); 251 core_->RemoveObserver(this); 252 if (core_->client()) 253 OnCoreDisconnecting(core_); 254 255 io_task_runner_->DeleteSoon(FROM_HERE, 256 external_policy_data_fetcher_backend_.release()); 257 backend_task_runner_->DeleteSoon(FROM_HERE, backend_.release()); 258} 259 260// static 261bool ComponentCloudPolicyService::SupportsDomain(PolicyDomain domain) { 262 return ComponentCloudPolicyStore::SupportsDomain(domain); 263} 264 265void ComponentCloudPolicyService::ClearCache() { 266 DCHECK(CalledOnValidThread()); 267 // Empty credentials will wipe the cache. 268 backend_task_runner_->PostTask(FROM_HERE, 269 base::Bind(&Backend::SetCredentials, 270 base::Unretained(backend_.get()), 271 std::string(), std::string())); 272} 273 274void ComponentCloudPolicyService::OnSchemaRegistryReady() { 275 DCHECK(CalledOnValidThread()); 276 InitializeIfReady(); 277} 278 279void ComponentCloudPolicyService::OnSchemaRegistryUpdated( 280 bool has_new_schemas) { 281 DCHECK(CalledOnValidThread()); 282 283 // Ignore schema updates until the backend is initialized. 284 // OnBackendInitialized() will send the current schema to the backend again, 285 // in case it was updated before the backend initialized. 286 if (!loaded_initial_policy_) 287 return; 288 289 ReloadSchema(); 290} 291 292void ComponentCloudPolicyService::OnCoreConnected(CloudPolicyCore* core) { 293 DCHECK(CalledOnValidThread()); 294 DCHECK_EQ(core_, core); 295 296 core_->client()->AddObserver(this); 297 298 // Register the supported policy domains at the client. 299 core_->client()->AddNamespaceToFetch( 300 PolicyNamespaceKey(dm_protocol::kChromeExtensionPolicyType, "")); 301 302 // Immediately load any PolicyFetchResponses that the client may already 303 // have. 304 OnPolicyFetched(core_->client()); 305} 306 307void ComponentCloudPolicyService::OnCoreDisconnecting(CloudPolicyCore* core) { 308 DCHECK(CalledOnValidThread()); 309 DCHECK_EQ(core_, core); 310 311 core_->client()->RemoveObserver(this); 312 313 // Remove all the namespaces from the client. 314 core_->client()->RemoveNamespaceToFetch( 315 PolicyNamespaceKey(dm_protocol::kChromeExtensionPolicyType, "")); 316} 317 318void ComponentCloudPolicyService::OnRefreshSchedulerStarted( 319 CloudPolicyCore* core) { 320 // Ignored. 321} 322 323void ComponentCloudPolicyService::OnStoreLoaded(CloudPolicyStore* store) { 324 DCHECK(CalledOnValidThread()); 325 DCHECK_EQ(core_->store(), store); 326 327 const bool was_registered_before = is_registered_for_cloud_policy_; 328 329 // Send the current credentials to the backend; do this whenever the store 330 // updates, to handle the case of the user registering for policy after the 331 // session starts, or the user signing out. 332 const em::PolicyData* policy = core_->store()->policy(); 333 std::string username; 334 std::string request_token; 335 if (policy && policy->has_username() && policy->has_request_token()) { 336 is_registered_for_cloud_policy_ = true; 337 username = policy->username(); 338 request_token = policy->request_token(); 339 } else { 340 is_registered_for_cloud_policy_ = false; 341 } 342 343 // Empty credentials will wipe the cache. 344 backend_task_runner_->PostTask(FROM_HERE, 345 base::Bind(&Backend::SetCredentials, 346 base::Unretained(backend_.get()), 347 username, 348 request_token)); 349 350 if (!loaded_initial_policy_) { 351 // This is the initial load; check if we're ready to initialize the 352 // backend, regardless of the signin state. 353 InitializeIfReady(); 354 } else if (!was_registered_before && is_registered_for_cloud_policy_) { 355 // We are already initialized, but just sent credentials to the backend for 356 // the first time; this means that the user was not registered for cloud 357 // policy on startup but registered during the session. 358 // 359 // When that happens, OnPolicyFetched() is sent to observers before the 360 // CloudPolicyStore gets a chance to verify the user policy. In those cases, 361 // the backend gets the PolicyFetchResponses before it has the credentials 362 // and therefore the validation of those responses fails. 363 // Reload any PolicyFetchResponses that the client may have now so that 364 // validation is retried with the credentials in place. 365 if (core_->client()) 366 OnPolicyFetched(core_->client()); 367 } 368} 369 370void ComponentCloudPolicyService::OnStoreError(CloudPolicyStore* store) { 371 DCHECK(CalledOnValidThread()); 372 OnStoreLoaded(store); 373} 374 375void ComponentCloudPolicyService::OnPolicyFetched(CloudPolicyClient* client) { 376 DCHECK(CalledOnValidThread()); 377 DCHECK_EQ(core_->client(), client); 378 379 if (!is_registered_for_cloud_policy_) { 380 // Trying to load any policies now will fail validation. An OnStoreLoaded() 381 // notification should follow soon, after the main user policy has been 382 // validated and stored. 383 return; 384 } 385 386 // Pass each PolicyFetchResponse whose policy type is registered to the 387 // Backend. 388 const CloudPolicyClient::ResponseMap& responses = 389 core_->client()->responses(); 390 for (CloudPolicyClient::ResponseMap::const_iterator it = responses.begin(); 391 it != responses.end(); ++it) { 392 PolicyNamespace ns; 393 if (ToPolicyNamespace(it->first, &ns) && 394 current_schema_map_->GetSchema(ns)) { 395 scoped_ptr<em::PolicyFetchResponse> response( 396 new em::PolicyFetchResponse(*it->second)); 397 backend_task_runner_->PostTask( 398 FROM_HERE, 399 base::Bind(&Backend::UpdateExternalPolicy, 400 base::Unretained(backend_.get()), 401 base::Passed(&response))); 402 } 403 } 404} 405 406void ComponentCloudPolicyService::OnRegistrationStateChanged( 407 CloudPolicyClient* client) { 408 DCHECK(CalledOnValidThread()); 409 // Ignored; the registration state is tracked by looking at the 410 // CloudPolicyStore instead. 411} 412 413void ComponentCloudPolicyService::OnClientError(CloudPolicyClient* client) { 414 DCHECK(CalledOnValidThread()); 415 // Ignored. 416} 417 418void ComponentCloudPolicyService::InitializeIfReady() { 419 DCHECK(CalledOnValidThread()); 420 if (started_loading_initial_policy_ || !schema_registry_->IsReady() || 421 !core_->store()->is_initialized()) { 422 return; 423 } 424 // The initial list of components is ready. Initialize the backend now, which 425 // will call back to OnBackendInitialized. 426 backend_task_runner_->PostTask(FROM_HERE, 427 base::Bind(&Backend::Init, 428 base::Unretained(backend_.get()), 429 schema_registry_->schema_map())); 430 started_loading_initial_policy_ = true; 431} 432 433void ComponentCloudPolicyService::OnBackendInitialized( 434 scoped_ptr<PolicyBundle> initial_policy) { 435 DCHECK(CalledOnValidThread()); 436 DCHECK(!loaded_initial_policy_); 437 438 loaded_initial_policy_ = true; 439 440 // We're now ready to serve the initial policy; notify the policy observers. 441 OnPolicyUpdated(initial_policy.Pass()); 442 443 // Send the current schema to the backend, in case it has changed while the 444 // backend was initializing. 445 ReloadSchema(); 446 447 // Start observing the core and tracking the state of the client. 448 core_->AddObserver(this); 449 450 if (core_->client()) 451 OnCoreConnected(core_); 452} 453 454void ComponentCloudPolicyService::ReloadSchema() { 455 DCHECK(CalledOnValidThread()); 456 457 scoped_ptr<PolicyNamespaceList> removed(new PolicyNamespaceList); 458 PolicyNamespaceList added; 459 const scoped_refptr<SchemaMap>& new_schema_map = 460 schema_registry_->schema_map(); 461 new_schema_map->GetChanges(current_schema_map_, removed.get(), &added); 462 463 current_schema_map_ = new_schema_map; 464 465 // Schedule a policy refresh if a new managed component was added. 466 if (core_->client() && !added.empty()) 467 core_->RefreshSoon(); 468 469 // Send the updated SchemaMap and a list of removed components to the 470 // backend. 471 backend_task_runner_->PostTask(FROM_HERE, 472 base::Bind(&Backend::OnSchemasUpdated, 473 base::Unretained(backend_.get()), 474 current_schema_map_, 475 base::Passed(&removed))); 476} 477 478void ComponentCloudPolicyService::OnPolicyUpdated( 479 scoped_ptr<PolicyBundle> policy) { 480 DCHECK(CalledOnValidThread()); 481 policy_.Swap(policy.get()); 482 delegate_->OnComponentCloudPolicyUpdated(); 483} 484 485} // namespace policy 486