Commit 5a852d06 authored by Rohit Rao's avatar Rohit Rao Committed by Commit Bot

Updates PolicyLoaderIOS to support reading JSON-encoded dictionaries.

The AppConfig spec does not support complex structured data, so we will
encode "dict" policies as JSON strings.  PolicyLoaderIOS has been
updated to compare the type of the loaded policy data against the
expected type for that policy and attempt a conversion if the types
don't match.

BUG=1065906

Change-Id: Ic3ef56baba05bc7b4753c872ab8817a41d5138cd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2135827Reviewed-by: default avatarOwen Min <zmin@chromium.org>
Commit-Queue: Rohit Rao <rohitrao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#757071}
parent 00851323
...@@ -14,11 +14,14 @@ ...@@ -14,11 +14,14 @@
namespace policy { namespace policy {
class SchemaRegistry;
// A policy loader that loads policy from the managed app configuration // A policy loader that loads policy from the managed app configuration
// introduced in iOS 7. // introduced in iOS 7.
class POLICY_EXPORT PolicyLoaderIOS : public AsyncPolicyLoader { class POLICY_EXPORT PolicyLoaderIOS : public AsyncPolicyLoader {
public: public:
explicit PolicyLoaderIOS( explicit PolicyLoaderIOS(
SchemaRegistry* registry,
scoped_refptr<base::SequencedTaskRunner> task_runner); scoped_refptr<base::SequencedTaskRunner> task_runner);
~PolicyLoaderIOS() override; ~PolicyLoaderIOS() override;
...@@ -31,8 +34,18 @@ class POLICY_EXPORT PolicyLoaderIOS : public AsyncPolicyLoader { ...@@ -31,8 +34,18 @@ class POLICY_EXPORT PolicyLoaderIOS : public AsyncPolicyLoader {
void UserDefaultsChanged(); void UserDefaultsChanged();
// Loads the Chrome policies in |dictionary| into the given |bundle|. // Loads the Chrome policies in |dictionary| into the given |bundle|.
static void LoadNSDictionaryToPolicyBundle(NSDictionary* dictionary, void LoadNSDictionaryToPolicyBundle(NSDictionary* dictionary,
PolicyBundle* bundle); PolicyBundle* bundle);
// Validates the given policy data against the stored |schema_|, converting
// data to the expected type if necessary. The returned value is suitable for
// adding to a PolicyMap.
std::unique_ptr<base::Value> ConvertPolicyDataIfNecessary(
const std::string& key,
const base::Value& value);
// The schema used by |ValidatePolicyData()|.
const Schema* policy_schema_;
// Used to manage the registration for NSNotificationCenter notifications. // Used to manage the registration for NSNotificationCenter notifications.
__strong id notification_observer_; __strong id notification_observer_;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#include "base/bind.h" #include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/location.h" #include "base/location.h"
#include "base/logging.h" #include "base/logging.h"
#import "base/mac/foundation_util.h" #import "base/mac/foundation_util.h"
...@@ -20,6 +21,8 @@ ...@@ -20,6 +21,8 @@
#import "components/policy/core/common/policy_loader_ios_constants.h" #import "components/policy/core/common/policy_loader_ios_constants.h"
#include "components/policy/core/common/policy_map.h" #include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_namespace.h" #include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/schema.h"
#include "components/policy/core/common/schema_registry.h"
#include "components/policy/policy_constants.h" #include "components/policy/policy_constants.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
...@@ -83,9 +86,12 @@ ...@@ -83,9 +86,12 @@
namespace policy { namespace policy {
PolicyLoaderIOS::PolicyLoaderIOS( PolicyLoaderIOS::PolicyLoaderIOS(
SchemaRegistry* registry,
scoped_refptr<base::SequencedTaskRunner> task_runner) scoped_refptr<base::SequencedTaskRunner> task_runner)
: AsyncPolicyLoader(task_runner), : AsyncPolicyLoader(task_runner), weak_factory_(this) {
weak_factory_(this) {} PolicyNamespace ns(POLICY_DOMAIN_CHROME, std::string());
policy_schema_ = registry->schema_map()->GetSchema(ns);
}
PolicyLoaderIOS::~PolicyLoaderIOS() { PolicyLoaderIOS::~PolicyLoaderIOS() {
DCHECK(task_runner()->RunsTasksInCurrentSequence()); DCHECK(task_runner()->RunsTasksInCurrentSequence());
...@@ -136,7 +142,6 @@ void PolicyLoaderIOS::UserDefaultsChanged() { ...@@ -136,7 +142,6 @@ void PolicyLoaderIOS::UserDefaultsChanged() {
Reload(false); Reload(false);
} }
// static
void PolicyLoaderIOS::LoadNSDictionaryToPolicyBundle(NSDictionary* dictionary, void PolicyLoaderIOS::LoadNSDictionaryToPolicyBundle(NSDictionary* dictionary,
PolicyBundle* bundle) { PolicyBundle* bundle) {
// NSDictionary is toll-free bridged to CFDictionaryRef, which is a // NSDictionary is toll-free bridged to CFDictionaryRef, which is a
...@@ -150,9 +155,34 @@ void PolicyLoaderIOS::LoadNSDictionaryToPolicyBundle(NSDictionary* dictionary, ...@@ -150,9 +155,34 @@ void PolicyLoaderIOS::LoadNSDictionaryToPolicyBundle(NSDictionary* dictionary,
dict->RemoveKey(base::SysNSStringToUTF8(kPolicyLoaderIOSLoadPolicyKey)); dict->RemoveKey(base::SysNSStringToUTF8(kPolicyLoaderIOSLoadPolicyKey));
PolicyMap& map = bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, "")); PolicyMap& map = bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
map.LoadFrom(dict, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE, for (const auto& it : dict->DictItems()) {
POLICY_SOURCE_PLATFORM); map.Set(it.first, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
POLICY_SOURCE_PLATFORM,
ConvertPolicyDataIfNecessary(it.first, it.second), nullptr);
}
}
}
std::unique_ptr<base::Value> PolicyLoaderIOS::ConvertPolicyDataIfNecessary(
const std::string& key,
const base::Value& value) {
const Schema schema = policy_schema_->GetKnownProperty(key);
if (!schema.valid()) {
return value.CreateDeepCopy();
}
// Handle the case of a JSON-encoded string for a dict policy.
if (schema.type() == base::Value::Type::DICTIONARY && value.is_string()) {
base::Optional<base::Value> decoded_value = base::JSONReader::Read(
value.GetString(), base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
if (decoded_value.has_value()) {
return base::Value::ToUniquePtrValue(std::move(decoded_value.value()));
}
} }
// Otherwise return an unchanged value.
return value.CreateDeepCopy();
} }
} // namespace policy } // namespace policy
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/json/json_string_value_serializer.h"
#include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_cftyperef.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/sequenced_task_runner.h" #include "base/sequenced_task_runner.h"
...@@ -34,21 +35,9 @@ namespace policy { ...@@ -34,21 +35,9 @@ namespace policy {
namespace { namespace {
// Creates a new PolicyLoaderIOS and verifies that it does not load any
// policies.
void VerifyNoPoliciesAreLoaded() {
PolicyBundle empty;
scoped_refptr<base::TestSimpleTaskRunner> taskRunner =
new base::TestSimpleTaskRunner();
PolicyLoaderIOS loader(taskRunner);
std::unique_ptr<PolicyBundle> bundle = loader.Load();
ASSERT_TRUE(bundle);
EXPECT_TRUE(bundle->Equals(empty));
}
class TestHarness : public PolicyProviderTestHarness { class TestHarness : public PolicyProviderTestHarness {
public: public:
TestHarness(); TestHarness(bool encode_complex_data_as_json);
~TestHarness() override; ~TestHarness() override;
void SetUp() override; void SetUp() override;
...@@ -71,6 +60,7 @@ class TestHarness : public PolicyProviderTestHarness { ...@@ -71,6 +60,7 @@ class TestHarness : public PolicyProviderTestHarness {
const base::DictionaryValue* policy_value) override; const base::DictionaryValue* policy_value) override;
static PolicyProviderTestHarness* Create(); static PolicyProviderTestHarness* Create();
static PolicyProviderTestHarness* CreateWithJSONEncoding();
private: private:
// Merges the policies in |policy| into the current policy dictionary // Merges the policies in |policy| into the current policy dictionary
...@@ -78,13 +68,18 @@ class TestHarness : public PolicyProviderTestHarness { ...@@ -78,13 +68,18 @@ class TestHarness : public PolicyProviderTestHarness {
// exists. // exists.
void AddPolicies(NSDictionary* policy); void AddPolicies(NSDictionary* policy);
// If true, the test harness will encode complex data (dicts and lists) as
// JSON strings.
bool encode_complex_data_as_json_;
DISALLOW_COPY_AND_ASSIGN(TestHarness); DISALLOW_COPY_AND_ASSIGN(TestHarness);
}; };
TestHarness::TestHarness() TestHarness::TestHarness(bool encode_complex_data_as_json)
: PolicyProviderTestHarness(POLICY_LEVEL_MANDATORY, : PolicyProviderTestHarness(POLICY_LEVEL_MANDATORY,
POLICY_SCOPE_MACHINE, POLICY_SCOPE_MACHINE,
POLICY_SOURCE_PLATFORM) {} POLICY_SOURCE_PLATFORM),
encode_complex_data_as_json_(encode_complex_data_as_json) {}
TestHarness::~TestHarness() { TestHarness::~TestHarness() {
// Cleanup any policies left from the test. // Cleanup any policies left from the test.
...@@ -106,7 +101,7 @@ ConfigurationPolicyProvider* TestHarness::CreateProvider( ...@@ -106,7 +101,7 @@ ConfigurationPolicyProvider* TestHarness::CreateProvider(
SchemaRegistry* registry, SchemaRegistry* registry,
scoped_refptr<base::SequencedTaskRunner> task_runner) { scoped_refptr<base::SequencedTaskRunner> task_runner) {
return new AsyncPolicyProvider( return new AsyncPolicyProvider(
registry, std::make_unique<PolicyLoaderIOS>(task_runner)); registry, std::make_unique<PolicyLoaderIOS>(registry, task_runner));
} }
void TestHarness::InstallEmptyPolicy() { void TestHarness::InstallEmptyPolicy() {
...@@ -150,14 +145,29 @@ void TestHarness::InstallDictionaryPolicy( ...@@ -150,14 +145,29 @@ void TestHarness::InstallDictionaryPolicy(
const std::string& policy_name, const std::string& policy_name,
const base::DictionaryValue* policy_value) { const base::DictionaryValue* policy_value) {
NSString* key = base::SysUTF8ToNSString(policy_name); NSString* key = base::SysUTF8ToNSString(policy_name);
base::ScopedCFTypeRef<CFPropertyListRef> value(
ValueToProperty(*policy_value)); if (encode_complex_data_as_json_) {
AddPolicies(@{key : (__bridge NSDictionary*)(value.get())}); // Convert |policy_value| to a JSON-encoded string.
std::string json_string;
JSONStringValueSerializer serializer(&json_string);
ASSERT_TRUE(serializer.Serialize(*policy_value));
AddPolicies(@{key : base::SysUTF8ToNSString(json_string)});
} else {
base::ScopedCFTypeRef<CFPropertyListRef> value(
ValueToProperty(*policy_value));
AddPolicies(@{key : (__bridge NSDictionary*)(value.get())});
}
} }
// static // static
PolicyProviderTestHarness* TestHarness::Create() { PolicyProviderTestHarness* TestHarness::Create() {
return new TestHarness(); return new TestHarness(false);
}
// static
PolicyProviderTestHarness* TestHarness::CreateWithJSONEncoding() {
return new TestHarness(true);
} }
void TestHarness::AddPolicies(NSDictionary* policy) { void TestHarness::AddPolicies(NSDictionary* policy) {
...@@ -182,14 +192,60 @@ INSTANTIATE_TEST_SUITE_P(PolicyProviderIOSChromePolicyTest, ...@@ -182,14 +192,60 @@ INSTANTIATE_TEST_SUITE_P(PolicyProviderIOSChromePolicyTest,
ConfigurationPolicyProviderTest, ConfigurationPolicyProviderTest,
testing::Values(TestHarness::Create)); testing::Values(TestHarness::Create));
using PolicyLoaderIOSTest = PlatformTest; INSTANTIATE_TEST_SUITE_P(PolicyProviderIOSChromePolicyJSONTest,
ConfigurationPolicyProviderTest,
testing::Values(TestHarness::CreateWithJSONEncoding));
class PolicyLoaderIOSTest : public PlatformTest {
public:
void SetUp() override {
const std::string load_policy_key =
base::SysNSStringToUTF8(kPolicyLoaderIOSLoadPolicyKey);
const std::string test_schema = "{"
" \"type\": \"object\","
" \"properties\": {"
" \"" +
load_policy_key +
"\": { \"type\": \"boolean\" },"
" \"key1\": { \"type\": \"string\" },"
" \"key2\": { \"type\": \"string\" },"
" }"
"}";
std::string error;
Schema schema = Schema::Parse(test_schema, &error);
ASSERT_TRUE(schema.valid());
const PolicyNamespace ns(POLICY_DOMAIN_CHROME, "");
schema_registry_.RegisterComponent(ns, schema);
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner();
loader_ = std::make_unique<PolicyLoaderIOS>(&schema_registry_, task_runner);
}
protected:
// Verifies that |loader_| does not load any policies.
void VerifyNoPoliciesAreLoaded() {
PolicyBundle empty;
std::unique_ptr<PolicyBundle> bundle = loader_->Load();
ASSERT_TRUE(bundle);
EXPECT_TRUE(bundle->Equals(empty));
}
// The schema registry used while testing.
SchemaRegistry schema_registry_;
// The PolicyLoaderIOS under test.
std::unique_ptr<PolicyLoaderIOS> loader_;
};
// Verifies that policies are not loaded if kPolicyLoaderIOSLoadPolicyKey is not // Verifies that policies are not loaded if kPolicyLoaderIOSLoadPolicyKey is not
// present. // present.
TEST_F(PolicyLoaderIOSTest, NoPoliciesLoadedWithoutLoadPolicyKey) { TEST_F(PolicyLoaderIOSTest, NoPoliciesLoadedWithoutLoadPolicyKey) {
NSDictionary* policy = @{ NSDictionary* policy = @{
@"shared": @"wrong", @"key1" : @"value1",
@"key1": @"value1", @"key2" : @"value2",
}; };
[[NSUserDefaults standardUserDefaults] [[NSUserDefaults standardUserDefaults]
setObject:policy setObject:policy
...@@ -203,8 +259,8 @@ TEST_F(PolicyLoaderIOSTest, NoPoliciesLoadedWithoutLoadPolicyKey) { ...@@ -203,8 +259,8 @@ TEST_F(PolicyLoaderIOSTest, NoPoliciesLoadedWithoutLoadPolicyKey) {
TEST_F(PolicyLoaderIOSTest, NoPoliciesLoadedWhenEnableChromeKeyIsFalse) { TEST_F(PolicyLoaderIOSTest, NoPoliciesLoadedWhenEnableChromeKeyIsFalse) {
NSDictionary* policy = @{ NSDictionary* policy = @{
kPolicyLoaderIOSLoadPolicyKey : @NO, kPolicyLoaderIOSLoadPolicyKey : @NO,
@"shared" : @"wrong",
@"key1" : @"value1", @"key1" : @"value1",
@"key2" : @"value2",
}; };
[[NSUserDefaults standardUserDefaults] [[NSUserDefaults standardUserDefaults]
setObject:policy setObject:policy
......
...@@ -76,9 +76,10 @@ BrowserPolicyConnectorIOS::CreatePolicyProviders() { ...@@ -76,9 +76,10 @@ BrowserPolicyConnectorIOS::CreatePolicyProviders() {
std::unique_ptr<ConfigurationPolicyProvider> std::unique_ptr<ConfigurationPolicyProvider>
BrowserPolicyConnectorIOS::CreatePlatformProvider() { BrowserPolicyConnectorIOS::CreatePlatformProvider() {
std::unique_ptr<AsyncPolicyLoader> loader( auto loader = std::make_unique<PolicyLoaderIOS>(
new PolicyLoaderIOS(base::ThreadPool::CreateSequencedTaskRunner( GetSchemaRegistry(),
{base::MayBlock(), base::TaskPriority::BEST_EFFORT}))); base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT}));
return std::make_unique<AsyncPolicyProvider>(GetSchemaRegistry(), return std::make_unique<AsyncPolicyProvider>(GetSchemaRegistry(),
std::move(loader)); std::move(loader));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment