Home | History | Annotate | Download | only in policy
      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/policy/device_local_account.h"
     17 #include "chrome/browser/chromeos/profiles/profile_helper.h"
     18 #include "chrome/browser/policy/profile_policy_connector.h"
     19 #include "chrome/browser/policy/profile_policy_connector_factory.h"
     20 #include "chrome/browser/profiles/profile.h"
     21 #include "chromeos/settings/cros_settings_names.h"
     22 #include "chromeos/settings/cros_settings_provider.h"
     23 #include "components/policy/core/common/cloud/cloud_policy_core.h"
     24 #include "components/policy/core/common/cloud/cloud_policy_store.h"
     25 #include "components/policy/core/common/external_data_fetcher.h"
     26 #include "components/policy/core/common/policy_namespace.h"
     27 #include "components/policy/core/common/policy_service.h"
     28 #include "components/user_manager/user.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 
     33 namespace 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.
     37 class 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 
     58 CloudExternalDataPolicyObserver::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 
     80 CloudExternalDataPolicyObserver::PolicyServiceObserver::
     81     ~PolicyServiceObserver() {
     82   policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this);
     83 }
     84 
     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()));
     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 
    103 void CloudExternalDataPolicyObserver::Delegate::OnExternalDataSet(
    104     const std::string& policy,
    105     const std::string& user_id) {
    106 }
    107 
    108 void CloudExternalDataPolicyObserver::Delegate::OnExternalDataCleared(
    109     const std::string& policy,
    110     const std::string& user_id) {
    111 }
    112 
    113 void CloudExternalDataPolicyObserver::Delegate::OnExternalDataFetched(
    114     const std::string& policy,
    115     const std::string& user_id,
    116     scoped_ptr<std::string> data) {
    117 }
    118 
    119 CloudExternalDataPolicyObserver::Delegate::~Delegate() {
    120 }
    121 
    122 CloudExternalDataPolicyObserver::CloudExternalDataPolicyObserver(
    123     chromeos::CrosSettings* cros_settings,
    124     DeviceLocalAccountPolicyService* device_local_account_policy_service,
    125     const std::string& policy,
    126     Delegate* delegate)
    127     : cros_settings_(cros_settings),
    128       device_local_account_policy_service_(device_local_account_policy_service),
    129       policy_(policy),
    130       delegate_(delegate),
    131       weak_factory_(this) {
    132   notification_registrar_.Add(
    133       this,
    134       chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
    135       content::NotificationService::AllSources());
    136 
    137   if (device_local_account_policy_service_)
    138     device_local_account_policy_service_->AddObserver(this);
    139 
    140   device_local_accounts_subscription_ = cros_settings_->AddSettingsObserver(
    141       chromeos::kAccountsPrefDeviceLocalAccounts,
    142       base::Bind(&CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts,
    143                  base::Unretained(this)));
    144 }
    145 
    146 CloudExternalDataPolicyObserver::~CloudExternalDataPolicyObserver() {
    147   if (device_local_account_policy_service_)
    148     device_local_account_policy_service_->RemoveObserver(this);
    149   for (DeviceLocalAccountEntryMap::iterator it =
    150            device_local_account_entries_.begin();
    151        it != device_local_account_entries_.end(); ++it) {
    152     it->second.DeleteOwnedMembers();
    153   }
    154   device_local_account_entries_.clear();
    155 }
    156 
    157 void CloudExternalDataPolicyObserver::Init() {
    158   RetrieveDeviceLocalAccounts();
    159 }
    160 
    161 void CloudExternalDataPolicyObserver::Observe(
    162     int type,
    163     const content::NotificationSource& source,
    164     const content::NotificationDetails& details) {
    165   if (type != chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED) {
    166     NOTREACHED();
    167     return;
    168   }
    169   Profile* profile = content::Details<Profile>(details).ptr();
    170 
    171   const user_manager::User* user =
    172       chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
    173   if (!user) {
    174     NOTREACHED();
    175     return;
    176   }
    177 
    178   const std::string& user_id = user->email();
    179   if (ContainsKey(logged_in_user_observers_, user_id)) {
    180     NOTREACHED();
    181     return;
    182   }
    183 
    184   ProfilePolicyConnector* policy_connector =
    185       ProfilePolicyConnectorFactory::GetForProfile(profile);
    186   logged_in_user_observers_[user_id] = make_linked_ptr(
    187       new PolicyServiceObserver(this,
    188                                 user_id,
    189                                 policy_connector->policy_service()));
    190 }
    191 
    192 void CloudExternalDataPolicyObserver::OnPolicyUpdated(
    193     const std::string& user_id) {
    194   if (ContainsKey(logged_in_user_observers_, user_id)) {
    195     // When a device-local account is logged in, a policy change triggers both
    196     // OnPolicyUpdated() and PolicyServiceObserver::OnPolicyUpdated(). Ignore
    197     // the former so that the policy change is handled only once.
    198     return;
    199   }
    200 
    201   if (!device_local_account_policy_service_) {
    202     NOTREACHED();
    203     return;
    204   }
    205   DeviceLocalAccountPolicyBroker* broker =
    206       device_local_account_policy_service_->GetBrokerForUser(user_id);
    207   if (!broker) {
    208     // The order in which |this| and the |device_local_account_policy_service_|
    209     // find out that a new device-local account has been added is undefined. If
    210     // no |broker| exists yet, the |device_local_account_policy_service_| must
    211     // not have seen the new |user_id| yet. OnPolicyUpdated() will be invoked
    212     // again by the |device_local_account_policy_service_| in this case when it
    213     // finds out about |user_id| and creates a |broker| for it.
    214     return;
    215   }
    216 
    217   const PolicyMap::Entry* entry =
    218       broker->core()->store()->policy_map().Get(policy_);
    219   if (!entry) {
    220     DeviceLocalAccountEntryMap::iterator it =
    221         device_local_account_entries_.find(user_id);
    222     if (it != device_local_account_entries_.end()) {
    223       it->second.DeleteOwnedMembers();
    224       device_local_account_entries_.erase(it);
    225       HandleExternalDataPolicyUpdate(user_id, NULL);
    226     }
    227     return;
    228   }
    229 
    230   PolicyMap::Entry& map_entry = device_local_account_entries_[user_id];
    231   if (map_entry.Equals(*entry))
    232     return;
    233 
    234   map_entry.DeleteOwnedMembers();
    235   map_entry = *entry->DeepCopy();
    236   HandleExternalDataPolicyUpdate(user_id, entry);
    237 }
    238 
    239 void CloudExternalDataPolicyObserver::OnDeviceLocalAccountsChanged() {
    240   // No action needed here, changes to the list of device-local accounts get
    241   // handled via the kAccountsPrefDeviceLocalAccounts device setting observer.
    242 }
    243 
    244 void CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts() {
    245   // Schedule a callback if device policy has not yet been verified.
    246   if (chromeos::CrosSettingsProvider::TRUSTED !=
    247       cros_settings_->PrepareTrustedValues(base::Bind(
    248           &CloudExternalDataPolicyObserver::RetrieveDeviceLocalAccounts,
    249           weak_factory_.GetWeakPtr()))) {
    250     return;
    251   }
    252 
    253   std::vector<DeviceLocalAccount> device_local_account_list =
    254       policy::GetDeviceLocalAccounts(cros_settings_);
    255   std::set<std::string> device_local_accounts;
    256   for (std::vector<DeviceLocalAccount>::const_iterator it =
    257            device_local_account_list.begin();
    258        it != device_local_account_list.end(); ++it) {
    259     device_local_accounts.insert(it->user_id);
    260   }
    261 
    262   for (DeviceLocalAccountEntryMap::iterator it =
    263            device_local_account_entries_.begin();
    264        it != device_local_account_entries_.end(); ) {
    265     if (!ContainsKey(device_local_accounts, it->first)) {
    266       const std::string user_id = it->first;
    267       it->second.DeleteOwnedMembers();
    268       device_local_account_entries_.erase(it++);
    269       // When a device-local account whose external data reference was set is
    270       // removed, emit a notification that the external data reference has been
    271       // cleared.
    272       HandleExternalDataPolicyUpdate(user_id, NULL);
    273     } else {
    274       ++it;
    275     }
    276   }
    277 
    278   for (std::set<std::string>::const_iterator it = device_local_accounts.begin();
    279        it != device_local_accounts.end(); ++it) {
    280     OnPolicyUpdated(*it);
    281   }
    282 }
    283 
    284 void CloudExternalDataPolicyObserver::HandleExternalDataPolicyUpdate(
    285     const std::string& user_id,
    286     const PolicyMap::Entry* entry) {
    287   if (!entry) {
    288     delegate_->OnExternalDataCleared(policy_, user_id);
    289     fetch_weak_ptrs_.erase(user_id);
    290     return;
    291   }
    292 
    293   delegate_->OnExternalDataSet(policy_, user_id);
    294 
    295   linked_ptr<WeakPtrFactory>& weak_ptr_factory = fetch_weak_ptrs_[user_id];
    296   weak_ptr_factory.reset(new WeakPtrFactory(this));
    297   if (entry->external_data_fetcher) {
    298     entry->external_data_fetcher->Fetch(base::Bind(
    299         &CloudExternalDataPolicyObserver::OnExternalDataFetched,
    300         weak_ptr_factory->GetWeakPtr(),
    301         user_id));
    302   } else {
    303     NOTREACHED();
    304   }
    305 }
    306 
    307 void CloudExternalDataPolicyObserver::OnExternalDataFetched(
    308     const std::string& user_id,
    309     scoped_ptr<std::string> data) {
    310   FetchWeakPtrMap::iterator it = fetch_weak_ptrs_.find(user_id);
    311   DCHECK(it != fetch_weak_ptrs_.end());
    312   fetch_weak_ptrs_.erase(it);
    313   delegate_->OnExternalDataFetched(policy_, user_id, data.Pass());
    314 }
    315 
    316 }  // namespace policy
    317