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/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 
     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     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 
    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();
    155   }
    156   device_local_account_entries_.clear();
    157 }
    158 
    159 void CloudExternalDataPolicyObserver::Init() {
    160   RetrieveDeviceLocalAccounts();
    161 }
    162 
    163 void 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 
    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.
    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 
    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.
    243 }
    244 
    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()))) {
    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 
    285 void 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 
    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());
    315 }
    316 
    317 }  // namespace policy
    318