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