Commit 0d40d8ac authored by lukasza's avatar lukasza Committed by Commit bot

Malformed PortRange or ThirdPartyAuthConfig should trigger OnPolicyError.

Before this change a malformed value of RemoteAccessHostUdpPortRange policy
(i.e. "123456-blah") was ignored and a default value was used.  Similarily for
the 3 third-party-auth-config policies (RemoteAccessHostTokenUrl,
...TokenValidationUrl and ...TokenValidationCertificateIssuer) we were falling
back to a secure default, but not reporting a policy error.  After this change
such malformed values will trigger an OnPolicyError callback.

Notes:
- Guaranteeing that PolicyWatcher always returns valid policy values, removes
  the need for a "rejecting" Me2MeHostAuthenticatorFactory.
- Moving PortRange and ThirdPartyAuthConfig to separate compilation units
  helps readability elsewhere + encourages better unit tests coverage.
- Initially I tried to wrap all policies in a new ChromotingPolicies class,
  but eventually went back to prevalidating and passing base::DictionaryValue.
  - Arguments for using ChromotingPolicies:
    - Helps avoid overtesting in policy_watcher_unittests.cc (i.e. helps focus
      the tests on a single policy value).
  - Arguments for using base::DictionaryValue:
    - Minimizes changes.
    - Keeps things simple (as opposed to having to introduce a custom
      equivalent of optional<T> [nothing similar present in Chromium AFAICT]).
  - Neutral:
    - Strong-typing of ChromotingPolicies didn't help readability as much as I
      expected and hoped for.

BUG=427513
TEST=remoting_unittests

Review URL: https://codereview.chromium.org/966433002

