普通文本  |  377行  |  14.37 KB

// Copyright (c) 2011 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 "chrome/browser/policy/user_policy_cache.h"

#include <limits>
#include <string>

#include "base/file_util.h"
#include "base/memory/scoped_temp_dir.h"
#include "base/message_loop.h"
#include "base/values.h"
#include "chrome/browser/policy/configuration_policy_provider.h"
#include "chrome/browser/policy/proto/cloud_policy.pb.h"
#include "chrome/browser/policy/proto/device_management_backend.pb.h"
#include "chrome/browser/policy/proto/device_management_local.pb.h"
#include "chrome/browser/policy/proto/old_generic_format.pb.h"
#include "content/browser/browser_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace policy {

// Decodes a CloudPolicySettings object into two maps with mandatory and
// recommended settings, respectively. The implementation is generated code
// in policy/cloud_policy_generated.cc.
void DecodePolicy(const em::CloudPolicySettings& policy,
                  PolicyMap* mandatory, PolicyMap* recommended);

// The implementations of these methods are in cloud_policy_generated.cc.
Value* DecodeIntegerValue(google::protobuf::int64 value);
ListValue* DecodeStringList(const em::StringList& string_list);

class MockConfigurationPolicyProviderObserver
    : public ConfigurationPolicyProvider::Observer {
 public:
  MockConfigurationPolicyProviderObserver() {}
  virtual ~MockConfigurationPolicyProviderObserver() {}
  MOCK_METHOD0(OnUpdatePolicy, void());
  void OnProviderGoingAway() {}
};

// Tests the device management policy cache.
class UserPolicyCacheTest : public testing::Test {
 protected:
  UserPolicyCacheTest()
      : loop_(MessageLoop::TYPE_UI),
        ui_thread_(BrowserThread::UI, &loop_),
        file_thread_(BrowserThread::FILE, &loop_) {}

  void SetUp() {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
  }

  void TearDown() {
    loop_.RunAllPending();
  }

  // Creates a (signed) PolicyFetchResponse setting the given |homepage| and
  // featuring the given |timestamp| (as issued by the server).
  // Mildly hacky special feature: pass an empty string as |homepage| to get
  // a completely empty policy.
  em::PolicyFetchResponse* CreateHomepagePolicy(
      const std::string& homepage,
      const base::Time& timestamp,
      const em::PolicyOptions::PolicyMode policy_mode) {
    em::PolicyData signed_response;
    if (homepage != "") {
      em::CloudPolicySettings settings;
      em::HomepageLocationProto* homepagelocation_proto =
          settings.mutable_homepagelocation();
      homepagelocation_proto->set_homepagelocation(homepage);
      homepagelocation_proto->mutable_policy_options()->set_mode(policy_mode);
      EXPECT_TRUE(
          settings.SerializeToString(signed_response.mutable_policy_value()));
    }
    signed_response.set_timestamp(
        (timestamp - base::Time::UnixEpoch()).InMilliseconds());
    std::string serialized_signed_response;
    EXPECT_TRUE(signed_response.SerializeToString(&serialized_signed_response));

    em::PolicyFetchResponse* response = new em::PolicyFetchResponse;
    response->set_policy_data(serialized_signed_response);
    // TODO(jkummerow): Set proper new_public_key and signature (when
    // implementing support for signature verification).
    response->set_policy_data_signature("TODO");
    response->set_new_public_key("TODO");
    return response;
  }

  void WritePolicy(const em::PolicyFetchResponse& policy) {
    std::string data;
    em::CachedCloudPolicyResponse cached_policy;
    cached_policy.mutable_cloud_policy()->CopyFrom(policy);
    EXPECT_TRUE(cached_policy.SerializeToString(&data));
    int size = static_cast<int>(data.size());
    EXPECT_EQ(size, file_util::WriteFile(test_file(), data.c_str(), size));
  }

