Home | History | Annotate | Download | only in cloud
      1 // Copyright (c) 2013 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 "chrome/browser/policy/cloud/component_cloud_policy_store.h"
      6 
      7 #include <map>
      8 #include <set>
      9 #include <string>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/callback.h"
     13 #include "base/files/scoped_temp_dir.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/sha1.h"
     16 #include "chrome/browser/policy/cloud/cloud_policy_constants.h"
     17 #include "chrome/browser/policy/cloud/policy_builder.h"
     18 #include "chrome/browser/policy/cloud/resource_cache.h"
     19 #include "chrome/browser/policy/external_data_fetcher.h"
     20 #include "chrome/browser/policy/proto/cloud/chrome_extension_policy.pb.h"
     21 #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h"
     22 #include "testing/gmock/include/gmock/gmock.h"
     23 #include "testing/gtest/include/gtest/gtest.h"
     24 
     25 namespace em = enterprise_management;
     26 
     27 using testing::Mock;
     28 
     29 namespace policy {
     30 
     31 namespace {
     32 
     33 const char kTestExtension[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
     34 const char kTestDownload[] = "http://example.com/getpolicy?id=123";
     35 const char kTestPolicy[] =
     36     "{"
     37     "  \"Name\": {"
     38     "    \"Value\": \"disabled\""
     39     "  },"
     40     "  \"Second\": {"
     41     "    \"Value\": \"maybe\","
     42     "    \"Level\": \"Recommended\""
     43     "  }"
     44     "}";
     45 
     46 std::string TestPolicyHash() {
     47   return base::SHA1HashString(kTestPolicy);
     48 }
     49 
     50 class MockComponentCloudPolicyStoreDelegate
     51     : public ComponentCloudPolicyStore::Delegate {
     52  public:
     53   virtual ~MockComponentCloudPolicyStoreDelegate() {}
     54 
     55   MOCK_METHOD0(OnComponentCloudPolicyStoreUpdated, void());
     56 };
     57 
     58 }  // namespace
     59 
     60 class ComponentCloudPolicyStoreTest : public testing::Test {
     61  protected:
     62   virtual void SetUp() OVERRIDE {
     63     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     64     cache_.reset(new ResourceCache(temp_dir_.path()));
     65     store_.reset(new ComponentCloudPolicyStore(&store_delegate_, cache_.get()));
     66     store_->SetCredentials(ComponentPolicyBuilder::kFakeUsername,
     67                            ComponentPolicyBuilder::kFakeToken);
     68 
     69     builder_.policy_data().set_policy_type(
     70         dm_protocol::kChromeExtensionPolicyType);
     71     builder_.policy_data().set_settings_entity_id(kTestExtension);
     72     builder_.payload().set_download_url(kTestDownload);
     73     builder_.payload().set_secure_hash(TestPolicyHash());
     74 
     75     PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, kTestExtension);
     76     PolicyMap& policy = expected_bundle_.Get(ns);
     77     policy.Set("Name", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
     78                base::Value::CreateStringValue("disabled"), NULL);
     79     policy.Set("Second", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
     80                base::Value::CreateStringValue("maybe"), NULL);
     81   }
     82 
     83   // Returns true if the policy exposed by the |store_| is empty.
     84   bool IsEmpty() {
     85     return store_->policy().begin() == store_->policy().end();
     86   }
     87 
     88   scoped_ptr<em::PolicyFetchResponse> CreateResponse() {
     89     builder_.Build();
     90     return make_scoped_ptr(new em::PolicyFetchResponse(builder_.policy()));
     91   }
     92 
     93   std::string CreateSerializedResponse() {
     94     builder_.Build();
     95     return builder_.GetBlob();
     96   }
     97 
     98   base::ScopedTempDir temp_dir_;
     99   scoped_ptr<ResourceCache> cache_;
    100   scoped_ptr<ComponentCloudPolicyStore> store_;
    101   MockComponentCloudPolicyStoreDelegate store_delegate_;
    102   ComponentPolicyBuilder builder_;
    103   PolicyBundle expected_bundle_;
    104 };
    105 
    106 TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicy) {
    107   em::ExternalPolicyData payload;
    108   PolicyNamespace ns;
    109   EXPECT_TRUE(store_->ValidatePolicy(CreateResponse(), &ns, &payload));
    110   EXPECT_EQ(POLICY_DOMAIN_EXTENSIONS, ns.domain);
    111   EXPECT_EQ(kTestExtension, ns.component_id);
    112   EXPECT_EQ(kTestDownload, payload.download_url());
    113   EXPECT_EQ(TestPolicyHash(), payload.secure_hash());
    114 }
    115 
    116 TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyWrongUsername) {
    117   builder_.policy_data().set_username("anotheruser (at) example.com");
    118   em::ExternalPolicyData payload;
    119   PolicyNamespace ns;
    120   EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload));
    121 }
    122 
    123 TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyWrongDMToken) {
    124   builder_.policy_data().set_request_token("notmytoken");
    125   em::ExternalPolicyData payload;
    126   PolicyNamespace ns;
    127   EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload));
    128 }
    129 
    130 TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyBadType) {
    131   builder_.policy_data().set_policy_type(dm_protocol::kChromeUserPolicyType);
    132   em::ExternalPolicyData payload;
    133   PolicyNamespace ns;
    134   EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload));
    135 }
    136 
    137 TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyBadDownloadUrl) {
    138   builder_.payload().set_download_url("invalidurl");
    139   em::ExternalPolicyData payload;
    140   PolicyNamespace ns;
    141   EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload));
    142 }
    143 
    144 TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyEmptyDownloadUrl) {
    145   builder_.payload().clear_download_url();
    146   builder_.payload().clear_secure_hash();
    147   em::ExternalPolicyData payload;
    148   PolicyNamespace ns;
    149   // This is valid; it's how "no policy" is signalled to the client.
    150   EXPECT_TRUE(store_->ValidatePolicy(CreateResponse(), &ns, &payload));
    151 }
    152 
    153 TEST_F(ComponentCloudPolicyStoreTest, ValidatePolicyBadPayload) {
    154   builder_.clear_payload();
    155   builder_.policy_data().set_policy_value("broken");
    156   em::ExternalPolicyData payload;
    157   PolicyNamespace ns;
    158   EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload));
    159 }
    160 
    161 TEST_F(ComponentCloudPolicyStoreTest, ValidateNoCredentials) {
    162   store_.reset(new ComponentCloudPolicyStore(&store_delegate_, cache_.get()));
    163   em::ExternalPolicyData payload;
    164   PolicyNamespace ns;
    165   EXPECT_FALSE(store_->ValidatePolicy(CreateResponse(), &ns, &payload));
    166 }
    167 
    168 TEST_F(ComponentCloudPolicyStoreTest, ValidateWrongCredentials) {
    169   em::ExternalPolicyData payload;
    170   PolicyNamespace ns;
    171   // Verify that the default response validates with the right credentials.
    172   EXPECT_TRUE(store_->ValidatePolicy(CreateResponse(), &ns, &payload));
    173   // Now store that response.
    174   EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated());
    175   EXPECT_TRUE(store_->Store(
    176       ns, CreateSerializedResponse(), TestPolicyHash(), kTestPolicy));
    177   Mock::VerifyAndClearExpectations(&store_delegate_);
    178   EXPECT_TRUE(store_->policy().Equals(expected_bundle_));
    179   // And verify that the response data in the cache.
    180   std::map<std::string, std::string> contents;
    181   cache_->LoadAllSubkeys("extension-policy", &contents);
    182   EXPECT_FALSE(contents.empty());
    183 
    184   // Try loading the cached response data with wrong credentials.
    185   ComponentCloudPolicyStore another_store(&store_delegate_, cache_.get());
    186   another_store.SetCredentials("wrongdude (at) example.com", "wrongtoken");
    187   another_store.Load();
    188   const PolicyBundle empty_bundle;
    189   EXPECT_TRUE(another_store.policy().Equals(empty_bundle));
    190 
    191   // The failure to read wiped the cache.
    192   cache_->LoadAllSubkeys("extension-policy", &contents);
    193   EXPECT_TRUE(contents.empty());
    194 }
    195 
    196 TEST_F(ComponentCloudPolicyStoreTest, StoreAndLoad) {
    197   // Initially empty.
    198   EXPECT_TRUE(IsEmpty());
    199   store_->Load();
    200   EXPECT_TRUE(IsEmpty());
    201 
    202   // Store policy for an unsupported domain.
    203   PolicyNamespace ns(POLICY_DOMAIN_CHROME, kTestExtension);
    204   builder_.policy_data().set_policy_type(dm_protocol::kChromeUserPolicyType);
    205   EXPECT_FALSE(store_->Store(
    206       ns, CreateSerializedResponse(), TestPolicyHash(), kTestPolicy));
    207 
    208   // Store policy with the wrong hash.
    209   builder_.policy_data().set_policy_type(
    210       dm_protocol::kChromeExtensionPolicyType);
    211   ns.domain = POLICY_DOMAIN_EXTENSIONS;
    212   builder_.payload().set_secure_hash("badash");
    213   EXPECT_FALSE(store_->Store(
    214       ns, CreateSerializedResponse(), "badash", kTestPolicy));
    215 
    216   // Store policy without a hash.
    217   builder_.payload().clear_secure_hash();
    218   EXPECT_FALSE(store_->Store(
    219       ns, CreateSerializedResponse(), std::string(), kTestPolicy));
    220 
    221   // Store policy with invalid JSON data.
    222   static const char kInvalidData[] = "{ not json }";
    223   const std::string invalid_data_hash = base::SHA1HashString(kInvalidData);
    224   builder_.payload().set_secure_hash(invalid_data_hash);
    225   EXPECT_FALSE(store_->Store(
    226       ns, CreateSerializedResponse(), invalid_data_hash, kInvalidData));
    227 
    228   // All of those failed.
    229   EXPECT_TRUE(IsEmpty());
    230   EXPECT_EQ(std::string(), store_->GetCachedHash(ns));
    231 
    232   // Now store a valid policy.
    233   builder_.payload().set_secure_hash(TestPolicyHash());
    234   EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated());
    235   EXPECT_TRUE(store_->Store(
    236       ns, CreateSerializedResponse(), TestPolicyHash(), kTestPolicy));
    237   Mock::VerifyAndClearExpectations(&store_delegate_);
    238   EXPECT_FALSE(IsEmpty());
    239   EXPECT_TRUE(store_->policy().Equals(expected_bundle_));
    240   EXPECT_EQ(TestPolicyHash(), store_->GetCachedHash(ns));
    241 
    242   // Loading from the cache validates the policy data again.
    243   ComponentCloudPolicyStore another_store(&store_delegate_, cache_.get());
    244   another_store.SetCredentials(ComponentPolicyBuilder::kFakeUsername,
    245                                ComponentPolicyBuilder::kFakeToken);
    246   another_store.Load();
    247   EXPECT_TRUE(another_store.policy().Equals(expected_bundle_));
    248   EXPECT_EQ(TestPolicyHash(), another_store.GetCachedHash(ns));
    249 }
    250 
    251 TEST_F(ComponentCloudPolicyStoreTest, Updates) {
    252   // Store some policies.
    253   PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, kTestExtension);
    254   EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated());
    255   EXPECT_TRUE(store_->Store(
    256       ns, CreateSerializedResponse(), TestPolicyHash(), kTestPolicy));
    257   Mock::VerifyAndClearExpectations(&store_delegate_);
    258   EXPECT_FALSE(IsEmpty());
    259   EXPECT_TRUE(store_->policy().Equals(expected_bundle_));
    260 
    261   // Deleting a non-existant namespace doesn't trigger updates.
    262   PolicyNamespace ns_fake(POLICY_DOMAIN_EXTENSIONS, "nosuchid");
    263   store_->Delete(ns_fake);
    264   Mock::VerifyAndClearExpectations(&store_delegate_);
    265 
    266   // Deleting a namespace that has policies triggers an update.
    267   EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated());
    268   store_->Delete(ns);
    269   Mock::VerifyAndClearExpectations(&store_delegate_);
    270 }
    271 
    272 TEST_F(ComponentCloudPolicyStoreTest, Purge) {
    273   // Store a valid policy.
    274   EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated());
    275   PolicyNamespace ns(POLICY_DOMAIN_EXTENSIONS, kTestExtension);
    276   EXPECT_TRUE(store_->Store(
    277       ns, CreateSerializedResponse(), TestPolicyHash(), kTestPolicy));
    278   Mock::VerifyAndClearExpectations(&store_delegate_);
    279   EXPECT_FALSE(IsEmpty());
    280   EXPECT_TRUE(store_->policy().Equals(expected_bundle_));
    281 
    282   // Purge other namespaces.
    283   std::set<std::string> keep;
    284   keep.insert(kTestExtension);
    285   store_->Purge(POLICY_DOMAIN_EXTENSIONS, keep);
    286 
    287   // The policy for |ns| is still served.
    288   EXPECT_TRUE(store_->policy().Equals(expected_bundle_));
    289 
    290   // Loading the store again will still see |ns|.
    291   ComponentCloudPolicyStore another_store(&store_delegate_, cache_.get());
    292   const PolicyBundle empty_bundle;
    293   EXPECT_TRUE(another_store.policy().Equals(empty_bundle));
    294   another_store.SetCredentials(ComponentPolicyBuilder::kFakeUsername,
    295                                ComponentPolicyBuilder::kFakeToken);
    296   another_store.Load();
    297   EXPECT_TRUE(another_store.policy().Equals(expected_bundle_));
    298 
    299   // Now purge everything.
    300   EXPECT_CALL(store_delegate_, OnComponentCloudPolicyStoreUpdated());
    301   keep.clear();
    302   store_->Purge(POLICY_DOMAIN_EXTENSIONS, keep);
    303   Mock::VerifyAndClearExpectations(&store_delegate_);
    304 
    305   // No policies are served anymore.
    306   EXPECT_TRUE(store_->policy().Equals(empty_bundle));
    307 
    308   // And they aren't loaded anymore either.
    309   ComponentCloudPolicyStore yet_another_store(&store_delegate_, cache_.get());
    310   yet_another_store.SetCredentials(ComponentPolicyBuilder::kFakeUsername,
    311                                    ComponentPolicyBuilder::kFakeToken);
    312   yet_another_store.Load();
    313   EXPECT_TRUE(yet_another_store.policy().Equals(empty_bundle));
    314 }
    315 
    316 }  // namespace policy
    317