| Index: chrome/browser/policy/cloud/component_cloud_policy_service.cc
|
| diff --git a/chrome/browser/policy/cloud/component_cloud_policy_service.cc b/chrome/browser/policy/cloud/component_cloud_policy_service.cc
|
| index 295c67074ae3d584f88eaac9bf6b70f20da52a83..78ea295f7e5e6418b61b840e82e01c35db8dd818 100644
|
| --- a/chrome/browser/policy/cloud/component_cloud_policy_service.cc
|
| +++ b/chrome/browser/policy/cloud/component_cloud_policy_service.cc
|
| @@ -12,8 +12,8 @@
|
| #include "base/logging.h"
|
| #include "base/message_loop/message_loop_proxy.h"
|
| #include "base/sequenced_task_runner.h"
|
| -#include "base/time/time.h"
|
| #include "chrome/browser/policy/cloud/cloud_policy_constants.h"
|
| +#include "chrome/browser/policy/cloud/cloud_policy_refresh_scheduler.h"
|
| #include "chrome/browser/policy/cloud/component_cloud_policy_store.h"
|
| #include "chrome/browser/policy/cloud/component_cloud_policy_updater.h"
|
| #include "chrome/browser/policy/cloud/external_policy_data_fetcher.h"
|
| @@ -130,7 +130,12 @@ ComponentCloudPolicyService::Backend::~Backend() {}
|
| void ComponentCloudPolicyService::Backend::SetCredentials(
|
| const std::string& username,
|
| const std::string& dm_token) {
|
| - store_.SetCredentials(username, dm_token);
|
| + if (username.empty() || dm_token.empty()) {
|
| + // No sign-in credentials, so drop any cached policy.
|
| + store_.Clear();
|
| + } else {
|
| + store_.SetCredentials(username, dm_token);
|
| + }
|
| }
|
|
|
| void ComponentCloudPolicyService::Backend::Init(
|
| @@ -164,7 +169,7 @@ void ComponentCloudPolicyService::Backend::UpdateExternalPolicy(
|
| void ComponentCloudPolicyService::Backend::
|
| OnComponentCloudPolicyStoreUpdated() {
|
| if (!schema_map_) {
|
| - // Ignore notifications triggered by the initial Purge.
|
| + // Ignore notifications triggered by the initial Purge or Clear.
|
| return;
|
| }
|
|
|
| @@ -202,27 +207,21 @@ void ComponentCloudPolicyService::Backend::OnSchemasUpdated(
|
| ComponentCloudPolicyService::ComponentCloudPolicyService(
|
| Delegate* delegate,
|
| SchemaRegistry* schema_registry,
|
| - CloudPolicyStore* store,
|
| + CloudPolicyCore* core,
|
| scoped_ptr<ResourceCache> cache,
|
| - CloudPolicyClient* client,
|
| scoped_refptr<net::URLRequestContextGetter> request_context,
|
| scoped_refptr<base::SequencedTaskRunner> backend_task_runner,
|
| scoped_refptr<base::SequencedTaskRunner> io_task_runner)
|
| : delegate_(delegate),
|
| schema_registry_(schema_registry),
|
| - store_(store),
|
| - client_(client),
|
| + core_(core),
|
| request_context_(request_context),
|
| backend_task_runner_(backend_task_runner),
|
| io_task_runner_(io_task_runner),
|
| current_schema_map_(new SchemaMap),
|
| - is_initialized_(false),
|
| - has_credentials_(false),
|
| + loaded_initial_policy_(false),
|
| + is_registered_for_cloud_policy_(false),
|
| weak_ptr_factory_(this) {
|
| - schema_registry_->AddObserver(this);
|
| - store_->AddObserver(this);
|
| - client_->AddObserver(this);
|
| -
|
| external_policy_data_fetcher_backend_.reset(
|
| new ExternalPolicyDataFetcherBackend(io_task_runner_, request_context));
|
|
|
| @@ -234,22 +233,25 @@ ComponentCloudPolicyService::ComponentCloudPolicyService(
|
| external_policy_data_fetcher_backend_->CreateFrontend(
|
| backend_task_runner_)));
|
|
|
| - if (store_->is_initialized())
|
| - OnStoreLoaded(store_);
|
| + schema_registry_->AddObserver(this);
|
| + core_->store()->AddObserver(this);
|
| +
|
| + // Wait for the store and the schema registry to become ready before
|
| + // initializing the backend, so that it can get the initial list of
|
| + // components and the cached credentials (if any) to validate the cached
|
| + // policies.
|
| + if (core_->store()->is_initialized())
|
| + OnStoreLoaded(core_->store());
|
| }
|
|
|
| ComponentCloudPolicyService::~ComponentCloudPolicyService() {
|
| DCHECK(CalledOnValidThread());
|
| +
|
| schema_registry_->RemoveObserver(this);
|
| - store_->RemoveObserver(this);
|
| - client_->RemoveObserver(this);
|
| -
|
| - // Remove all the namespaces from |client_| but don't send this empty schema
|
| - // to the backend, to avoid dropping the caches.
|
| - if (is_initialized()) {
|
| - scoped_refptr<SchemaMap> empty(new SchemaMap);
|
| - SetCurrentSchema(empty, false);
|
| - }
|
| + core_->store()->RemoveObserver(this);
|
| + core_->RemoveObserver(this);
|
| + if (core_->client())
|
| + OnCoreDisconnecting(core_);
|
|
|
| io_task_runner_->DeleteSoon(FROM_HERE,
|
| external_policy_data_fetcher_backend_.release());
|
| @@ -261,16 +263,129 @@ bool ComponentCloudPolicyService::SupportsDomain(PolicyDomain domain) {
|
| return ComponentCloudPolicyStore::SupportsDomain(domain);
|
| }
|
|
|
| +void ComponentCloudPolicyService::OnSchemaRegistryReady() {
|
| + DCHECK(CalledOnValidThread());
|
| + InitializeIfReady();
|
| +}
|
| +
|
| +void ComponentCloudPolicyService::OnSchemaRegistryUpdated(
|
| + bool has_new_schemas) {
|
| + DCHECK(CalledOnValidThread());
|
| +
|
| + // Ignore schema updates until the backend is initialized.
|
| + // OnBackendInitialized() will send the current schema to the backend again,
|
| + // in case it was updated before the backend initialized.
|
| + if (!loaded_initial_policy_)
|
| + return;
|
| +
|
| + SetCurrentSchema();
|
| +}
|
| +
|
| +void ComponentCloudPolicyService::OnCoreConnected(CloudPolicyCore* core) {
|
| + DCHECK(CalledOnValidThread());
|
| + DCHECK_EQ(core_, core);
|
| +
|
| + core_->client()->AddObserver(this);
|
| +
|
| + // Immediately load any PolicyFetchResponses that the client may already
|
| + // have.
|
| + OnPolicyFetched(core_->client());
|
| +
|
| + // Register the current namespaces at the client.
|
| + current_schema_map_ = new SchemaMap();
|
| + SetCurrentSchema();
|
| +}
|
| +
|
| +void ComponentCloudPolicyService::OnCoreDisconnecting(CloudPolicyCore* core) {
|
| + DCHECK(CalledOnValidThread());
|
| + DCHECK_EQ(core_, core);
|
| +
|
| + core_->client()->RemoveObserver(this);
|
| +
|
| + // Remove all the namespaces from the client.
|
| + scoped_refptr<SchemaMap> empty = new SchemaMap();
|
| + PolicyNamespaceList removed;
|
| + PolicyNamespaceList added;
|
| + empty->GetChanges(current_schema_map_, &removed, &added);
|
| + for (size_t i = 0; i < removed.size(); ++i) {
|
| + PolicyNamespaceKey key;
|
| + if (ToPolicyNamespaceKey(removed[i], &key))
|
| + core_->client()->RemoveNamespaceToFetch(key);
|
| + }
|
| +}
|
| +
|
| +void ComponentCloudPolicyService::OnRefreshSchedulerStarted(
|
| + CloudPolicyCore* core) {
|
| + // Ignored.
|
| +}
|
| +
|
| +void ComponentCloudPolicyService::OnStoreLoaded(CloudPolicyStore* store) {
|
| + DCHECK(CalledOnValidThread());
|
| + DCHECK_EQ(core_->store(), store);
|
| +
|
| + const bool was_registered_before = is_registered_for_cloud_policy_;
|
| +
|
| + // Send the current credentials to the backend; do this whenever the store
|
| + // updates, to handle the case of the user registering for policy after the
|
| + // session starts, or the user signing out.
|
| + const em::PolicyData* policy = core_->store()->policy();
|
| + std::string username;
|
| + std::string request_token;
|
| + if (policy && policy->has_username() && policy->has_request_token()) {
|
| + is_registered_for_cloud_policy_ = true;
|
| + username = policy->username();
|
| + request_token = policy->request_token();
|
| + } else {
|
| + is_registered_for_cloud_policy_ = false;
|
| + }
|
| +
|
| + // Empty credentials will wipe the cache.
|
| + backend_task_runner_->PostTask(FROM_HERE,
|
| + base::Bind(&Backend::SetCredentials,
|
| + base::Unretained(backend_.get()),
|
| + username,
|
| + request_token));
|
| +
|
| + if (!loaded_initial_policy_) {
|
| + // This is the initial load; check if we're ready to initialize the
|
| + // backend, regardless of the signin state.
|
| + InitializeIfReady();
|
| + } else if (!was_registered_before && is_registered_for_cloud_policy_) {
|
| + // We are already initialized, but just sent credentials to the backend for
|
| + // the first time; this means that the user was not registered for cloud
|
| + // policy on startup but registered during the session.
|
| + //
|
| + // When that happens, OnPolicyFetched() is sent to observers before the
|
| + // CloudPolicyStore gets a chance to verify the user policy. In those cases,
|
| + // the backend gets the PolicyFetchResponses before it has the credentials
|
| + // and therefore the validation of those responses fails.
|
| + // Reload any PolicyFetchResponses that the client may have now so that
|
| + // validation is retried with the credentials in place.
|
| + if (core_->client())
|
| + OnPolicyFetched(core_->client());
|
| + }
|
| +}
|
| +
|
| +void ComponentCloudPolicyService::OnStoreError(CloudPolicyStore* store) {
|
| + DCHECK(CalledOnValidThread());
|
| + OnStoreLoaded(store);
|
| +}
|
| +
|
| void ComponentCloudPolicyService::OnPolicyFetched(CloudPolicyClient* client) {
|
| DCHECK(CalledOnValidThread());
|
| - DCHECK_EQ(client_, client);
|
| + DCHECK_EQ(core_->client(), client);
|
|
|
| - if (!is_initialized() || !has_credentials_)
|
| + if (!is_registered_for_cloud_policy_) {
|
| + // Trying to load any policies now will fail validation. An OnStoreLoaded()
|
| + // notification should follow soon, after the main user policy has been
|
| + // validated and stored.
|
| return;
|
| + }
|
|
|
| // Pass each PolicyFetchResponse whose policy type is registered to the
|
| // Backend.
|
| - const CloudPolicyClient::ResponseMap& responses = client_->responses();
|
| + const CloudPolicyClient::ResponseMap& responses =
|
| + core_->client()->responses();
|
| for (CloudPolicyClient::ResponseMap::const_iterator it = responses.begin();
|
| it != responses.end(); ++it) {
|
| PolicyNamespace ns;
|
| @@ -290,7 +405,8 @@ void ComponentCloudPolicyService::OnPolicyFetched(CloudPolicyClient* client) {
|
| void ComponentCloudPolicyService::OnRegistrationStateChanged(
|
| CloudPolicyClient* client) {
|
| DCHECK(CalledOnValidThread());
|
| - // Ignored.
|
| + // Ignored; the registration state is tracked by looking at the
|
| + // CloudPolicyStore instead.
|
| }
|
|
|
| void ComponentCloudPolicyService::OnClientError(CloudPolicyClient* client) {
|
| @@ -298,77 +414,12 @@ void ComponentCloudPolicyService::OnClientError(CloudPolicyClient* client) {
|
| // Ignored.
|
| }
|
|
|
| -void ComponentCloudPolicyService::OnStoreLoaded(CloudPolicyStore* store) {
|
| - DCHECK(CalledOnValidThread());
|
| - DCHECK_EQ(store_, store);
|
| -
|
| - if (!store_->is_initialized())
|
| - return;
|
| -
|
| - const em::PolicyData* policy = store_->policy();
|
| - if (!has_credentials_ && policy && policy->has_username() &&
|
| - policy->has_request_token()) {
|
| - // Send the current credentials to the backend, if they haven't been sent
|
| - // before. Usually this happens at startup if the user already had a cached
|
| - // cloud policy; otherwise it happens right after the initial registration
|
| - // for cloud policy.
|
| - backend_task_runner_->PostTask(FROM_HERE,
|
| - base::Bind(&Backend::SetCredentials,
|
| - base::Unretained(backend_.get()),
|
| - policy->username(),
|
| - policy->request_token()));
|
| - has_credentials_ = true;
|
| - if (is_initialized()) {
|
| - // This was the first policy fetch for this client. Process any
|
| - // PolicyFetchResponses that the client may have now; processing them
|
| - // before the credentials were sent to the backend would fail validation.
|
| - OnPolicyFetched(client_);
|
| - }
|
| - }
|
| -
|
| - if (!is_initialized())
|
| - InitializeIfReady();
|
| -}
|
| -
|
| -void ComponentCloudPolicyService::OnStoreError(CloudPolicyStore* store) {
|
| - DCHECK(CalledOnValidThread());
|
| - OnStoreLoaded(store);
|
| -}
|
| -
|
| -void ComponentCloudPolicyService::OnSchemaRegistryReady() {
|
| - DCHECK(CalledOnValidThread());
|
| - InitializeIfReady();
|
| -}
|
| -
|
| -void ComponentCloudPolicyService::OnSchemaRegistryUpdated(
|
| - bool has_new_schemas) {
|
| - DCHECK(CalledOnValidThread());
|
| -
|
| - if (!is_initialized())
|
| - return;
|
| -
|
| - // When an extension is reloaded or updated, it triggers an unregister quickly
|
| - // followed by a register in the SchemaRegistry. If the intermediate version
|
| - // of the SchemaMap is passed to the backend then it will drop the cached
|
| - // policy for that extension and will trigger a new policy fetch soon after.
|
| - // Delaying the schema update here coalesces both updates into one, and the
|
| - // new schema will equal the older version in case of extension updates.
|
| - //
|
| - // TODO(joaodasilva): Increase this delay to 10 seconds. For now it's
|
| - // immediate so that tests don't get delayed.
|
| - schema_update_timer_.Start(
|
| - FROM_HERE,
|
| - base::TimeDelta::FromSeconds(0),
|
| - base::Bind(&ComponentCloudPolicyService::SetCurrentSchema,
|
| - base::Unretained(this),
|
| - schema_registry_->schema_map(),
|
| - true));
|
| -}
|
| -
|
| void ComponentCloudPolicyService::InitializeIfReady() {
|
| DCHECK(CalledOnValidThread());
|
| - if (!schema_registry_->IsReady() || !store_->is_initialized())
|
| + if (!schema_registry_->IsReady() || !core_->store()->is_initialized())
|
| return;
|
| + // The initial list of components is ready. Initialize the backend now, which
|
| + // will call back to OnBackendInitialized.
|
| backend_task_runner_->PostTask(FROM_HERE,
|
| base::Bind(&Backend::Init,
|
| base::Unretained(backend_.get()),
|
| @@ -379,57 +430,60 @@ void ComponentCloudPolicyService::OnBackendInitialized(
|
| scoped_ptr<PolicyBundle> initial_policy) {
|
| DCHECK(CalledOnValidThread());
|
|
|
| - is_initialized_ = true;
|
| + loaded_initial_policy_ = true;
|
|
|
| - // Send the current schema to the backend, in case it has changed while the
|
| - // backend was initializing.
|
| - SetCurrentSchema(schema_registry_->schema_map(), true);
|
| + // We're now ready to serve the initial policy; notify the policy observers.
|
| + OnPolicyUpdated(initial_policy.Pass());
|
|
|
| - // Process any PolicyFetchResponses that the client may already have, or that
|
| - // may have been received while the backend was initializing.
|
| - OnPolicyFetched(client_);
|
| + // Start observing the core and tracking the state of the client.
|
| + core_->AddObserver(this);
|
|
|
| - // Finally tell the Delegate that the initial policy is available.
|
| - OnPolicyUpdated(initial_policy.Pass());
|
| + if (core_->client()) {
|
| + OnCoreConnected(core_);
|
| + } else {
|
| + // Send the current schema to the backend, in case it has changed while the
|
| + // backend was initializing. OnCoreConnected() also does this if a client is
|
| + // already connected.
|
| + SetCurrentSchema();
|
| + }
|
| }
|
|
|
| -void ComponentCloudPolicyService::SetCurrentSchema(
|
| - const scoped_refptr<SchemaMap>& new_schema_map,
|
| - bool send_to_backend) {
|
| +void ComponentCloudPolicyService::SetCurrentSchema() {
|
| DCHECK(CalledOnValidThread());
|
| - DCHECK(is_initialized());
|
|
|
| scoped_ptr<PolicyNamespaceList> removed(new PolicyNamespaceList);
|
| PolicyNamespaceList added;
|
| + const scoped_refptr<SchemaMap>& new_schema_map =
|
| + schema_registry_->schema_map();
|
| new_schema_map->GetChanges(current_schema_map_, removed.get(), &added);
|
|
|
| current_schema_map_ = new_schema_map;
|
|
|
| - for (size_t i = 0; i < removed->size(); ++i) {
|
| - PolicyNamespaceKey key;
|
| - if (ToPolicyNamespaceKey((*removed)[i], &key))
|
| - client_->RemoveNamespaceToFetch(key);
|
| - }
|
| -
|
| - bool added_namespaces_to_client = false;
|
| - for (size_t i = 0; i < added.size(); ++i) {
|
| - PolicyNamespaceKey key;
|
| - if (ToPolicyNamespaceKey(added[i], &key)) {
|
| - client_->AddNamespaceToFetch(key);
|
| - added_namespaces_to_client = true;
|
| + if (core_->client()) {
|
| + for (size_t i = 0; i < removed->size(); ++i) {
|
| + PolicyNamespaceKey key;
|
| + if (ToPolicyNamespaceKey((*removed)[i], &key))
|
| + core_->client()->RemoveNamespaceToFetch(key);
|
| }
|
| - }
|
|
|
| - if (added_namespaces_to_client)
|
| - delegate_->OnComponentCloudPolicyRefreshNeeded();
|
| + bool added_namespaces_to_client = false;
|
| + for (size_t i = 0; i < added.size(); ++i) {
|
| + PolicyNamespaceKey key;
|
| + if (ToPolicyNamespaceKey(added[i], &key)) {
|
| + core_->client()->AddNamespaceToFetch(key);
|
| + added_namespaces_to_client = true;
|
| + }
|
| + }
|
|
|
| - if (send_to_backend) {
|
| - backend_task_runner_->PostTask(FROM_HERE,
|
| - base::Bind(&Backend::OnSchemasUpdated,
|
| - base::Unretained(backend_.get()),
|
| - current_schema_map_,
|
| - base::Passed(&removed)));
|
| + if (added_namespaces_to_client)
|
| + core_->RefreshSoon();
|
| }
|
| +
|
| + backend_task_runner_->PostTask(FROM_HERE,
|
| + base::Bind(&Backend::OnSchemasUpdated,
|
| + base::Unretained(backend_.get()),
|
| + current_schema_map_,
|
| + base::Passed(&removed)));
|
| }
|
|
|
| void ComponentCloudPolicyService::OnPolicyUpdated(
|
|
|