1// Copyright (c) 2012 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 "components/policy/core/common/cloud/cloud_policy_service.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "components/policy/core/common/cloud/cloud_policy_constants.h"
10#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
11#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
12#include "policy/proto/device_management_backend.pb.h"
13#include "testing/gmock/include/gmock/gmock.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16namespace em = enterprise_management;
17
18using testing::_;
19
20namespace policy {
21
22class MockCloudPolicyServiceObserver : public CloudPolicyService::Observer {
23 public:
24  MockCloudPolicyServiceObserver() {}
25  virtual ~MockCloudPolicyServiceObserver() {}
26
27  MOCK_METHOD1(OnInitializationCompleted, void(CloudPolicyService* service));
28 private:
29  DISALLOW_COPY_AND_ASSIGN(MockCloudPolicyServiceObserver);
30};
31
32class CloudPolicyServiceTest : public testing::Test {
33 public:
34  CloudPolicyServiceTest()
35      : policy_ns_key_(dm_protocol::kChromeUserPolicyType, std::string()),
36        service_(policy_ns_key_, &client_, &store_) {}
37
38  MOCK_METHOD1(OnPolicyRefresh, void(bool));
39
40 protected:
41  PolicyNamespaceKey policy_ns_key_;
42  MockCloudPolicyClient client_;
43  MockCloudPolicyStore store_;
44  CloudPolicyService service_;
45};
46
47MATCHER_P(ProtoMatches, proto, "") {
48  return arg.SerializePartialAsString() == proto.SerializePartialAsString();
49}
50
51TEST_F(CloudPolicyServiceTest, ManagedByEmptyPolicy) {
52  EXPECT_EQ("", service_.ManagedBy());
53}
54
55TEST_F(CloudPolicyServiceTest, ManagedByValidPolicy) {
56  store_.policy_.reset(new em::PolicyData());
57  store_.policy_->set_username("user@example.com");
58  EXPECT_EQ("example.com", service_.ManagedBy());
59}
60
61TEST_F(CloudPolicyServiceTest, PolicyUpdateSuccess) {
62  em::PolicyFetchResponse policy;
63  policy.set_policy_data("fake policy");
64  client_.SetPolicy(policy_ns_key_, policy);
65  EXPECT_CALL(store_, Store(ProtoMatches(policy))).Times(1);
66  client_.NotifyPolicyFetched();
67
68  // After |store_| initializes, credentials and other meta data should be
69  // transferred to |client_|.
70  store_.policy_.reset(new em::PolicyData());
71  store_.policy_->set_request_token("fake token");
72  store_.policy_->set_device_id("fake client id");
73  store_.policy_->set_timestamp(32);
74  store_.policy_->set_valid_serial_number_missing(true);
75  store_.policy_->set_public_key_version(17);
76  EXPECT_CALL(client_,
77              SetupRegistration(store_.policy_->request_token(),
78                                store_.policy_->device_id())).Times(1);
79  store_.NotifyStoreLoaded();
80  EXPECT_EQ(base::Time::UnixEpoch() + base::TimeDelta::FromMilliseconds(32),
81            client_.last_policy_timestamp_);
82  EXPECT_TRUE(client_.submit_machine_id_);
83  EXPECT_TRUE(client_.public_key_version_valid_);
84  EXPECT_EQ(17, client_.public_key_version_);
85}
86
87TEST_F(CloudPolicyServiceTest, PolicyUpdateClientFailure) {
88  client_.SetStatus(DM_STATUS_REQUEST_FAILED);
89  EXPECT_CALL(store_, Store(_)).Times(0);
90  client_.NotifyPolicyFetched();
91}
92
93TEST_F(CloudPolicyServiceTest, RefreshPolicySuccess) {
94  testing::InSequence seq;
95
96  EXPECT_CALL(*this, OnPolicyRefresh(_)).Times(0);
97  client_.SetDMToken("fake token");
98
99  // Trigger a fetch on the client.
100  EXPECT_CALL(client_, FetchPolicy()).Times(1);
101  service_.RefreshPolicy(base::Bind(&CloudPolicyServiceTest::OnPolicyRefresh,
102                                    base::Unretained(this)));
103
104  // Client responds, push policy to store.
105  em::PolicyFetchResponse policy;
106  policy.set_policy_data("fake policy");
107  client_.SetPolicy(policy_ns_key_, policy);
108  client_.fetched_invalidation_version_ = 12345;
109  EXPECT_CALL(store_, Store(ProtoMatches(policy))).Times(1);
110  EXPECT_EQ(0, store_.invalidation_version());
111  client_.NotifyPolicyFetched();
112  EXPECT_EQ(12345, store_.invalidation_version());
113
114  // Store reloads policy, callback gets triggered.
115  store_.policy_.reset(new em::PolicyData());
116  store_.policy_->set_request_token("token");
117  store_.policy_->set_device_id("device-id");
118  EXPECT_CALL(*this, OnPolicyRefresh(true)).Times(1);
119  store_.NotifyStoreLoaded();
120}
121
122TEST_F(CloudPolicyServiceTest, RefreshPolicyNotRegistered) {
123  // Clear the token so the client is not registered.
124  client_.SetDMToken(std::string());
125
126  EXPECT_CALL(client_, FetchPolicy()).Times(0);
127  EXPECT_CALL(*this, OnPolicyRefresh(false)).Times(1);
128  service_.RefreshPolicy(base::Bind(&CloudPolicyServiceTest::OnPolicyRefresh,
129                                    base::Unretained(this)));
130}
131
132TEST_F(CloudPolicyServiceTest, RefreshPolicyClientError) {
133  testing::InSequence seq;
134
135  EXPECT_CALL(*this, OnPolicyRefresh(_)).Times(0);
136  client_.SetDMToken("fake token");
137
138  // Trigger a fetch on the client.
139  EXPECT_CALL(client_, FetchPolicy()).Times(1);
140  service_.RefreshPolicy(base::Bind(&CloudPolicyServiceTest::OnPolicyRefresh,
141                                    base::Unretained(this)));
142
143  // Client responds with an error, which should trigger the callback.
144  client_.SetStatus(DM_STATUS_REQUEST_FAILED);
145  EXPECT_CALL(*this, OnPolicyRefresh(false)).Times(1);
146  client_.NotifyClientError();
147}
148
149TEST_F(CloudPolicyServiceTest, RefreshPolicyStoreError) {
150  testing::InSequence seq;
151
152  EXPECT_CALL(*this, OnPolicyRefresh(_)).Times(0);
153  client_.SetDMToken("fake token");
154
155  // Trigger a fetch on the client.
156  EXPECT_CALL(client_, FetchPolicy()).Times(1);
157  service_.RefreshPolicy(base::Bind(&CloudPolicyServiceTest::OnPolicyRefresh,
158                                    base::Unretained(this)));
159
160  // Client responds, push policy to store.
161  em::PolicyFetchResponse policy;
162  policy.set_policy_data("fake policy");
163  client_.SetPolicy(policy_ns_key_, policy);
164  EXPECT_CALL(store_, Store(ProtoMatches(policy))).Times(1);
165  client_.NotifyPolicyFetched();
166
167  // Store fails, which should trigger the callback.
168  EXPECT_CALL(*this, OnPolicyRefresh(false)).Times(1);
169  store_.NotifyStoreError();
170}
171
172TEST_F(CloudPolicyServiceTest, RefreshPolicyConcurrent) {
173  testing::InSequence seq;
174
175  EXPECT_CALL(*this, OnPolicyRefresh(_)).Times(0);
176  client_.SetDMToken("fake token");
177
178  // Trigger a fetch on the client.
179  EXPECT_CALL(client_, FetchPolicy()).Times(1);
180  service_.RefreshPolicy(base::Bind(&CloudPolicyServiceTest::OnPolicyRefresh,
181                                    base::Unretained(this)));
182
183  // Triggering another policy refresh should generate a new fetch request.
184  EXPECT_CALL(client_, FetchPolicy()).Times(1);
185  service_.RefreshPolicy(base::Bind(&CloudPolicyServiceTest::OnPolicyRefresh,
186                                    base::Unretained(this)));
187
188  // Client responds, push policy to store.
189  em::PolicyFetchResponse policy;
190  policy.set_policy_data("fake policy");
191  client_.SetPolicy(policy_ns_key_, policy);
192  EXPECT_CALL(store_, Store(ProtoMatches(policy))).Times(1);
193  client_.NotifyPolicyFetched();
194
195  // Trigger another policy fetch.
196  EXPECT_CALL(client_, FetchPolicy()).Times(1);
197  service_.RefreshPolicy(base::Bind(&CloudPolicyServiceTest::OnPolicyRefresh,
198                                    base::Unretained(this)));
199
200  // The store finishing the first load should not generate callbacks.
201  EXPECT_CALL(*this, OnPolicyRefresh(_)).Times(0);
202  store_.NotifyStoreLoaded();
203
204  // Second policy fetch finishes.
205  EXPECT_CALL(store_, Store(ProtoMatches(policy))).Times(1);
206  client_.NotifyPolicyFetched();
207
208  // Corresponding store operation finishes, all _three_ callbacks fire.
209  EXPECT_CALL(*this, OnPolicyRefresh(true)).Times(3);
210  store_.NotifyStoreLoaded();
211}
212
213TEST_F(CloudPolicyServiceTest, StoreAlreadyInitialized) {
214  // Service should start off initialized if the store has already loaded
215  // policy.
216  store_.NotifyStoreLoaded();
217  CloudPolicyService service(policy_ns_key_, &client_, &store_);
218  EXPECT_TRUE(service.IsInitializationComplete());
219}
220
221TEST_F(CloudPolicyServiceTest, StoreLoadAfterCreation) {
222  // Service should start off un-initialized if the store has not yet loaded
223  // policy.
224  EXPECT_FALSE(service_.IsInitializationComplete());
225  MockCloudPolicyServiceObserver observer;
226  service_.AddObserver(&observer);
227  // Service should be marked as initialized and observer should be called back.
228  EXPECT_CALL(observer, OnInitializationCompleted(&service_)).Times(1);
229  store_.NotifyStoreLoaded();
230  EXPECT_TRUE(service_.IsInitializationComplete());
231  testing::Mock::VerifyAndClearExpectations(&observer);
232
233  // Now, the next time the store is loaded, the observer should not be called
234  // again.
235  EXPECT_CALL(observer, OnInitializationCompleted(&service_)).Times(0);
236  store_.NotifyStoreLoaded();
237  service_.RemoveObserver(&observer);
238}
239
240}  // namespace policy
241