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 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 47 ComponentCloudPolicyService::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. 52 class 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 bool initialized_; 104 105 DISALLOW_COPY_AND_ASSIGN(Backend); 106 }; 107 108 ComponentCloudPolicyService::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 initialized_(false) {} 121 122 ComponentCloudPolicyService::Backend::~Backend() {} 123 124 void ComponentCloudPolicyService::Backend::SetCredentials( 125 const std::string& username, 126 const std::string& dm_token) { 127 if (username.empty() || dm_token.empty()) { 128 // No sign-in credentials, so drop any cached policy. 129 store_.Clear(); 130 } else { 131 store_.SetCredentials(username, dm_token); 132 } 133 } 134 135 void ComponentCloudPolicyService::Backend::Init( 136 scoped_refptr<SchemaMap> schema_map) { 137 DCHECK(!initialized_); 138 139 OnSchemasUpdated(schema_map, scoped_ptr<PolicyNamespaceList>()); 140 141 // Read the initial policy. Note that this does not trigger notifications 142 // through OnComponentCloudPolicyStoreUpdated. Note also that the cached 143 // data may contain names or values that don't match the schema for that 144 // component; the data must be cached without modifications so that its 145 // integrity can be verified using the hash, but it must also be filtered 146 // right after a Load(). 147 store_.Load(); 148 scoped_ptr<PolicyBundle> bundle(new PolicyBundle); 149 bundle->CopyFrom(store_.policy()); 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 initialized_ = true; 162 } 163 164 void ComponentCloudPolicyService::Backend::UpdateExternalPolicy( 165 scoped_ptr<em::PolicyFetchResponse> response) { 166 updater_->UpdateExternalPolicy(response.Pass()); 167 } 168 169 void ComponentCloudPolicyService::Backend:: 170 OnComponentCloudPolicyStoreUpdated() { 171 if (!initialized_) { 172 // Ignore notifications triggered by the initial Purge or Clear. 173 return; 174 } 175 176 scoped_ptr<PolicyBundle> bundle(new PolicyBundle); 177 bundle->CopyFrom(store_.policy()); 178 service_task_runner_->PostTask( 179 FROM_HERE, 180 base::Bind(&ComponentCloudPolicyService::OnPolicyUpdated, 181 service_, 182 base::Passed(&bundle))); 183 } 184 185 void ComponentCloudPolicyService::Backend::OnSchemasUpdated( 186 scoped_refptr<SchemaMap> schema_map, 187 scoped_ptr<PolicyNamespaceList> removed) { 188 // Purge any components that have been removed. 189 const DomainMap& domains = schema_map->GetDomains(); 190 for (DomainMap::const_iterator domain = domains.begin(); 191 domain != domains.end(); ++domain) { 192 store_.Purge(domain->first, 193 base::Bind(&NotInSchemaMap, schema_map, domain->first)); 194 } 195 196 if (removed) { 197 for (size_t i = 0; i < removed->size(); ++i) 198 updater_->CancelUpdate((*removed)[i]); 199 } 200 } 201 202 ComponentCloudPolicyService::ComponentCloudPolicyService( 203 Delegate* delegate, 204 SchemaRegistry* schema_registry, 205 CloudPolicyCore* core, 206 scoped_ptr<ResourceCache> cache, 207 scoped_refptr<net::URLRequestContextGetter> request_context, 208 scoped_refptr<base::SequencedTaskRunner> backend_task_runner, 209 scoped_refptr<base::SequencedTaskRunner> io_task_runner) 210 : delegate_(delegate), 211 schema_registry_(schema_registry), 212 core_(core), 213 request_context_(request_context), 214 backend_task_runner_(backend_task_runner), 215 io_task_runner_(io_task_runner), 216 current_schema_map_(new SchemaMap), 217 unfiltered_policy_(new PolicyBundle), 218 started_loading_initial_policy_(false), 219 loaded_initial_policy_(false), 220 is_registered_for_cloud_policy_(false), 221 weak_ptr_factory_(this) { 222 external_policy_data_fetcher_backend_.reset( 223 new ExternalPolicyDataFetcherBackend(io_task_runner_, request_context)); 224 225 backend_.reset( 226 new Backend(weak_ptr_factory_.GetWeakPtr(), 227 backend_task_runner_, 228 base::MessageLoopProxy::current(), 229 cache.Pass(), 230 external_policy_data_fetcher_backend_->CreateFrontend( 231 backend_task_runner_))); 232 233 schema_registry_->AddObserver(this); 234 core_->store()->AddObserver(this); 235 236 // Wait for the store and the schema registry to become ready before 237 // initializing the backend, so that it can get the initial list of 238 // components and the cached credentials (if any) to validate the cached 239 // policies. 240 if (core_->store()->is_initialized()) 241 OnStoreLoaded(core_->store()); 242 243 // Start observing the core and tracking the state of the client. 244 core_->AddObserver(this); 245 if (core_->client()) 246 OnCoreConnected(core_); 247 } 248 249 ComponentCloudPolicyService::~ComponentCloudPolicyService() { 250 DCHECK(CalledOnValidThread()); 251 252 schema_registry_->RemoveObserver(this); 253 core_->store()->RemoveObserver(this); 254 core_->RemoveObserver(this); 255 if (core_->client()) 256 OnCoreDisconnecting(core_); 257 258 io_task_runner_->DeleteSoon(FROM_HERE, 259 external_policy_data_fetcher_backend_.release()); 260 backend_task_runner_->DeleteSoon(FROM_HERE, backend_.release()); 261 } 262 263 // static 264 bool ComponentCloudPolicyService::SupportsDomain(PolicyDomain domain) { 265 return ComponentCloudPolicyStore::SupportsDomain(domain); 266 } 267 268 void ComponentCloudPolicyService::ClearCache() { 269 DCHECK(CalledOnValidThread()); 270 // Empty credentials will wipe the cache. 271 backend_task_runner_->PostTask(FROM_HERE, 272 base::Bind(&Backend::SetCredentials, 273 base::Unretained(backend_.get()), 274 std::string(), std::string())); 275 } 276 277 void ComponentCloudPolicyService::OnSchemaRegistryReady() { 278 DCHECK(CalledOnValidThread()); 279 InitializeIfReady(); 280 } 281 282 void ComponentCloudPolicyService::OnSchemaRegistryUpdated( 283 bool has_new_schemas) { 284 DCHECK(CalledOnValidThread()); 285 286 // Ignore schema updates until the backend is initialized. 287 // OnBackendInitialized() will send the current schema to the backend again, 288 // in case it was updated before the backend initialized. 289 if (!loaded_initial_policy_) 290 return; 291 292 ReloadSchema(); 293 294 // Filter the |unfiltered_policy_| again, now that |current_schema_map_| has 295 // been updated. We must make sure we never serve invalid policy; we must 296 // also filter again if an invalid Schema has now been loaded. 297 OnPolicyUpdated(unfiltered_policy_.Pass()); 298 } 299 300 void ComponentCloudPolicyService::OnCoreConnected(CloudPolicyCore* core) { 301 DCHECK(CalledOnValidThread()); 302 DCHECK_EQ(core_, core); 303 304 core_->client()->AddObserver(this); 305 306 // Register the supported policy domains at the client. 307 core_->client()->AddNamespaceToFetch( 308 PolicyNamespaceKey(dm_protocol::kChromeExtensionPolicyType, "")); 309 310 // Immediately load any PolicyFetchResponses that the client may already 311 // have if the backend is ready. 312 if (loaded_initial_policy_) 313 OnPolicyFetched(core_->client()); 314 } 315 316 void ComponentCloudPolicyService::OnCoreDisconnecting(CloudPolicyCore* core) { 317 DCHECK(CalledOnValidThread()); 318 DCHECK_EQ(core_, core); 319 320 core_->client()->RemoveObserver(this); 321 322 // Remove all the namespaces from the client. 323 core_->client()->RemoveNamespaceToFetch( 324 PolicyNamespaceKey(dm_protocol::kChromeExtensionPolicyType, "")); 325 } 326 327 void ComponentCloudPolicyService::OnRefreshSchedulerStarted( 328 CloudPolicyCore* core) { 329 // Ignored. 330 } 331 332 void ComponentCloudPolicyService::OnStoreLoaded(CloudPolicyStore* store) { 333 DCHECK(CalledOnValidThread()); 334 DCHECK_EQ(core_->store(), store); 335 336 const bool was_registered_before = is_registered_for_cloud_policy_; 337 338 // Send the current credentials to the backend; do this whenever the store 339 // updates, to handle the case of the user registering for policy after the 340 // session starts, or the user signing out. 341 const em::PolicyData* policy = core_->store()->policy(); 342 std::string username; 343 std::string request_token; 344 if (policy && policy->has_username() && policy->has_request_token()) { 345 is_registered_for_cloud_policy_ = true; 346 username = policy->username(); 347 request_token = policy->request_token(); 348 } else { 349 is_registered_for_cloud_policy_ = false; 350 } 351 352 // Empty credentials will wipe the cache. 353 backend_task_runner_->PostTask(FROM_HERE, 354 base::Bind(&Backend::SetCredentials, 355 base::Unretained(backend_.get()), 356 username, 357 request_token)); 358 359 if (!loaded_initial_policy_) { 360 // This is the initial load; check if we're ready to initialize the 361 // backend, regardless of the signin state. 362 InitializeIfReady(); 363 } else if (!was_registered_before && is_registered_for_cloud_policy_) { 364 // We are already initialized, but just sent credentials to the backend for 365 // the first time; this means that the user was not registered for cloud 366 // policy on startup but registered during the session. 367 // 368 // When that happens, OnPolicyFetched() is sent to observers before the 369 // CloudPolicyStore gets a chance to verify the user policy. In those cases, 370 // the backend gets the PolicyFetchResponses before it has the credentials 371 // and therefore the validation of those responses fails. 372 // Reload any PolicyFetchResponses that the client may have now so that 373 // validation is retried with the credentials in place. 374 if (core_->client()) 375 OnPolicyFetched(core_->client()); 376 } 377 } 378 379 void ComponentCloudPolicyService::OnStoreError(CloudPolicyStore* store) { 380 DCHECK(CalledOnValidThread()); 381 OnStoreLoaded(store); 382 } 383 384 void ComponentCloudPolicyService::OnPolicyFetched(CloudPolicyClient* client) { 385 DCHECK(CalledOnValidThread()); 386 DCHECK_EQ(core_->client(), client); 387 388 if (!is_registered_for_cloud_policy_) { 389 // Trying to load any policies now will fail validation. An OnStoreLoaded() 390 // notification should follow soon, after the main user policy has been 391 // validated and stored. 392 return; 393 } 394 395 // Pass each PolicyFetchResponse whose policy type is registered to the 396 // Backend. 397 const CloudPolicyClient::ResponseMap& responses = 398 core_->client()->responses(); 399 for (CloudPolicyClient::ResponseMap::const_iterator it = responses.begin(); 400 it != responses.end(); ++it) { 401 PolicyNamespace ns; 402 if (ToPolicyNamespace(it->first, &ns) && 403 current_schema_map_->GetSchema(ns)) { 404 scoped_ptr<em::PolicyFetchResponse> response( 405 new em::PolicyFetchResponse(*it->second)); 406 backend_task_runner_->PostTask( 407 FROM_HERE, 408 base::Bind(&Backend::UpdateExternalPolicy, 409 base::Unretained(backend_.get()), 410 base::Passed(&response))); 411 } 412 } 413 } 414 415 void ComponentCloudPolicyService::OnRegistrationStateChanged( 416 CloudPolicyClient* client) { 417 DCHECK(CalledOnValidThread()); 418 // Ignored; the registration state is tracked by looking at the 419 // CloudPolicyStore instead. 420 } 421 422 void ComponentCloudPolicyService::OnClientError(CloudPolicyClient* client) { 423 DCHECK(CalledOnValidThread()); 424 // Ignored. 425 } 426 427 void ComponentCloudPolicyService::InitializeIfReady() { 428 DCHECK(CalledOnValidThread()); 429 if (started_loading_initial_policy_ || !schema_registry_->IsReady() || 430 !core_->store()->is_initialized()) { 431 return; 432 } 433 434 // The initial list of components is ready. Initialize the backend now, which 435 // will call back to OnBackendInitialized. 436 backend_task_runner_->PostTask(FROM_HERE, 437 base::Bind(&Backend::Init, 438 base::Unretained(backend_.get()), 439 schema_registry_->schema_map())); 440 started_loading_initial_policy_ = true; 441 } 442 443 void ComponentCloudPolicyService::OnBackendInitialized( 444 scoped_ptr<PolicyBundle> initial_policy) { 445 DCHECK(CalledOnValidThread()); 446 DCHECK(!loaded_initial_policy_); 447 448 loaded_initial_policy_ = true; 449 450 // Send the current schema to the backend, in case it has changed while the 451 // backend was initializing. 452 ReloadSchema(); 453 454 // We're now ready to serve the initial policy; notify the policy observers. 455 OnPolicyUpdated(initial_policy.Pass()); 456 } 457 458 void ComponentCloudPolicyService::ReloadSchema() { 459 DCHECK(CalledOnValidThread()); 460 461 scoped_ptr<PolicyNamespaceList> removed(new PolicyNamespaceList); 462 PolicyNamespaceList added; 463 const scoped_refptr<SchemaMap>& new_schema_map = 464 schema_registry_->schema_map(); 465 new_schema_map->GetChanges(current_schema_map_, removed.get(), &added); 466 467 current_schema_map_ = new_schema_map; 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 // Have another look at the client if the core is already connected. 478 // The client may have already fetched policy for some component and it was 479 // previously ignored because the component wasn't listed in the schema map. 480 // There's no point in fetching policy from the server again; the server 481 // always pushes all the components it knows about. 482 if (core_->client()) 483 OnPolicyFetched(core_->client()); 484 } 485 486 void ComponentCloudPolicyService::OnPolicyUpdated( 487 scoped_ptr<PolicyBundle> policy) { 488 DCHECK(CalledOnValidThread()); 489 490 // Store the current unfiltered policies. 491 unfiltered_policy_ = policy.Pass(); 492 493 // Make a copy in |policy_| and filter it; this is what's passed to the 494 // outside world. 495 policy_.CopyFrom(*unfiltered_policy_); 496 current_schema_map_->FilterBundle(&policy_); 497 498 delegate_->OnComponentCloudPolicyUpdated(); 499 } 500 501 } // namespace policy 502