blob: 1af56c8c3c7050739e0fa7ccbf91fc76ddd1bdd7 [file] [log] [blame]
// Copyright (c) 2012 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/policy_service_impl.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/values.h"
#include "chrome/browser/policy/mock_configuration_policy_provider.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::AnyNumber;
using ::testing::Mock;
using ::testing::Return;
using ::testing::_;
namespace policy {
namespace {
const char kExtension[] = "extension-id";
const char kSameLevelPolicy[] = "policy-same-level-and-scope";
const char kDiffLevelPolicy[] = "chrome-diff-level-and-scope";
class MockPolicyServiceObserver : public PolicyService::Observer {
public:
virtual ~MockPolicyServiceObserver() {}
MOCK_METHOD4(OnPolicyUpdated, void(PolicyDomain,
const std::string&,
const PolicyMap& previous,
const PolicyMap& current));
};
// Helper to compare the arguments to an EXPECT_CALL of OnPolicyUpdated() with
// their expected values.
MATCHER_P(PolicyEquals, expected, "") {
return arg.Equals(*expected);
}
// Helper to compare the arguments to an EXPECT_CALL of OnPolicyValueUpdated()
// with their expected values.
MATCHER_P(ValueEquals, expected, "") {
return base::Value::Equals(arg, expected);
}
// Helper that fills |bundle| with test policies.
void AddTestPolicies(PolicyBundle* bundle,
const char* value,
PolicyLevel level,
PolicyScope scope) {
PolicyMap* policy_map = &bundle->Get(POLICY_DOMAIN_CHROME, "");
policy_map->Set(kSameLevelPolicy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateStringValue(value));
policy_map->Set(kDiffLevelPolicy, level, scope,
base::Value::CreateStringValue(value));
policy_map = &bundle->Get(POLICY_DOMAIN_EXTENSIONS, kExtension);
policy_map->Set(kSameLevelPolicy, POLICY_LEVEL_MANDATORY,
POLICY_SCOPE_USER, base::Value::CreateStringValue(value));
policy_map->Set(kDiffLevelPolicy, level, scope,
base::Value::CreateStringValue(value));
}
} // namespace
class PolicyServiceTest : public testing::Test {
public:
PolicyServiceTest() {}
void SetUp() OVERRIDE {
EXPECT_CALL(provider0_, IsInitializationComplete())
.WillRepeatedly(Return(true));
EXPECT_CALL(provider1_, IsInitializationComplete())
.WillRepeatedly(Return(true));
EXPECT_CALL(provider2_, IsInitializationComplete())
.WillRepeatedly(Return(true));
policy0_.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(13));
provider0_.UpdateChromePolicy(policy0_);
PolicyServiceImpl::Providers providers;
providers.push_back(&provider0_);
providers.push_back(&provider1_);
providers.push_back(&provider2_);
policy_service_.reset(new PolicyServiceImpl(providers));
}
MOCK_METHOD2(OnPolicyValueUpdated, void(const base::Value*,
const base::Value*));
MOCK_METHOD0(OnPolicyRefresh, void());
// Returns true if the policies for |domain|, |component_id| match |expected|.
bool VerifyPolicies(PolicyDomain domain,
const std::string& component_id,
const PolicyMap& expected) {
return policy_service_->GetPolicies(domain, component_id).Equals(expected);
}
protected:
MockConfigurationPolicyProvider provider0_;
MockConfigurationPolicyProvider provider1_;
MockConfigurationPolicyProvider provider2_;
PolicyMap policy0_;
PolicyMap policy1_;
PolicyMap policy2_;
scoped_ptr<PolicyServiceImpl> policy_service_;
private:
DISALLOW_COPY_AND_ASSIGN(PolicyServiceTest);
};
TEST_F(PolicyServiceTest, LoadsPoliciesBeforeProvidersRefresh) {
PolicyMap expected;
expected.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(13));
EXPECT_TRUE(VerifyPolicies(POLICY_DOMAIN_CHROME, "", expected));
}
TEST_F(PolicyServiceTest, NotifyObservers) {
MockPolicyServiceObserver observer;
policy_service_->AddObserver(POLICY_DOMAIN_CHROME, &observer);
PolicyMap expectedPrevious;
expectedPrevious.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(13));
PolicyMap expectedCurrent;
expectedCurrent.CopyFrom(expectedPrevious);
expectedCurrent.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(123));
policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(123));
EXPECT_CALL(observer, OnPolicyUpdated(POLICY_DOMAIN_CHROME, "",
PolicyEquals(&expectedPrevious),
PolicyEquals(&expectedCurrent)));
provider0_.UpdateChromePolicy(policy0_);
Mock::VerifyAndClearExpectations(&observer);
// No changes.
EXPECT_CALL(observer, OnPolicyUpdated(_, _, _, _)).Times(0);
provider0_.UpdateChromePolicy(policy0_);
Mock::VerifyAndClearExpectations(&observer);
EXPECT_TRUE(VerifyPolicies(POLICY_DOMAIN_CHROME, "", expectedCurrent));
// New policy.
expectedPrevious.CopyFrom(expectedCurrent);
expectedCurrent.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(456));
policy0_.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(456));
EXPECT_CALL(observer, OnPolicyUpdated(POLICY_DOMAIN_CHROME, "",
PolicyEquals(&expectedPrevious),
PolicyEquals(&expectedCurrent)));
provider0_.UpdateChromePolicy(policy0_);
Mock::VerifyAndClearExpectations(&observer);
// Removed policy.
expectedPrevious.CopyFrom(expectedCurrent);
expectedCurrent.Erase("bbb");
policy0_.Erase("bbb");
EXPECT_CALL(observer, OnPolicyUpdated(POLICY_DOMAIN_CHROME, "",
PolicyEquals(&expectedPrevious),
PolicyEquals(&expectedCurrent)));
provider0_.UpdateChromePolicy(policy0_);
Mock::VerifyAndClearExpectations(&observer);
// Changed policy.
expectedPrevious.CopyFrom(expectedCurrent);
expectedCurrent.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(789));
policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(789));
EXPECT_CALL(observer, OnPolicyUpdated(POLICY_DOMAIN_CHROME, "",
PolicyEquals(&expectedPrevious),
PolicyEquals(&expectedCurrent)));
provider0_.UpdateChromePolicy(policy0_);
Mock::VerifyAndClearExpectations(&observer);
// No changes again.
EXPECT_CALL(observer, OnPolicyUpdated(_, _, _, _)).Times(0);
provider0_.UpdateChromePolicy(policy0_);
Mock::VerifyAndClearExpectations(&observer);
EXPECT_TRUE(VerifyPolicies(POLICY_DOMAIN_CHROME, "", expectedCurrent));
policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, &observer);
}
TEST_F(PolicyServiceTest, NotifyObserversInMultipleNamespaces) {
const std::string kExtension0("extension-0");
const std::string kExtension1("extension-1");
const std::string kExtension2("extension-2");
MockPolicyServiceObserver chrome_observer;
MockPolicyServiceObserver extension_observer;
policy_service_->AddObserver(POLICY_DOMAIN_CHROME, &chrome_observer);
policy_service_->AddObserver(POLICY_DOMAIN_EXTENSIONS, &extension_observer);
PolicyMap previous_policy_map;
previous_policy_map.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(13));
PolicyMap policy_map;
policy_map.CopyFrom(previous_policy_map);
policy_map.Set("policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateStringValue("value"));
scoped_ptr<PolicyBundle> bundle(new PolicyBundle());
// The initial setup includes a policy for chrome that is now changing.
bundle->Get(POLICY_DOMAIN_CHROME, "").CopyFrom(policy_map);
bundle->Get(POLICY_DOMAIN_EXTENSIONS, kExtension0).CopyFrom(policy_map);
bundle->Get(POLICY_DOMAIN_EXTENSIONS, kExtension1).CopyFrom(policy_map);
const PolicyMap kEmptyPolicyMap;
EXPECT_CALL(chrome_observer,
OnPolicyUpdated(POLICY_DOMAIN_CHROME, "",
PolicyEquals(&previous_policy_map),
PolicyEquals(&policy_map)));
EXPECT_CALL(extension_observer,
OnPolicyUpdated(POLICY_DOMAIN_EXTENSIONS, kExtension0,
PolicyEquals(&kEmptyPolicyMap),
PolicyEquals(&policy_map)));
EXPECT_CALL(extension_observer,
OnPolicyUpdated(POLICY_DOMAIN_EXTENSIONS, kExtension1,
PolicyEquals(&kEmptyPolicyMap),
PolicyEquals(&policy_map)));
provider0_.UpdatePolicy(bundle.Pass());
Mock::VerifyAndClearExpectations(&chrome_observer);
Mock::VerifyAndClearExpectations(&extension_observer);
// Chrome policy stays the same, kExtension0 is gone, kExtension1 changes,
// and kExtension2 is new.
previous_policy_map.CopyFrom(policy_map);
bundle.reset(new PolicyBundle());
bundle->Get(POLICY_DOMAIN_CHROME, "").CopyFrom(policy_map);
policy_map.Set("policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateStringValue("another value"));
bundle->Get(POLICY_DOMAIN_EXTENSIONS, kExtension1).CopyFrom(policy_map);
bundle->Get(POLICY_DOMAIN_EXTENSIONS, kExtension2).CopyFrom(policy_map);
EXPECT_CALL(chrome_observer, OnPolicyUpdated(_, _, _, _)).Times(0);
EXPECT_CALL(extension_observer,
OnPolicyUpdated(POLICY_DOMAIN_EXTENSIONS, kExtension0,
PolicyEquals(&previous_policy_map),
PolicyEquals(&kEmptyPolicyMap)));
EXPECT_CALL(extension_observer,
OnPolicyUpdated(POLICY_DOMAIN_EXTENSIONS, kExtension1,
PolicyEquals(&previous_policy_map),
PolicyEquals(&policy_map)));
EXPECT_CALL(extension_observer,
OnPolicyUpdated(POLICY_DOMAIN_EXTENSIONS, kExtension2,
PolicyEquals(&kEmptyPolicyMap),
PolicyEquals(&policy_map)));
provider0_.UpdatePolicy(bundle.Pass());
Mock::VerifyAndClearExpectations(&chrome_observer);
Mock::VerifyAndClearExpectations(&extension_observer);
policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, &chrome_observer);
policy_service_->RemoveObserver(POLICY_DOMAIN_EXTENSIONS,
&extension_observer);
}
TEST_F(PolicyServiceTest, Priorities) {
PolicyMap expected;
expected.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(13));
expected.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(0));
policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(0));
policy1_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(1));
policy2_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(2));
provider0_.UpdateChromePolicy(policy0_);
provider1_.UpdateChromePolicy(policy1_);
provider2_.UpdateChromePolicy(policy2_);
EXPECT_TRUE(VerifyPolicies(POLICY_DOMAIN_CHROME, "", expected));
expected.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(1));
policy0_.Erase("aaa");
provider0_.UpdateChromePolicy(policy0_);
EXPECT_TRUE(VerifyPolicies(POLICY_DOMAIN_CHROME, "", expected));
expected.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(2));
policy1_.Set("aaa", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
base::Value::CreateIntegerValue(1));
provider1_.UpdateChromePolicy(policy1_);
EXPECT_TRUE(VerifyPolicies(POLICY_DOMAIN_CHROME, "", expected));
}
TEST_F(PolicyServiceTest, PolicyChangeRegistrar) {
scoped_ptr<PolicyChangeRegistrar> registrar(
new PolicyChangeRegistrar(
policy_service_.get(), POLICY_DOMAIN_CHROME, ""));
// Starting to observe existing policies doesn't trigger a notification.
EXPECT_CALL(*this, OnPolicyValueUpdated(_, _)).Times(0);
registrar->Observe("pre", base::Bind(
&PolicyServiceTest::OnPolicyValueUpdated,
base::Unretained(this)));
registrar->Observe("aaa", base::Bind(
&PolicyServiceTest::OnPolicyValueUpdated,
base::Unretained(this)));
Mock::VerifyAndClearExpectations(this);
// Changing it now triggers a notification.
base::FundamentalValue kValue0(0);
EXPECT_CALL(*this, OnPolicyValueUpdated(NULL, ValueEquals(&kValue0)));
policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
kValue0.DeepCopy());
provider0_.UpdateChromePolicy(policy0_);
Mock::VerifyAndClearExpectations(this);
// Changing other values doesn't trigger a notification.
EXPECT_CALL(*this, OnPolicyValueUpdated(_, _)).Times(0);
policy0_.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
kValue0.DeepCopy());
provider0_.UpdateChromePolicy(policy0_);
Mock::VerifyAndClearExpectations(this);
// Modifying the value triggers a notification.
base::FundamentalValue kValue1(1);
EXPECT_CALL(*this, OnPolicyValueUpdated(ValueEquals(&kValue0),
ValueEquals(&kValue1)));
policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
kValue1.DeepCopy());
provider0_.UpdateChromePolicy(policy0_);
Mock::VerifyAndClearExpectations(this);
// Removing the value triggers a notification.
EXPECT_CALL(*this, OnPolicyValueUpdated(ValueEquals(&kValue1), NULL));
policy0_.Erase("aaa");
provider0_.UpdateChromePolicy(policy0_);
Mock::VerifyAndClearExpectations(this);
// No more notifications after destroying the registrar.
EXPECT_CALL(*this, OnPolicyValueUpdated(_, _)).Times(0);
registrar.reset();
policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
kValue1.DeepCopy());
policy0_.Set("pre", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
kValue1.DeepCopy());
provider0_.UpdateChromePolicy(policy0_);
Mock::VerifyAndClearExpectations(this);
}
TEST_F(PolicyServiceTest, RefreshPolicies) {
MessageLoop loop;
content::TestBrowserThread ui_thread(content::BrowserThread::UI, &loop);
content::TestBrowserThread file_thread(content::BrowserThread::FILE, &loop);
content::TestBrowserThread io_thread(content::BrowserThread::IO, &loop);
EXPECT_CALL(provider0_, RefreshPolicies()).Times(AnyNumber());
EXPECT_CALL(provider1_, RefreshPolicies()).Times(AnyNumber());
EXPECT_CALL(provider2_, RefreshPolicies()).Times(AnyNumber());
EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
policy_service_->RefreshPolicies(base::Bind(
&PolicyServiceTest::OnPolicyRefresh,
base::Unretained(this)));
loop.RunAllPending();
Mock::VerifyAndClearExpectations(this);
EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
base::FundamentalValue kValue0(0);
policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
kValue0.DeepCopy());
provider0_.UpdateChromePolicy(policy0_);
loop.RunAllPending();
Mock::VerifyAndClearExpectations(this);
EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
base::FundamentalValue kValue1(1);
policy1_.Set("aaa", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
kValue1.DeepCopy());
provider1_.UpdateChromePolicy(policy1_);
loop.RunAllPending();
Mock::VerifyAndClearExpectations(this);
// A provider can refresh more than once after a RefreshPolicies call, but
// OnPolicyRefresh should be triggered only after all providers are
// refreshed.
EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
policy1_.Set("bbb", POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER,
kValue1.DeepCopy());
provider1_.UpdateChromePolicy(policy1_);
loop.RunAllPending();
Mock::VerifyAndClearExpectations(this);
// If another RefreshPolicies() call happens while waiting for a previous
// one to complete, then all providers must refresh again.
EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
policy_service_->RefreshPolicies(base::Bind(
&PolicyServiceTest::OnPolicyRefresh,
base::Unretained(this)));
loop.RunAllPending();
Mock::VerifyAndClearExpectations(this);
EXPECT_CALL(*this, OnPolicyRefresh()).Times(0);
policy2_.Set("bbb", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
kValue0.DeepCopy());
provider2_.UpdateChromePolicy(policy2_);
loop.RunAllPending();
Mock::VerifyAndClearExpectations(this);
// Providers 0 and 1 must reload again.
EXPECT_CALL(*this, OnPolicyRefresh()).Times(2);
base::FundamentalValue kValue2(2);
policy0_.Set("aaa", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
kValue2.DeepCopy());
provider0_.UpdateChromePolicy(policy0_);
provider1_.UpdateChromePolicy(policy1_);
loop.RunAllPending();
Mock::VerifyAndClearExpectations(this);
const PolicyMap& policies = policy_service_->GetPolicies(
POLICY_DOMAIN_CHROME, "");
EXPECT_TRUE(base::Value::Equals(&kValue2, policies.GetValue("aaa")));
EXPECT_TRUE(base::Value::Equals(&kValue0, policies.GetValue("bbb")));
}
TEST_F(PolicyServiceTest, NamespaceMerge) {
scoped_ptr<PolicyBundle> bundle0(new PolicyBundle());
scoped_ptr<PolicyBundle> bundle1(new PolicyBundle());
scoped_ptr<PolicyBundle> bundle2(new PolicyBundle());
AddTestPolicies(bundle0.get(), "bundle0",
POLICY_LEVEL_RECOMMENDED, POLICY_SCOPE_USER);
AddTestPolicies(bundle1.get(), "bundle1",
POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER);
AddTestPolicies(bundle2.get(), "bundle2",
POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE);
provider0_.UpdatePolicy(bundle0.Pass());
provider1_.UpdatePolicy(bundle1.Pass());
provider2_.UpdatePolicy(bundle2.Pass());
PolicyMap expected;
// For policies of the same level and scope, the first provider takes
// precedence, on every namespace.
expected.Set(kSameLevelPolicy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
base::Value::CreateStringValue("bundle0"));
// For policies with different levels and scopes, the highest priority
// level/scope combination takes precedence, on every namespace.
expected.Set(kDiffLevelPolicy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
base::Value::CreateStringValue("bundle2"));
EXPECT_TRUE(policy_service_->GetPolicies(POLICY_DOMAIN_CHROME, "")
.Equals(expected));
EXPECT_TRUE(policy_service_->GetPolicies(POLICY_DOMAIN_EXTENSIONS, kExtension)
.Equals(expected));
}
} // namespace policy