  // Takes ownership of |policy_response|.
  void SetPolicy(UserPolicyCache* cache,
                 em::PolicyFetchResponse* policy_response,
                 bool expect_changed_policy) {
    scoped_ptr<em::PolicyFetchResponse> policy(policy_response);
    ConfigurationPolicyObserverRegistrar registrar;
    registrar.Init(cache->GetManagedPolicyProvider(), &observer);
    if (expect_changed_policy)
      EXPECT_CALL(observer, OnUpdatePolicy()).Times(1);
    else
      EXPECT_CALL(observer, OnUpdatePolicy()).Times(0);
    cache->SetPolicy(*policy);
    testing::Mock::VerifyAndClearExpectations(&observer);
  }

  FilePath test_file() {
    return temp_dir_.path().AppendASCII("UserPolicyCacheTest");
  }

  const PolicyMap& mandatory_policy(const UserPolicyCache& cache) {
    return cache.mandatory_policy_;
  }

  const PolicyMap& recommended_policy(const UserPolicyCache& cache) {
    return cache.recommended_policy_;
  }

  MessageLoop loop_;
  MockConfigurationPolicyProviderObserver observer;

 private:
  ScopedTempDir temp_dir_;
  BrowserThread ui_thread_;
  BrowserThread file_thread_;
};

TEST_F(UserPolicyCacheTest, DecodePolicy) {
  em::CloudPolicySettings settings;
  settings.mutable_homepagelocation()->set_homepagelocation("chromium.org");
  settings.mutable_javascriptenabled()->set_javascriptenabled(true);
  settings.mutable_javascriptenabled()->mutable_policy_options()->set_mode(
      em::PolicyOptions::MANDATORY);
  settings.mutable_policyrefreshrate()->set_policyrefreshrate(5);
  settings.mutable_policyrefreshrate()->mutable_policy_options()->set_mode(
      em::PolicyOptions::RECOMMENDED);
  PolicyMap mandatory_policy;
  PolicyMap recommended_policy;
  DecodePolicy(settings, &mandatory_policy, &recommended_policy);
  PolicyMap mandatory;
  mandatory.Set(kPolicyHomepageLocation,
                Value::CreateStringValue("chromium.org"));
  mandatory.Set(kPolicyJavascriptEnabled, Value::CreateBooleanValue(true));
  PolicyMap recommended;
  recommended.Set(kPolicyPolicyRefreshRate, Value::CreateIntegerValue(5));
  EXPECT_TRUE(mandatory.Equals(mandatory_policy));
  EXPECT_TRUE(recommended.Equals(recommended_policy));
}

TEST_F(UserPolicyCacheTest, DecodeIntegerValue) {
  const int min = std::numeric_limits<int>::min();
  const int max = std::numeric_limits<int>::max();
  scoped_ptr<Value> value(
      DecodeIntegerValue(static_cast<google::protobuf::int64>(42)));
  ASSERT_TRUE(value.get());
  FundamentalValue expected_42(42);
  EXPECT_TRUE(value->Equals(&expected_42));
  value.reset(
      DecodeIntegerValue(static_cast<google::protobuf::int64>(min - 1LL)));
  EXPECT_EQ(NULL, value.get());
  value.reset(DecodeIntegerValue(static_cast<google::protobuf::int64>(min)));
  ASSERT_TRUE(value.get());
  FundamentalValue expected_min(min);
  EXPECT_TRUE(value->Equals(&expected_min));
  value.reset(
      DecodeIntegerValue(static_cast<google::protobuf::int64>(max + 1LL)));
  EXPECT_EQ(NULL, value.get());
  value.reset(DecodeIntegerValue(static_cast<google::protobuf::int64>(max)));
  ASSERT_TRUE(value.get());
  FundamentalValue expected_max(max);
  EXPECT_TRUE(value->Equals(&expected_max));
}

TEST_F(UserPolicyCacheTest, DecodeStringList) {
  em::StringList string_list;
  string_list.add_entries("ponies");
  string_list.add_entries("more ponies");
  scoped_ptr<ListValue> decoded(DecodeStringList(string_list));
  ListValue expected;
  expected.Append(Value::CreateStringValue("ponies"));
  expected.Append(Value::CreateStringValue("more ponies"));
  EXPECT_TRUE(decoded->Equals(&expected));
}

