1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <UIKit/UIKit.h>
6
7#include "base/basictypes.h"
8#include "base/callback.h"
9#include "base/files/file_path.h"
10#include "base/ios/ios_util.h"
11#include "base/strings/sys_string_conversions.h"
12#include "base/test/test_simple_task_runner.h"
13#include "base/values.h"
14#include "components/policy/core/common/async_policy_provider.h"
15#include "components/policy/core/common/configuration_policy_provider_test.h"
16#include "components/policy/core/common/policy_bundle.h"
17#include "components/policy/core/common/policy_loader_ios.h"
18#include "components/policy/core/common/policy_map.h"
19#include "components/policy/core/common/policy_test_utils.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22namespace policy {
23
24namespace {
25
26// Key in the NSUserDefaults that contains the managed app configuration.
27NSString* const kConfigurationKey = @"com.apple.configuration.managed";
28
29class TestHarness : public PolicyProviderTestHarness {
30 public:
31  // If |use_encoded_key| is true then AddPolicies() serializes and encodes
32  // the policies, and publishes them under the EncodedChromePolicy key.
33  explicit TestHarness(bool use_encoded_key);
34  virtual ~TestHarness();
35
36  virtual void SetUp() OVERRIDE;
37
38  virtual ConfigurationPolicyProvider* CreateProvider(
39      SchemaRegistry* registry,
40      scoped_refptr<base::SequencedTaskRunner> task_runner) OVERRIDE;
41
42  virtual void InstallEmptyPolicy() OVERRIDE;
43  virtual void InstallStringPolicy(const std::string& policy_name,
44                                   const std::string& policy_value) OVERRIDE;
45  virtual void InstallIntegerPolicy(const std::string& policy_name,
46                                    int policy_value) OVERRIDE;
47  virtual void InstallBooleanPolicy(const std::string& policy_name,
48                                    bool policy_value) OVERRIDE;
49  virtual void InstallStringListPolicy(
50      const std::string& policy_name,
51      const base::ListValue* policy_value) OVERRIDE;
52  virtual void InstallDictionaryPolicy(
53      const std::string& policy_name,
54      const base::DictionaryValue* policy_value) OVERRIDE;
55
56  static PolicyProviderTestHarness* Create();
57  static PolicyProviderTestHarness* CreateWithEncodedKey();
58
59 private:
60  // Merges the policies in |policy| into the current policy dictionary
61  // in NSUserDefaults, after making sure that the policy dictionary
62  // exists.
63  void AddPolicies(NSDictionary* policy);
64  void AddChromePolicy(NSDictionary* policy);
65  void AddEncodedChromePolicy(NSDictionary* policy);
66
67  bool use_encoded_key_;
68
69  DISALLOW_COPY_AND_ASSIGN(TestHarness);
70};
71
72TestHarness::TestHarness(bool use_encoded_key)
73    : PolicyProviderTestHarness(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE),
74      use_encoded_key_(use_encoded_key) {}
75
76TestHarness::~TestHarness() {
77  // Cleanup any policies left from the test.
78  [[NSUserDefaults standardUserDefaults] removeObjectForKey:kConfigurationKey];
79}
80
81void TestHarness::SetUp() {
82  // Make sure there is no pre-existing policy present.
83  [[NSUserDefaults standardUserDefaults] removeObjectForKey:kConfigurationKey];
84}
85
86ConfigurationPolicyProvider* TestHarness::CreateProvider(
87    SchemaRegistry* registry,
88    scoped_refptr<base::SequencedTaskRunner> task_runner) {
89  scoped_ptr<AsyncPolicyLoader> loader(new PolicyLoaderIOS(task_runner));
90  return new AsyncPolicyProvider(registry, loader.Pass());
91}
92
93void TestHarness::InstallEmptyPolicy() {
94  AddPolicies(@{});
95}
96
97void TestHarness::InstallStringPolicy(const std::string& policy_name,
98                                      const std::string& policy_value) {
99  NSString* key = base::SysUTF8ToNSString(policy_name);
100  NSString* value = base::SysUTF8ToNSString(policy_value);
101  AddPolicies(@{
102      key: value
103  });
104}
105
106void TestHarness::InstallIntegerPolicy(const std::string& policy_name,
107                                       int policy_value) {
108  NSString* key = base::SysUTF8ToNSString(policy_name);
109  AddPolicies(@{
110      key: [NSNumber numberWithInt:policy_value]
111  });
112}
113
114void TestHarness::InstallBooleanPolicy(const std::string& policy_name,
115                                       bool policy_value) {
116  NSString* key = base::SysUTF8ToNSString(policy_name);
117  AddPolicies(@{
118      key: [NSNumber numberWithBool:policy_value]
119  });
120}
121
122void TestHarness::InstallStringListPolicy(const std::string& policy_name,
123                                          const base::ListValue* policy_value) {
124  NSString* key = base::SysUTF8ToNSString(policy_name);
125  base::ScopedCFTypeRef<CFPropertyListRef> value(ValueToProperty(policy_value));
126  AddPolicies(@{
127      key: static_cast<NSArray*>(value.get())
128  });
129}
130
131void TestHarness::InstallDictionaryPolicy(
132    const std::string& policy_name,
133    const base::DictionaryValue* policy_value) {
134  NSString* key = base::SysUTF8ToNSString(policy_name);
135  base::ScopedCFTypeRef<CFPropertyListRef> value(ValueToProperty(policy_value));
136  AddPolicies(@{
137      key: static_cast<NSDictionary*>(value.get())
138  });
139}
140
141// static
142PolicyProviderTestHarness* TestHarness::Create() {
143  return new TestHarness(false);
144}
145
146// static
147PolicyProviderTestHarness* TestHarness::CreateWithEncodedKey() {
148  if (base::ios::IsRunningOnIOS7OrLater())
149    return new TestHarness(true);
150  // Earlier versions of iOS don't have the APIs to support this test.
151  // Unfortunately it's not possible to conditionally run this harness using
152  // gtest, so we just fallback to running the non-encoded version.
153  NSLog(@"Skipping test");
154  return new TestHarness(false);
155}
156
157void TestHarness::AddPolicies(NSDictionary* policy) {
158  if (use_encoded_key_)
159    AddEncodedChromePolicy(policy);
160  else
161    AddChromePolicy(policy);
162}
163
164void TestHarness::AddChromePolicy(NSDictionary* policy) {
165  NSString* key = @"ChromePolicy";
166  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
167  base::scoped_nsobject<NSMutableDictionary> chromePolicy(
168      [[NSMutableDictionary alloc] init]);
169
170  NSDictionary* previous = [defaults dictionaryForKey:key];
171  if (previous)
172    [chromePolicy addEntriesFromDictionary:previous];
173
174  [chromePolicy addEntriesFromDictionary:policy];
175
176  NSDictionary* wrapper = @{
177      key: chromePolicy
178  };
179  [[NSUserDefaults standardUserDefaults] setObject:wrapper
180                                            forKey:kConfigurationKey];
181}
182
183void TestHarness::AddEncodedChromePolicy(NSDictionary* policy) {
184  NSString* key = @"EncodedChromePolicy";
185  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
186
187  base::scoped_nsobject<NSMutableDictionary> chromePolicy(
188      [[NSMutableDictionary alloc] init]);
189
190  NSString* previous = [defaults stringForKey:key];
191  if (previous) {
192    base::scoped_nsobject<NSData> data(
193        [[NSData alloc] initWithBase64EncodedString:previous options:0]);
194    NSDictionary* properties = [NSPropertyListSerialization
195        propertyListWithData:data.get()
196                     options:NSPropertyListImmutable
197                      format:NULL
198                       error:NULL];
199    [chromePolicy addEntriesFromDictionary:properties];
200  }
201
202  [chromePolicy addEntriesFromDictionary:policy];
203
204  NSData* data = [NSPropertyListSerialization
205      dataWithPropertyList:chromePolicy
206                    format:NSPropertyListXMLFormat_v1_0
207                   options:0
208                     error:NULL];
209  NSString* encoded = [data base64EncodedStringWithOptions:0];
210
211  NSDictionary* wrapper = @{
212      key: encoded
213  };
214  [[NSUserDefaults standardUserDefaults] setObject:wrapper
215                                            forKey:kConfigurationKey];
216}
217
218}  // namespace
219
220INSTANTIATE_TEST_CASE_P(
221    PolicyProviderIOSChromePolicyTest,
222    ConfigurationPolicyProviderTest,
223    testing::Values(TestHarness::Create));
224
225INSTANTIATE_TEST_CASE_P(
226    PolicyProviderIOSEncodedChromePolicyTest,
227    ConfigurationPolicyProviderTest,
228    testing::Values(TestHarness::CreateWithEncodedKey));
229
230TEST(PolicyProviderIOSTest, ChromePolicyOverEncodedChromePolicy) {
231  // This test verifies that if the "ChromePolicy" key is present then the
232  // "EncodedChromePolicy" key is ignored.
233
234  if (!base::ios::IsRunningOnIOS7OrLater()) {
235    // Skip this test if running on a version earlier than iOS 7.
236    NSLog(@"Skipping test");
237    return;
238  }
239
240  NSDictionary* policy = @{
241    @"shared": @"wrong",
242    @"key1": @"value1",
243  };
244  NSData* data = [NSPropertyListSerialization
245      dataWithPropertyList:policy
246                    format:NSPropertyListXMLFormat_v1_0
247                   options:0
248                     error:NULL];
249  NSString* encodedChromePolicy = [data base64EncodedStringWithOptions:0];
250
251  NSDictionary* chromePolicy = @{
252    @"shared": @"right",
253    @"key2": @"value2",
254  };
255
256  NSDictionary* wrapper = @{
257    @"ChromePolicy": chromePolicy,
258    @"EncodedChromePolicy": encodedChromePolicy,
259  };
260
261  [[NSUserDefaults standardUserDefaults] setObject:wrapper
262                                            forKey:kConfigurationKey];
263
264  PolicyBundle expected;
265  PolicyMap& expectedMap =
266      expected.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
267  expectedMap.Set("shared", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
268                  new base::StringValue("right"), NULL);
269  expectedMap.Set("key2", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
270                  new base::StringValue("value2"), NULL);
271
272  scoped_refptr<base::TestSimpleTaskRunner> taskRunner =
273      new base::TestSimpleTaskRunner();
274  PolicyLoaderIOS loader(taskRunner);
275  scoped_ptr<PolicyBundle> bundle = loader.Load();
276  ASSERT_TRUE(bundle);
277  EXPECT_TRUE(bundle->Equals(expected));
278}
279
280}  // namespace policy
281