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.
5 #include "chrome/browser/chromeos/policy/cloud_external_data_policy_observer.h"
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/user.h"
17 #include "chrome/browser/chromeos/login/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"
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.
37 class CloudExternalDataPolicyObserver::PolicyServiceObserver
38 : public PolicyService::Observer
{
40 PolicyServiceObserver(CloudExternalDataPolicyObserver
* parent
,
41 const std::string
& user_id
,
42 PolicyService
* policy_service
);
43 virtual ~PolicyServiceObserver();
45 // PolicyService::Observer:
46 virtual void OnPolicyUpdated(const PolicyNamespace
& ns
,
47 const PolicyMap
& previous
,
48 const PolicyMap
& current
) OVERRIDE
;
51 CloudExternalDataPolicyObserver
* parent_
;
52 const std::string user_id_
;
53 PolicyService
* policy_service_
;
55 DISALLOW_COPY_AND_ASSIGN(PolicyServiceObserver
);
58 CloudExternalDataPolicyObserver::PolicyServiceObserver::PolicyServiceObserver(
59 CloudExternalDataPolicyObserver
* parent
,
60 const std::string
& user_id
,
61 PolicyService
* policy_service
)
64 policy_service_(policy_service
) {
65 policy_service_
->AddObserver(POLICY_DOMAIN_CHROME
, this);
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_
);
76 parent_
->HandleExternalDataPolicyUpdate(user_id_
, entry
);
80 CloudExternalDataPolicyObserver::PolicyServiceObserver::
81 ~PolicyServiceObserver() {
82 policy_service_
->RemoveObserver(POLICY_DOMAIN_CHROME
, this);
85 void CloudExternalDataPolicyObserver::PolicyServiceObserver::OnPolicyUpdated(
86 const PolicyNamespace
& ns
,
87 const PolicyMap
& previous
,
88 const PolicyMap
& current
) {
89 DCHECK(ns
== PolicyNamespace(POLICY_DOMAIN_CHROME
, std::string()));
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
99 parent_
->HandleExternalDataPolicyUpdate(user_id_
, current_entry
);
103 void CloudExternalDataPolicyObserver::Delegate::OnExternalDataSet(
104 const std::string
& policy
,
105 const std::string
& user_id
) {
108 void CloudExternalDataPolicyObserver::Delegate::OnExternalDataCleared(
109 const std::string
& policy
,
110 const std::string
& user_id
) {
113 void CloudExternalDataPolicyObserver::Delegate::OnExternalDataFetched(
114 const std::string
& policy
,
115 const std::string
& user_id
,
116 scoped_ptr
<std::string
> data
) {
119 CloudExternalDataPolicyObserver::Delegate::~Delegate() {
122 CloudExternalDataPolicyObserver::CloudExternalDataPolicyObserver(
123 chromeos::CrosSettings
* cros_settings
,
124 chromeos::UserManager
* user_manager
,
125 DeviceLocalAccountPolicyService
* device_local_account_policy_service
,
126 const std::string
& policy
,
128 : cros_settings_(cros_settings
),
129 user_manager_(user_manager
),
130 device_local_account_policy_service_(device_local_account_policy_service
),
133 weak_factory_(this) {
134 notification_registrar_
.Add(
136 chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED
,
137 content::NotificationService::AllSources());
139 if (device_local_account_policy_service_
)
140 device_local_account_policy_service_
->AddObserver(this);
142 device_local_accounts_subscription_
= cros_settings_
->AddSettingsObserver(
143 chromeos::kAccountsPrefDeviceLocalAccounts
,
144 base::Bind(&CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts
,
145 base::Unretained(this)));
148 CloudExternalDataPolicyObserver::~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();
156 device_local_account_entries_
.clear();
159 void CloudExternalDataPolicyObserver::Init() {
160 RetrieveDeviceLocalAccounts();
163 void CloudExternalDataPolicyObserver::Observe(
165 const content::NotificationSource
& source
,
166 const content::NotificationDetails
& details
) {
167 if (type
!= chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED
) {
171 Profile
* profile
= content::Details
<Profile
>(details
).ptr();
173 const chromeos::User
* user
= user_manager_
->GetUserByProfile(profile
);
179 const std::string
& user_id
= user
->email();
180 if (ContainsKey(logged_in_user_observers_
, user_id
)) {
185 ProfilePolicyConnector
* policy_connector
=
186 ProfilePolicyConnectorFactory::GetForProfile(profile
);
187 logged_in_user_observers_
[user_id
] = make_linked_ptr(
188 new PolicyServiceObserver(this,
190 policy_connector
->policy_service()));
193 void 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.
202 if (!device_local_account_policy_service_
) {
206 DeviceLocalAccountPolicyBroker
* broker
=
207 device_local_account_policy_service_
->GetBrokerForUser(user_id
);
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.
218 const PolicyMap::Entry
* entry
=
219 broker
->core()->store()->policy_map().Get(policy_
);
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
);
231 PolicyMap::Entry
& map_entry
= device_local_account_entries_
[user_id
];
232 if (map_entry
.Equals(*entry
))
235 map_entry
.DeleteOwnedMembers();
236 map_entry
= *entry
->DeepCopy();
237 HandleExternalDataPolicyUpdate(user_id
, entry
);
240 void CloudExternalDataPolicyObserver::OnDeviceLocalAccountsChanged() {
241 // No action needed here, changes to the list of device-local accounts get
242 // handled via the kAccountsPrefDeviceLocalAccounts device setting observer.
245 void 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()))) {
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
);
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
273 HandleExternalDataPolicyUpdate(user_id
, NULL
);
279 for (std::set
<std::string
>::const_iterator it
= device_local_accounts
.begin();
280 it
!= device_local_accounts
.end(); ++it
) {
281 OnPolicyUpdated(*it
);
285 void CloudExternalDataPolicyObserver::HandleExternalDataPolicyUpdate(
286 const std::string
& user_id
,
287 const PolicyMap::Entry
* entry
) {
289 delegate_
->OnExternalDataCleared(policy_
, user_id
);
290 fetch_weak_ptrs_
.erase(user_id
);
294 delegate_
->OnExternalDataSet(policy_
, user_id
);
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(),
308 void 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());
317 } // namespace policy