| Index: remoting/host/policy_watcher.cc
|
| diff --git a/remoting/host/policy_watcher.cc b/remoting/host/policy_watcher.cc
|
| index 2cf7be8889425262c78ef45fb113b347959b9b5b..60f14c9ceb5becd41b0456177c3139fc9d82d670 100644
|
| --- a/remoting/host/policy_watcher.cc
|
| +++ b/remoting/host/policy_watcher.cc
|
| @@ -21,6 +21,8 @@
|
| #include "components/policy/core/common/schema_registry.h"
|
| #include "policy/policy_constants.h"
|
| #include "remoting/host/dns_blackhole_checker.h"
|
| +#include "remoting/host/third_party_auth_config.h"
|
| +#include "remoting/protocol/port_range.h"
|
|
|
| #if !defined(NDEBUG)
|
| #include "base/json/json_reader.h"
|
| @@ -44,15 +46,15 @@ namespace {
|
| // Copies all policy values from one dictionary to another, using values from
|
| // |default_values| if they are not set in |from|.
|
| scoped_ptr<base::DictionaryValue> CopyValuesAndAddDefaults(
|
| - const base::DictionaryValue* from,
|
| - const base::DictionaryValue* default_values) {
|
| - scoped_ptr<base::DictionaryValue> to(default_values->DeepCopy());
|
| - for (base::DictionaryValue::Iterator i(*default_values); !i.IsAtEnd();
|
| + const base::DictionaryValue& from,
|
| + const base::DictionaryValue& default_values) {
|
| + scoped_ptr<base::DictionaryValue> to(default_values.DeepCopy());
|
| + for (base::DictionaryValue::Iterator i(default_values); !i.IsAtEnd();
|
| i.Advance()) {
|
| const base::Value* value = nullptr;
|
|
|
| // If the policy isn't in |from|, use the default.
|
| - if (!from->Get(i.key(), &value)) {
|
| + if (!from.Get(i.key(), &value)) {
|
| continue;
|
| }
|
|
|
| @@ -63,8 +65,8 @@ scoped_ptr<base::DictionaryValue> CopyValuesAndAddDefaults(
|
| #if !defined(NDEBUG)
|
| // Replace values with those specified in DebugOverridePolicies, if present.
|
| std::string policy_overrides;
|
| - if (from->GetString(key::kRemoteAccessHostDebugOverridePolicies,
|
| - &policy_overrides)) {
|
| + if (from.GetString(key::kRemoteAccessHostDebugOverridePolicies,
|
| + &policy_overrides)) {
|
| scoped_ptr<base::Value> value(base::JSONReader::Read(policy_overrides));
|
| const base::DictionaryValue* override_values;
|
| if (value && value->GetAsDictionary(&override_values)) {
|
| @@ -92,6 +94,56 @@ scoped_ptr<policy::SchemaRegistry> CreateSchemaRegistry() {
|
| return schema_registry.Pass();
|
| }
|
|
|
| +scoped_ptr<base::DictionaryValue> CopyChromotingPoliciesIntoDictionary(
|
| + const policy::PolicyMap& current) {
|
| + const char kPolicyNameSubstring[] = "RemoteAccessHost";
|
| + scoped_ptr<base::DictionaryValue> policy_dict(new base::DictionaryValue());
|
| + for (auto it = current.begin(); it != current.end(); ++it) {
|
| + const std::string& key = it->first;
|
| + const base::Value* value = it->second.value;
|
| +
|
| + // Copying only Chromoting-specific policies helps avoid false alarms
|
| + // raised by NormalizePolicies below (such alarms shutdown the host).
|
| + // TODO(lukasza): Removing this somewhat brittle filtering will be possible
|
| + // after having separate, Chromoting-specific schema.
|
| + if (key.find(kPolicyNameSubstring) != std::string::npos) {
|
| + policy_dict->Set(key, value->DeepCopy());
|
| + }
|
| + }
|
| +
|
| + return policy_dict.Pass();
|
| +}
|
| +
|
| +// Takes a dictionary containing only 1) recognized policy names and 2)
|
| +// well-typed policy values and further verifies policy contents.
|
| +bool VerifyWellformedness(const base::DictionaryValue& changed_policies) {
|
| + // Verify ThirdPartyAuthConfig policy.
|
| + ThirdPartyAuthConfig not_used;
|
| + switch (ThirdPartyAuthConfig::Parse(changed_policies, ¬_used)) {
|
| + case ThirdPartyAuthConfig::NoPolicy:
|
| + case ThirdPartyAuthConfig::ParsingSuccess:
|
| + break; // Well-formed.
|
| + case ThirdPartyAuthConfig::InvalidPolicy:
|
| + return false; // Malformed.
|
| + default:
|
| + NOTREACHED();
|
| + return false;
|
| + }
|
| +
|
| + // Verify UdpPortRange policy.
|
| + std::string udp_port_range_string;
|
| + PortRange udp_port_range;
|
| + if (changed_policies.GetString(policy::key::kRemoteAccessHostUdpPortRange,
|
| + &udp_port_range_string)) {
|
| + if (!PortRange::Parse(udp_port_range_string, &udp_port_range)) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + // Report that all the policies were well-formed.
|
| + return true;
|
| +}
|
| +
|
| } // namespace
|
|
|
| void PolicyWatcher::StartWatching(
|
| @@ -114,36 +166,6 @@ void PolicyWatcher::StartWatching(
|
| }
|
| }
|
|
|
| -void PolicyWatcher::UpdatePolicies(
|
| - const base::DictionaryValue* new_policies_raw) {
|
| - DCHECK(CalledOnValidThread());
|
| -
|
| - // Use default values for any missing policies.
|
| - scoped_ptr<base::DictionaryValue> new_policies =
|
| - CopyValuesAndAddDefaults(new_policies_raw, default_values_.get());
|
| -
|
| - // Find the changed policies.
|
| - scoped_ptr<base::DictionaryValue> changed_policies(
|
| - new base::DictionaryValue());
|
| - base::DictionaryValue::Iterator iter(*new_policies);
|
| - while (!iter.IsAtEnd()) {
|
| - base::Value* old_policy;
|
| - if (!(old_policies_->Get(iter.key(), &old_policy) &&
|
| - old_policy->Equals(&iter.value()))) {
|
| - changed_policies->Set(iter.key(), iter.value().DeepCopy());
|
| - }
|
| - iter.Advance();
|
| - }
|
| -
|
| - // Save the new policies.
|
| - old_policies_.swap(new_policies);
|
| -
|
| - // Notify our client of the changed policies.
|
| - if (!changed_policies->empty()) {
|
| - policy_updated_callback_.Run(changed_policies.Pass());
|
| - }
|
| -}
|
| -
|
| void PolicyWatcher::SignalPolicyError() {
|
| old_policies_->Clear();
|
| policy_error_callback_.Run();
|
| @@ -201,24 +223,7 @@ const policy::Schema* PolicyWatcher::GetPolicySchema() const {
|
| return owned_schema_registry_->schema_map()->GetSchema(GetPolicyNamespace());
|
| }
|
|
|
| -void PolicyWatcher::OnPolicyUpdated(const policy::PolicyNamespace& ns,
|
| - const policy::PolicyMap& previous,
|
| - const policy::PolicyMap& current) {
|
| - const char kPolicyNamePrefix[] = "RemoteAccessHost";
|
| - scoped_ptr<base::DictionaryValue> policy_dict(new base::DictionaryValue());
|
| - for (auto it = current.begin(); it != current.end(); ++it) {
|
| - const std::string& key = it->first;
|
| - const base::Value* value = it->second.value;
|
| -
|
| - // Copying only Chromoting-specific policies helps avoid false alarms
|
| - // raised by Schema::Normalize below (such alarms shutdown the host).
|
| - // TODO(lukasza): Removing this somewhat brittle filtering will be possible
|
| - // after having separate, Chromoting-specific schema.
|
| - if (key.find(kPolicyNamePrefix) != std::string::npos) {
|
| - policy_dict->Set(key, value->DeepCopy());
|
| - }
|
| - }
|
| -
|
| +bool PolicyWatcher::NormalizePolicies(base::DictionaryValue* policy_dict) {
|
| // Allowing unrecognized policy names allows presence of
|
| // 1) comments (i.e. JSON of the form: { "_comment": "blah", ... }),
|
| // 2) policies intended for future/newer versions of the host,
|
| @@ -231,16 +236,95 @@ void PolicyWatcher::OnPolicyUpdated(const policy::PolicyNamespace& ns,
|
| std::string error;
|
| bool changed = false;
|
| const policy::Schema* schema = GetPolicySchema();
|
| - if (schema->Normalize(policy_dict.get(), strategy, &path, &error, &changed)) {
|
| + if (schema->Normalize(policy_dict, strategy, &path, &error, &changed)) {
|
| if (changed) {
|
| LOG(WARNING) << "Unknown (unrecognized or unsupported) policy: " << path
|
| << ": " << error;
|
| }
|
| - UpdatePolicies(policy_dict.get());
|
| + return true;
|
| } else {
|
| LOG(ERROR) << "Invalid policy contents: " << path << ": " << error;
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +namespace {
|
| +void CopyDictionaryValue(const base::DictionaryValue& from,
|
| + base::DictionaryValue& to,
|
| + std::string key) {
|
| + const base::Value* value;
|
| + if (from.Get(key, &value)) {
|
| + to.Set(key, value->DeepCopy());
|
| + }
|
| +}
|
| +} // namespace
|
| +
|
| +scoped_ptr<base::DictionaryValue>
|
| +PolicyWatcher::StoreNewAndReturnChangedPolicies(
|
| + scoped_ptr<base::DictionaryValue> new_policies) {
|
| + // Find the changed policies.
|
| + scoped_ptr<base::DictionaryValue> changed_policies(
|
| + new base::DictionaryValue());
|
| + base::DictionaryValue::Iterator iter(*new_policies);
|
| + while (!iter.IsAtEnd()) {
|
| + base::Value* old_policy;
|
| + if (!(old_policies_->Get(iter.key(), &old_policy) &&
|
| + old_policy->Equals(&iter.value()))) {
|
| + changed_policies->Set(iter.key(), iter.value().DeepCopy());
|
| + }
|
| + iter.Advance();
|
| + }
|
| +
|
| + // If one of ThirdPartyAuthConfig policies changed, we need to include all.
|
| + if (changed_policies->HasKey(key::kRemoteAccessHostTokenUrl) ||
|
| + changed_policies->HasKey(key::kRemoteAccessHostTokenValidationUrl) ||
|
| + changed_policies->HasKey(
|
| + key::kRemoteAccessHostTokenValidationCertificateIssuer)) {
|
| + CopyDictionaryValue(*new_policies, *changed_policies,
|
| + key::kRemoteAccessHostTokenUrl);
|
| + CopyDictionaryValue(*new_policies, *changed_policies,
|
| + key::kRemoteAccessHostTokenValidationUrl);
|
| + CopyDictionaryValue(*new_policies, *changed_policies,
|
| + key::kRemoteAccessHostTokenValidationCertificateIssuer);
|
| + }
|
| +
|
| + // Save the new policies.
|
| + old_policies_.swap(new_policies);
|
| +
|
| + return changed_policies.Pass();
|
| +}
|
| +
|
| +void PolicyWatcher::OnPolicyUpdated(const policy::PolicyNamespace& ns,
|
| + const policy::PolicyMap& previous,
|
| + const policy::PolicyMap& current) {
|
| + scoped_ptr<base::DictionaryValue> new_policies =
|
| + CopyChromotingPoliciesIntoDictionary(current);
|
| +
|
| + // Check for mistyped values and get rid of unknown policies.
|
| + if (!NormalizePolicies(new_policies.get())) {
|
| + SignalPolicyError();
|
| + return;
|
| + }
|
| +
|
| + // Use default values for any missing policies.
|
| + scoped_ptr<base::DictionaryValue> filled_policies =
|
| + CopyValuesAndAddDefaults(*new_policies, *default_values_);
|
| +
|
| + // Limit reporting to only the policies that were changed.
|
| + scoped_ptr<base::DictionaryValue> changed_policies =
|
| + StoreNewAndReturnChangedPolicies(filled_policies.Pass());
|
| + if (changed_policies->empty()) {
|
| + return;
|
| + }
|
| +
|
| + // Verify that we are calling the callback with valid policies.
|
| + if (!VerifyWellformedness(*changed_policies)) {
|
| SignalPolicyError();
|
| + return;
|
| }
|
| +
|
| + // Notify our client of the changed policies.
|
| + policy_updated_callback_.Run(changed_policies.Pass());
|
| }
|
|
|
| void PolicyWatcher::OnPolicyServiceInitialized(policy::PolicyDomain domain) {
|
|
|