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) { |