Cr-Commit-Position: refs/heads/master@{#318910}
parent 3a2bce8a
......@@ -229,8 +229,10 @@ void It2MeHost::FinishConnect() {
protocol::NetworkSettings::NAT_TRAVERSAL_FULL :
protocol::NetworkSettings::NAT_TRAVERSAL_DISABLED);
if (!nat_traversal_enabled_) {
network_settings.min_port = protocol::NetworkSettings::kDefaultMinPort;
network_settings.max_port = protocol::NetworkSettings::kDefaultMaxPort;
network_settings.port_range.min_port =
protocol::NetworkSettings::kDefaultMinPort;
network_settings.port_range.max_port =
protocol::NetworkSettings::kDefaultMaxPort;
}
// Create the host.
......
......@@ -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, &not_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) {
......
......@@ -80,13 +80,20 @@ class PolicyWatcher : public policy::PolicyService::Observer,
private:
friend class PolicyWatcherTest;
// Takes the policy dictionary from the OS specific store and extracts the
// relevant policies.
void UpdatePolicies(const base::DictionaryValue* new_policy);
// Gets Chromoting schema stored inside |owned_schema_registry_|.
const policy::Schema* GetPolicySchema() const;
// Simplifying wrapper around Schema::Normalize.
// - Returns false if |dict| is invalid (i.e. contains mistyped policy
// values).
// - Returns true if |dict| was valid or got normalized.
bool NormalizePolicies(base::DictionaryValue* dict);
// Stores |new_policies| into |old_policies_|. Returns dictionary with items
// from |new_policies| that are different from the old |old_policies_|.
scoped_ptr<base::DictionaryValue> StoreNewAndReturnChangedPolicies(
scoped_ptr<base::DictionaryValue> new_policies);
// Signals policy error to the registered |PolicyErrorCallback|.
void SignalPolicyError();
......
This diff is collapsed.
......@@ -64,6 +64,7 @@
#include "remoting/host/shutdown_watchdog.h"
#include "remoting/host/signaling_connector.h"
#include "remoting/host/single_window_desktop_environment.h"
#include "remoting/host/third_party_auth_config.h"
#include "remoting/host/token_validator_factory_impl.h"
#include "remoting/host/usage_stats_consent.h"
#include "remoting/host/username.h"
......@@ -71,6 +72,7 @@
#include "remoting/protocol/me2me_host_authenticator_factory.h"
#include "remoting/protocol/network_settings.h"
#include "remoting/protocol/pairing_registry.h"
#include "remoting/protocol/port_range.h"
#include "remoting/protocol/token_validator.h"
#include "remoting/signaling/xmpp_signal_strategy.h"
......@@ -339,8 +341,7 @@ class HostProcess : public ConfigWatcher::Delegate,
bool host_username_match_required_;
bool allow_nat_traversal_;
bool allow_relay_;
uint16 min_udp_port_;
uint16 max_udp_port_;
PortRange udp_port_range_;
std::string talkgadget_prefix_;
bool allow_pairing_;
......@@ -394,8 +395,6 @@ HostProcess::HostProcess(scoped_ptr<ChromotingHostContext> context,
host_username_match_required_(false),
allow_nat_traversal_(true),
allow_relay_(true),
min_udp_port_(0),
max_udp_port_(0),
allow_pairing_(true),
curtain_required_(false),
enable_gnubby_auth_(false),
......@@ -672,7 +671,7 @@ void HostProcess::CreateAuthenticatorFactory() {
scoped_ptr<protocol::AuthenticatorFactory> factory;
if (third_party_auth_config_.is_empty()) {
if (third_party_auth_config_.is_null()) {
scoped_refptr<PairingRegistry> pairing_registry;
if (allow_pairing_) {
// On Windows |pairing_registry_| is initialized in
......@@ -696,7 +695,10 @@ void HostProcess::CreateAuthenticatorFactory() {
host_secret_hash_, pairing_registry);
host_->set_pairing_registry(pairing_registry);
} else if (third_party_auth_config_.is_valid()) {
} else {
DCHECK(third_party_auth_config_.token_url.is_valid());
DCHECK(third_party_auth_config_.token_validation_url.is_valid());
scoped_ptr<protocol::TokenValidatorFactory> token_validator_factory(
new TokenValidatorFactoryImpl(
third_party_auth_config_,
......@@ -704,17 +706,6 @@ void HostProcess::CreateAuthenticatorFactory() {
factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth(
use_service_account_, host_owner_, local_certificate, key_pair_,
token_validator_factory.Pass());
} else {
// TODO(rmsousa): If the policy is bad the host should not go online. It
// should keep running, but not connected, until the policies are fixed.
// Having it show up as online and then reject all clients is misleading.
LOG(ERROR) << "One of the third-party token URLs is empty or invalid. "
<< "Host will reject all clients until policies are corrected. "
<< "TokenUrl: " << third_party_auth_config_.token_url << ", "
<< "TokenValidationUrl: "
<< third_party_auth_config_.token_validation_url;
factory = protocol::Me2MeHostAuthenticatorFactory::CreateRejecting();
}
#if defined(OS_POSIX)
......@@ -1189,34 +1180,15 @@ bool HostProcess::OnUdpPortPolicyUpdate(base::DictionaryValue* policies) {
// Returns true if the host has to be restarted after this policy update.
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
std::string udp_port_range;
std::string string_value;
if (!policies->GetString(policy::key::kRemoteAccessHostUdpPortRange,
&udp_port_range)) {
&string_value)) {
return false;
}
// Use default values if policy setting is empty or invalid.
uint16 min_udp_port = 0;
uint16 max_udp_port = 0;
if (!udp_port_range.empty() &&
!NetworkSettings::ParsePortRange(udp_port_range, &min_udp_port,
&max_udp_port)) {
LOG(WARNING) << "Invalid port range policy: \"" << udp_port_range
<< "\". Using default values.";
}
if (min_udp_port_ != min_udp_port || max_udp_port_ != max_udp_port) {
if (min_udp_port != 0 && max_udp_port != 0) {
HOST_LOG << "Policy restricts UDP port range to [" << min_udp_port
<< ", " << max_udp_port << "]";
} else {
HOST_LOG << "Policy does not restrict UDP port range.";
}
min_udp_port_ = min_udp_port;
max_udp_port_ = max_udp_port;
return true;
}
return false;
DCHECK(PortRange::Parse(string_value, &udp_port_range_));
HOST_LOG << "Policy restricts UDP port range to: " << udp_port_range_;
return true;
}
bool HostProcess::OnCurtainPolicyUpdate(base::DictionaryValue* policies) {
......@@ -1274,39 +1246,18 @@ bool HostProcess::OnHostTalkGadgetPrefixPolicyUpdate(
}
bool HostProcess::OnHostTokenUrlPolicyUpdate(base::DictionaryValue* policies) {
// Returns true if the host has to be restarted after this policy update.
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
bool token_policy_changed = false;
std::string token_url_string;
if (policies->GetString(policy::key::kRemoteAccessHostTokenUrl,
&token_url_string)) {
token_policy_changed = true;
third_party_auth_config_.token_url = GURL(token_url_string);
}
std::string token_validation_url_string;
if (policies->GetString(policy::key::kRemoteAccessHostTokenValidationUrl,
&token_validation_url_string)) {
token_policy_changed = true;
third_party_auth_config_.token_validation_url =
GURL(token_validation_url_string);
}
if (policies->GetString(
policy::key::kRemoteAccessHostTokenValidationCertificateIssuer,
&third_party_auth_config_.token_validation_cert_issuer)) {
token_policy_changed = true;
}
if (token_policy_changed) {
HOST_LOG << "Policy sets third-party token URLs: "
<< "TokenUrl: "
<< third_party_auth_config_.token_url << ", "
<< "TokenValidationUrl: "
<< third_party_auth_config_.token_validation_url << ", "
<< "TokenValidationCertificateIssuer: "
<< third_party_auth_config_.token_validation_cert_issuer;
switch (ThirdPartyAuthConfig::Parse(*policies, &third_party_auth_config_)) {
case ThirdPartyAuthConfig::NoPolicy:
return false;
case ThirdPartyAuthConfig::ParsingSuccess:
HOST_LOG << "Policy sets third-party token URLs: "
<< third_party_auth_config_;
return true;
case ThirdPartyAuthConfig::InvalidPolicy:
default:
NOTREACHED();
return false;
}
return token_policy_changed;
}
bool HostProcess::OnPairingPolicyUpdate(base::DictionaryValue* policies) {
......@@ -1392,15 +1343,14 @@ void HostProcess::StartHost() {
NetworkSettings network_settings(network_flags);
if (min_udp_port_ && max_udp_port_) {
network_settings.min_port = min_udp_port_;
network_settings.max_port = max_udp_port_;
if (!udp_port_range_.is_null()) {
network_settings.port_range = udp_port_range_;
} else if (!allow_nat_traversal_) {
// For legacy reasons we have to restrict the port range to a set of default
// values when nat traversal is disabled, even if the port range was not
// set in policy.
network_settings.min_port = NetworkSettings::kDefaultMinPort;
network_settings.max_port = NetworkSettings::kDefaultMaxPort;
network_settings.port_range.min_port = NetworkSettings::kDefaultMinPort;
network_settings.port_range.max_port = NetworkSettings::kDefaultMaxPort;
}
host_.reset(new ChromotingHost(
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/third_party_auth_config.h"
#include "base/logging.h"
#include "base/values.h"
#include "policy/policy_constants.h"
namespace remoting {
namespace {
bool ParseUrlPolicy(const std::string& str, GURL* out) {
if (str.empty()) {
*out = GURL();
return true;
}
GURL gurl(str);
if (!gurl.is_valid()) {
LOG(ERROR) << "Not a valid URL: " << str;
return false;
}
// We validate https-vs-http only on Release builds to help with manual testing.
#if defined(NDEBUG)
if (!gurl.SchemeIsSecure()) {
LOG(ERROR) << "Not a secure URL: " << str;
return false;
}
#endif
*out = gurl;
return true;
}
} // namespace
bool ThirdPartyAuthConfig::ParseStrings(
const std::string& token_url,
const std::string& token_validation_url,
const std::string& token_validation_cert_issuer,
ThirdPartyAuthConfig* result) {
ThirdPartyAuthConfig tmp;
// Extract raw values for the 3 individual fields.
bool urls_valid = true;
urls_valid &= ParseUrlPolicy(token_url, &tmp.token_url);
urls_valid &= ParseUrlPolicy(token_validation_url, &tmp.token_validation_url);
if (!urls_valid) {
return false;
}
tmp.token_validation_cert_issuer = token_validation_cert_issuer;
// Validate inter-dependencies between the 3 fields.
if (tmp.token_url.is_empty() ^ tmp.token_validation_url.is_empty()) {
LOG(ERROR) << "TokenUrl and TokenValidationUrl "
<< "have to be specified together.";
return false;
}
if (!tmp.token_validation_cert_issuer.empty() && tmp.token_url.is_empty()) {
LOG(ERROR) << "TokenValidationCertificateIssuer cannot be used "
<< "without TokenUrl and TokenValidationUrl.";
return false;
}
*result = tmp;
return true;
}
namespace {
void ExtractHelper(const base::DictionaryValue& policy_dict,
const std::string& policy_name,
bool* policy_present,
std::string* policy_value) {
if (policy_dict.GetString(policy_name, policy_value)) {
*policy_present = true;
} else {
policy_value->clear();
}
}
} // namespace
bool ThirdPartyAuthConfig::ExtractStrings(
const base::DictionaryValue& policy_dict,
std::string* token_url,
std::string* token_validation_url,
std::string* token_validation_cert_issuer) {
bool policies_present = false;
ExtractHelper(policy_dict, policy::key::kRemoteAccessHostTokenUrl,
&policies_present, token_url);
ExtractHelper(policy_dict, policy::key::kRemoteAccessHostTokenValidationUrl,
&policies_present, token_validation_url);
ExtractHelper(policy_dict,
policy::key::kRemoteAccessHostTokenValidationCertificateIssuer,
&policies_present, token_validation_cert_issuer);
return policies_present;
}
ThirdPartyAuthConfig::ParseStatus ThirdPartyAuthConfig::Parse(
const base::DictionaryValue& policy_dict,
ThirdPartyAuthConfig* result) {
// Extract 3 individial policy values.
std::string token_url;
std::string token_validation_url;
std::string token_validation_cert_issuer;
if (!ThirdPartyAuthConfig::ExtractStrings(policy_dict, &token_url,
&token_validation_url,
&token_validation_cert_issuer)) {
return NoPolicy;
}
// Parse the policy value.
if (!ThirdPartyAuthConfig::ParseStrings(token_url, token_validation_url,
token_validation_cert_issuer,
result)) {
return InvalidPolicy;
}
return ParsingSuccess;
}
std::ostream& operator<<(std::ostream& os, const ThirdPartyAuthConfig& cfg) {
if (cfg.is_null()) {
os << "<no 3rd party auth config specified>";
} else {
os << "TokenUrl = <" << cfg.token_url << ">, ";
os << "TokenValidationUrl = <" << cfg.token_validation_url << ">, ";
os << "TokenValidationCertificateIssuer = <"
<< cfg.token_validation_cert_issuer << ">";
}
return os;
}
} // namespace remoting
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef REMOTING_HOST_THIRD_PARTY_AUTH_CONFIG_H_
#define REMOTING_HOST_THIRD_PARTY_AUTH_CONFIG_H_
#include <ostream>
#include <string>
#include "base/gtest_prod_util.h"
#include "url/gurl.h"
namespace base {
class DictionaryValue;
} // namespace base
namespace remoting {
struct ThirdPartyAuthConfig {
GURL token_url;
GURL token_validation_url;
std::string token_validation_cert_issuer;
inline bool is_null() const {
return token_url.is_empty() && token_validation_url.is_empty();
}
// Status of Parse method call.
enum ParseStatus {
// |policy_dict| contains invalid entries (i.e. malformed urls).
// |result| has not been modified.
InvalidPolicy,
// |policy_dict| doesn't contain any ThirdPartyAuthConfig-related entries.
// |result| has not been modified.
NoPolicy,
// |policy_dict| contains valid entries that have been stored into |result|.
ParsingSuccess,
};
static ParseStatus Parse(const base::DictionaryValue& policy_dict,
ThirdPartyAuthConfig* result);
private:
// Returns false and doesn't modify |result| if parsing fails (i.e. some input
// values are invalid).
static bool ParseStrings(const std::string& token_url,
const std::string& token_validation_url,
const std::string& token_validation_cert_issuer,
ThirdPartyAuthConfig* result);
FRIEND_TEST_ALL_PREFIXES(InvalidUrlTest, ParseInvalidUrl);