cloud_external_data_policy_observer.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright 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/chromeos/policy/cloud_external_data_policy_observer.h"
6
7#include <set>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/logging.h"
13#include "base/stl_util.h"
14#include "base/values.h"
15#include "chrome/browser/chrome_notification_types.h"
16#include "chrome/browser/chromeos/login/users/user.h"
17#include "chrome/browser/chromeos/login/users/user_manager.h"
18#include "chrome/browser/chromeos/policy/device_local_account.h"
19#include "chrome/browser/policy/profile_policy_connector.h"
20#include "chrome/browser/policy/profile_policy_connector_factory.h"
21#include "chrome/browser/profiles/profile.h"
22#include "chromeos/settings/cros_settings_names.h"
23#include "chromeos/settings/cros_settings_provider.h"
24#include "components/policy/core/common/cloud/cloud_policy_core.h"
25#include "components/policy/core/common/cloud/cloud_policy_store.h"
26#include "components/policy/core/common/external_data_fetcher.h"
27#include "components/policy/core/common/policy_namespace.h"
28#include "components/policy/core/common/policy_service.h"
29#include "content/public/browser/notification_details.h"
30#include "content/public/browser/notification_service.h"
31#include "content/public/browser/notification_source.h"
32
33namespace policy {
34
35// Helper class that observes a policy for a logged-in user, notifying the
36// |parent_| whenever the external data reference for this user changes.
37class CloudExternalDataPolicyObserver::PolicyServiceObserver
38    : public PolicyService::Observer {
39 public:
40  PolicyServiceObserver(CloudExternalDataPolicyObserver* parent,
41                        const std::string& user_id,
42                        PolicyService* policy_service);
43  virtual ~PolicyServiceObserver();
44
45  // PolicyService::Observer:
46  virtual void OnPolicyUpdated(const PolicyNamespace& ns,
47                               const PolicyMap& previous,
48                               const PolicyMap& current) OVERRIDE;
49
50 private:
51  CloudExternalDataPolicyObserver* parent_;
52  const std::string user_id_;
53  PolicyService* policy_service_;
54
55  DISALLOW_COPY_AND_ASSIGN(PolicyServiceObserver);
56};
57
58CloudExternalDataPolicyObserver::PolicyServiceObserver::PolicyServiceObserver(
59    CloudExternalDataPolicyObserver* parent,
60    const std::string& user_id,
61    PolicyService* policy_service)
62    : parent_(parent),
63      user_id_(user_id),
64      policy_service_(policy_service) {
65  policy_service_->AddObserver(POLICY_DOMAIN_CHROME, this);
66
67  if (!IsDeviceLocalAccountUser(user_id, NULL)) {
68    // Notify |parent_| if the external data reference for |user_id_| is set
69    // during login. This is omitted for device-local accounts because their
70    // policy is available before login and the external data reference will
71    // have been seen by the |parent_| already.
72    const PolicyMap::Entry* entry = policy_service_->GetPolicies(
73        PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
74            .Get(parent_->policy_);
75    if (entry)
76      parent_->HandleExternalDataPolicyUpdate(user_id_, entry);
77  }
78}
79
80CloudExternalDataPolicyObserver::PolicyServiceObserver::
81    ~PolicyServiceObserver() {
82  policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this);
83}
84
85void CloudExternalDataPolicyObserver::PolicyServiceObserver::OnPolicyUpdated(
86    const PolicyNamespace& ns,
87    const PolicyMap& previous,
88    const PolicyMap& current) {
89  DCHECK(ns == PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
90
91  const PolicyMap::Entry* previous_entry = previous.Get(parent_->policy_);
92  const PolicyMap::Entry* current_entry = current.Get(parent_->policy_);
93  if ((!previous_entry && current_entry) ||
94      (previous_entry && !current_entry) ||
95      (previous_entry && current_entry &&
96           !previous_entry->Equals(*current_entry))) {
97    // Notify |parent_| if the external data reference for |user_id_| has
98    // changed.
99    parent_->HandleExternalDataPolicyUpdate(user_id_, current_entry);
100  }
101}
102
103void CloudExternalDataPolicyObserver::Delegate::OnExternalDataSet(
104    const std::string& policy,
105    const std::string& user_id) {
106}
107
108void CloudExternalDataPolicyObserver::Delegate::OnExternalDataCleared(
109    const std::string& policy,
110    const std::string& user_id) {
111}
112
113void CloudExternalDataPolicyObserver::Delegate::OnExternalDataFetched(
114    const std::string& policy,
115    const std::string& user_id,
116    scoped_ptr<std::string> data) {
117}
118
119CloudExternalDataPolicyObserver::Delegate::~Delegate() {
120}
121
122CloudExternalDataPolicyObserver::CloudExternalDataPolicyObserver(
123    chromeos::CrosSettings* cros_settings,
124    chromeos::UserManager* user_manager,
125    DeviceLocalAccountPolicyService* device_local_account_policy_service,
126    const std::string& policy,
127    Delegate* delegate)
128    : cros_settings_(cros_settings),
129      user_manager_(user_manager),
130      device_local_account_policy_service_(device_local_account_policy_service),
131      policy_(policy),
132      delegate_(delegate),
133      weak_factory_(this) {
134  notification_registrar_.Add(
135      this,
136      chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
137      content::NotificationService::AllSources());
138
139  if (device_local_account_policy_service_)
140    device_local_account_policy_service_->AddObserver(this);
141
142  device_local_accounts_subscription_ = cros_settings_->AddSettingsObserver(
143      chromeos::kAccountsPrefDeviceLocalAccounts,
144      base::Bind(&CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts,
145                 base::Unretained(this)));
146}
147
148CloudExternalDataPolicyObserver::~CloudExternalDataPolicyObserver() {
149  if (device_local_account_policy_service_)
150    device_local_account_policy_service_->RemoveObserver(this);
151  for (DeviceLocalAccountEntryMap::iterator it =
152           device_local_account_entries_.begin();
153       it != device_local_account_entries_.end(); ++it) {
154    it->second.DeleteOwnedMembers();
155  }
156  device_local_account_entries_.clear();
157}
158
159void CloudExternalDataPolicyObserver::Init() {
160  RetrieveDeviceLocalAccounts();
161}
162
163void CloudExternalDataPolicyObserver::Observe(
164    int type,
165    const content::NotificationSource& source,
166    const content::NotificationDetails& details) {
167  if (type != chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) {
168    NOTREACHED();
169    return;
170  }
171  Profile* profile = content::Details<Profile>(details).ptr();
172
173  const chromeos::User* user = user_manager_->GetUserByProfile(profile);
174  if (!user) {
175    NOTREACHED();
176    return;
177  }
178
179  const std::string& user_id = user->email();
180  if (ContainsKey(logged_in_user_observers_, user_id)) {
181    NOTREACHED();
182    return;
183  }
184
185  ProfilePolicyConnector* policy_connector =
186      ProfilePolicyConnectorFactory::GetForProfile(profile);
187  logged_in_user_observers_[user_id] = make_linked_ptr(
188      new PolicyServiceObserver(this,
189                                user_id,
190                                policy_connector->policy_service()));
191}
192
193void CloudExternalDataPolicyObserver::OnPolicyUpdated(
194    const std::string& user_id) {
195  if (ContainsKey(logged_in_user_observers_, user_id)) {
196    // When a device-local account is logged in, a policy change triggers both
197    // OnPolicyUpdated() and PolicyServiceObserver::OnPolicyUpdated(). Ignore
198    // the former so that the policy change is handled only once.
199    return;
200  }
201
202  if (!device_local_account_policy_service_) {
203    NOTREACHED();
204    return;
205  }
206  DeviceLocalAccountPolicyBroker* broker =
207      device_local_account_policy_service_->GetBrokerForUser(user_id);
208  if (!broker) {
209    // The order in which |this| and the |device_local_account_policy_service_|
210    // find out that a new device-local account has been added is undefined. If
211    // no |broker| exists yet, the |device_local_account_policy_service_| must
212    // not have seen the new |user_id| yet. OnPolicyUpdated() will be invoked
213    // again by the |device_local_account_policy_service_| in this case when it
214    // finds out about |user_id| and creates a |broker| for it.
215    return;
216  }
217
218  const PolicyMap::Entry* entry =
219      broker->core()->store()->policy_map().Get(policy_);
220  if (!entry) {
221    DeviceLocalAccountEntryMap::iterator it =
222        device_local_account_entries_.find(user_id);
223    if (it != device_local_account_entries_.end()) {
224      it->second.DeleteOwnedMembers();
225      device_local_account_entries_.erase(it);
226      HandleExternalDataPolicyUpdate(user_id, NULL);
227    }
228    return;
229  }
230
231  PolicyMap::Entry& map_entry = device_local_account_entries_[user_id];
232  if (map_entry.Equals(*entry))
233    return;
234
235  map_entry.DeleteOwnedMembers();
236  map_entry = *entry->DeepCopy();
237  HandleExternalDataPolicyUpdate(user_id, entry);
238}
239
240void CloudExternalDataPolicyObserver::OnDeviceLocalAccountsChanged() {
241  // No action needed here, changes to the list of device-local accounts get
242  // handled via the kAccountsPrefDeviceLocalAccounts device setting observer.
243}
244
245void CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts() {
246  // Schedule a callback if device policy has not yet been verified.
247  if (chromeos::CrosSettingsProvider::TRUSTED !=
248      cros_settings_->PrepareTrustedValues(base::Bind(
249          &CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts,
250          weak_factory_.GetWeakPtr()))) {
251    return;
252  }
253
254  std::vector<DeviceLocalAccount> device_local_account_list =
255      policy::GetDeviceLocalAccounts(cros_settings_);
256  std::set<std::string> device_local_accounts;
257  for (std::vector<DeviceLocalAccount>::const_iterator it =
258           device_local_account_list.begin();
259       it != device_local_account_list.end(); ++it) {
260    device_local_accounts.insert(it->user_id);
261  }
262
263  for (DeviceLocalAccountEntryMap::iterator it =
264           device_local_account_entries_.begin();
265       it != device_local_account_entries_.end(); ) {
266    if (!ContainsKey(device_local_accounts, it->first)) {
267      const std::string user_id = it->first;
268      it->second.DeleteOwnedMembers();
269      device_local_account_entries_.erase(it++);
270      // When a device-local account whose external data reference was set is
271      // removed, emit a notification that the external data reference has been
272      // cleared.
273      HandleExternalDataPolicyUpdate(user_id, NULL);
274    } else {
275      ++it;
276    }
277  }
278
279  for (std::set<std::string>::const_iterator it = device_local_accounts.begin();
280       it != device_local_accounts.end(); ++it) {
281    OnPolicyUpdated(*it);
282  }
283}
284
285void CloudExternalDataPolicyObserver::HandleExternalDataPolicyUpdate(
286    const std::string& user_id,
287    const PolicyMap::Entry* entry) {
288  if (!entry) {
289    delegate_->OnExternalDataCleared(policy_, user_id);
290    fetch_weak_ptrs_.erase(user_id);
291    return;
292  }
293
294  delegate_->OnExternalDataSet(policy_, user_id);
295
296  linked_ptr<WeakPtrFactory>& weak_ptr_factory = fetch_weak_ptrs_[user_id];
297  weak_ptr_factory.reset(new WeakPtrFactory(this));
298  if (entry->external_data_fetcher) {
299    entry->external_data_fetcher->Fetch(base::Bind(
300        &CloudExternalDataPolicyObserver::OnExternalDataFetched,
301        weak_ptr_factory->GetWeakPtr(),
302        user_id));
303  } else {
304    NOTREACHED();
305  }
306}
307
308void CloudExternalDataPolicyObserver::OnExternalDataFetched(
309    const std::string& user_id,
310    scoped_ptr<std::string> data) {
311  FetchWeakPtrMap::iterator it = fetch_weak_ptrs_.find(user_id);
312  DCHECK(it != fetch_weak_ptrs_.end());
313  fetch_weak_ptrs_.erase(it);
314  delegate_->OnExternalDataFetched(policy_, user_id, data.Pass());
315}
316
317}  // namespace policy
318