TEST_F(UserPolicyCacheTest, Empty) {
  UserPolicyCache cache(test_file());
  PolicyMap empty;
  EXPECT_TRUE(empty.Equals(mandatory_policy(cache)));
  EXPECT_TRUE(empty.Equals(recommended_policy(cache)));
  EXPECT_EQ(base::Time(), cache.last_policy_refresh_time());
}

TEST_F(UserPolicyCacheTest, LoadNoFile) {
  UserPolicyCache cache(test_file());
  cache.Load();
  PolicyMap empty;
  EXPECT_TRUE(empty.Equals(mandatory_policy(cache)));
  EXPECT_EQ(base::Time(), cache.last_policy_refresh_time());
}

TEST_F(UserPolicyCacheTest, RejectFuture) {
  scoped_ptr<em::PolicyFetchResponse> policy_response(
      CreateHomepagePolicy("", base::Time::NowFromSystemTime() +
                               base::TimeDelta::FromMinutes(5),
                           em::PolicyOptions::MANDATORY));
  WritePolicy(*policy_response);
  UserPolicyCache cache(test_file());
  cache.Load();
  PolicyMap empty;
  EXPECT_TRUE(empty.Equals(mandatory_policy(cache)));
  EXPECT_EQ(base::Time(), cache.last_policy_refresh_time());
}

TEST_F(UserPolicyCacheTest, LoadWithFile) {
  scoped_ptr<em::PolicyFetchResponse> policy_response(
      CreateHomepagePolicy("", base::Time::NowFromSystemTime(),
                           em::PolicyOptions::MANDATORY));
  WritePolicy(*policy_response);
  UserPolicyCache cache(test_file());
  cache.Load();
  PolicyMap empty;
  EXPECT_TRUE(empty.Equals(mandatory_policy(cache)));
  EXPECT_NE(base::Time(), cache.last_policy_refresh_time());
  EXPECT_GE(base::Time::Now(), cache.last_policy_refresh_time());
}

TEST_F(UserPolicyCacheTest, LoadWithData) {
  scoped_ptr<em::PolicyFetchResponse> policy(
      CreateHomepagePolicy("http://www.example.com",
                           base::Time::NowFromSystemTime(),
                           em::PolicyOptions::MANDATORY));
  WritePolicy(*policy);
  UserPolicyCache cache(test_file());
  cache.Load();
  PolicyMap expected;
  expected.Set(kPolicyHomepageLocation,
               Value::CreateStringValue("http://www.example.com"));
  EXPECT_TRUE(expected.Equals(mandatory_policy(cache)));
}

TEST_F(UserPolicyCacheTest, SetPolicy) {
  UserPolicyCache cache(test_file());
  em::PolicyFetchResponse* policy =
      CreateHomepagePolicy("http://www.example.com",
                           base::Time::NowFromSystemTime(),
                           em::PolicyOptions::MANDATORY);
  SetPolicy(&cache, policy, true);
  em::PolicyFetchResponse* policy2 =
      CreateHomepagePolicy("http://www.example.com",
                           base::Time::NowFromSystemTime(),
                           em::PolicyOptions::MANDATORY);
  SetPolicy(&cache, policy2, false);
  PolicyMap expected;
  expected.Set(kPolicyHomepageLocation,
               Value::CreateStringValue("http://www.example.com"));
  PolicyMap empty;
  EXPECT_TRUE(expected.Equals(mandatory_policy(cache)));
  EXPECT_TRUE(empty.Equals(recommended_policy(cache)));
  policy = CreateHomepagePolicy("http://www.example.com",
                                base::Time::NowFromSystemTime(),
                                em::PolicyOptions::RECOMMENDED);
  SetPolicy(&cache, policy, true);
  EXPECT_TRUE(expected.Equals(recommended_policy(cache)));
  EXPECT_TRUE(empty.Equals(mandatory_policy(cache)));
}

TEST_F(UserPolicyCacheTest, ResetPolicy) {
  UserPolicyCache cache(test_file());

  em::PolicyFetchResponse* policy =
      CreateHomepagePolicy("http://www.example.com",
                           base::Time::NowFromSystemTime(),
                           em::PolicyOptions::MANDATORY);
  SetPolicy(&cache, policy, true);
  PolicyMap expected;
  expected.Set(kPolicyHomepageLocation,
               Value::CreateStringValue("http://www.example.com"));
  EXPECT_TRUE(expected.Equals(mandatory_policy(cache)));

  em::PolicyFetchResponse* empty_policy =
      CreateHomepagePolicy("", base::Time::NowFromSystemTime(),
                           em::PolicyOptions::MANDATORY);
  SetPolicy(&cache, empty_policy, true);
  PolicyMap empty;
  EXPECT_TRUE(empty.Equals(mandatory_policy(cache)));
}

TEST_F(UserPolicyCacheTest, PersistPolicy) {
  {
    UserPolicyCache cache(test_file());
    scoped_ptr<em::PolicyFetchResponse> policy(
        CreateHomepagePolicy("http://www.example.com",
                             base::Time::NowFromSystemTime(),
                             em::PolicyOptions::MANDATORY));
    cache.SetPolicy(*policy);
  }

  loop_.RunAllPending();

  EXPECT_TRUE(file_util::PathExists(test_file()));
  UserPolicyCache cache(test_file());
  cache.Load();
  PolicyMap expected;
  expected.Set(kPolicyHomepageLocation,
               Value::CreateStringValue("http://www.example.com"));
  EXPECT_TRUE(expected.Equals(mandatory_policy(cache)));
}

TEST_F(UserPolicyCacheTest, FreshPolicyOverride) {
  scoped_ptr<em::PolicyFetchResponse> policy(
      CreateHomepagePolicy("http://www.example.com",
                           base::Time::NowFromSystemTime(),
                           em::PolicyOptions::MANDATORY));
  WritePolicy(*policy);

  UserPolicyCache cache(test_file());
  em::PolicyFetchResponse* updated_policy =
      CreateHomepagePolicy("http://www.chromium.org",
                           base::Time::NowFromSystemTime(),
                           em::PolicyOptions::MANDATORY);
  SetPolicy(&cache, updated_policy, true);

  cache.Load();
  PolicyMap expected;
  expected.Set(kPolicyHomepageLocation,
               Value::CreateStringValue("http://www.chromium.org"));
  EXPECT_TRUE(expected.Equals(mandatory_policy(cache)));
}

// Test case for the temporary support for GenericNamedValues in the
// CloudPolicySettings protobuf. Can be removed when this support is no longer
// required.
TEST_F(UserPolicyCacheTest, OldStylePolicy) {
  UserPolicyCache cache(test_file());
  em::PolicyFetchResponse* policy = new em::PolicyFetchResponse();
  em::PolicyData signed_response;
  em::LegacyChromeSettingsProto settings;
  em::GenericNamedValue* named_value = settings.add_named_value();
  named_value->set_name("HomepageLocation");
  em::GenericValue* value_container = named_value->mutable_value();
  value_container->set_value_type(em::GenericValue::VALUE_TYPE_STRING);
  value_container->set_string_value("http://www.example.com");
  EXPECT_TRUE(
      settings.SerializeToString(signed_response.mutable_policy_value()));
  base::TimeDelta timestamp =
      base::Time::NowFromSystemTime() - base::Time::UnixEpoch();
  signed_response.set_timestamp(timestamp.InMilliseconds());
  EXPECT_TRUE(
      signed_response.SerializeToString(policy->mutable_policy_data()));

  SetPolicy(&cache, policy, true);
  PolicyMap expected;
  expected.Set(kPolicyHomepageLocation,
               Value::CreateStringValue("http://www.example.com"));
  PolicyMap empty;
  EXPECT_TRUE(expected.Equals(mandatory_policy(cache)));
  EXPECT_TRUE(empty.Equals(recommended_policy(cache)));
  // If new-style policy comes in, it should override old-style policy.
  policy = CreateHomepagePolicy("http://www.example.com",
                                base::Time::NowFromSystemTime(),
                                em::PolicyOptions::RECOMMENDED);
  SetPolicy(&cache, policy, true);
  EXPECT_TRUE(expected.Equals(recommended_policy(cache)));
  EXPECT_TRUE(empty.Equals(mandatory_policy(cache)));
}

}  // namespace policy