Home | History | Annotate | Download | only in supervised_user
      1 // Copyright 2014 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/supervised_user/supervised_user_site_list.h"
      6 
      7 #include "base/json/json_file_value_serializer.h"
      8 #include "base/logging.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "base/values.h"
     11 #include "extensions/common/extension.h"
     12 
     13 using base::DictionaryValue;
     14 using base::ListValue;
     15 using base::Value;
     16 
     17 const int kSitelistFormatVersion = 1;
     18 
     19 const char kCategoriesKey[] = "categories";
     20 const char kHostnameHashesKey[] = "hostname_hashes";
     21 const char kNameKey[] = "name";
     22 const char kSitesKey[] = "sites";
     23 const char kSitelistFormatVersionKey[] = "version";
     24 const char kThumbnailKey[] = "thumbnail";
     25 const char kThumbnailUrlKey[] = "thumbnail_url";
     26 const char kUrlKey[] = "url";
     27 const char kWhitelistKey[] = "whitelist";
     28 
     29 namespace {
     30 
     31 struct CategoryInfo {
     32   const char* identifier;
     33   const char* name;
     34 };
     35 
     36 // These are placeholders for now.
     37 CategoryInfo g_categories[] = {
     38   {"com.google.chrome.animals", "Animals and Plants"},
     39   {"com.google.chrome.arts", "Arts"},
     40   {"com.google.chrome.business", "Business"},
     41   {"com.google.chrome.computers", "Computers"},
     42   {"com.google.chrome.education", "Education"},
     43   {"com.google.chrome.entertainment", "Entertainment"},
     44   {"com.google.chrome.games", "Games"},
     45   {"com.google.chrome.health", "Health"},
     46   {"com.google.chrome.home", "Home"},
     47   {"com.google.chrome.international", "International"},
     48   {"com.google.chrome.news", "News"},
     49   {"com.google.chrome.people", "People and Society"},
     50   {"com.google.chrome.places", "Places"},
     51   {"com.google.chrome.pre-school", "Pre-School"},
     52   {"com.google.chrome.reference", "Reference"},
     53   {"com.google.chrome.science", "Science"},
     54   {"com.google.chrome.shopping", "Shopping"},
     55   {"com.google.chrome.sports", "Sports and Hobbies"},
     56   {"com.google.chrome.teens", "Teens"}
     57 };
     58 
     59 // Category 0 is "not listed"; actual category IDs start at 1.
     60 int GetCategoryId(const std::string& category) {
     61   for (size_t i = 0; i < arraysize(g_categories); ++i) {
     62     if (g_categories[i].identifier == category)
     63       return i + 1;
     64   }
     65   return 0;
     66 }
     67 
     68 // Takes a DictionaryValue entry from the JSON file and fills the whitelist
     69 // (via URL patterns or hostname hashes) and the URL in the corresponding Site
     70 // struct.
     71 void AddWhitelistEntries(const base::DictionaryValue* site_dict,
     72                          SupervisedUserSiteList::Site* site) {
     73   std::vector<std::string>* patterns = &site->patterns;
     74 
     75   bool found = false;
     76   const base::ListValue* whitelist = NULL;
     77   if (site_dict->GetList(kWhitelistKey, &whitelist)) {
     78     found = true;
     79     for (base::ListValue::const_iterator whitelist_it = whitelist->begin();
     80          whitelist_it != whitelist->end(); ++whitelist_it) {
     81       std::string pattern;
     82       if (!(*whitelist_it)->GetAsString(&pattern)) {
     83         LOG(ERROR) << "Invalid whitelist entry";
     84         continue;
     85       }
     86 
     87       patterns->push_back(pattern);
     88     }
     89   }
     90 
     91   std::vector<std::string>* hashes = &site->hostname_hashes;
     92   const base::ListValue* hash_list = NULL;
     93   if (site_dict->GetList(kHostnameHashesKey, &hash_list)) {
     94     found = true;
     95     for (base::ListValue::const_iterator hash_list_it = hash_list->begin();
     96          hash_list_it != hash_list->end(); ++hash_list_it) {
     97       std::string hash;
     98       if (!(*hash_list_it)->GetAsString(&hash)) {
     99         LOG(ERROR) << "Invalid whitelist entry";
    100         continue;
    101       }
    102 
    103       hashes->push_back(hash);
    104     }
    105   }
    106 
    107   if (found)
    108     return;
    109 
    110   // Fall back to using a whitelist based on the URL.
    111   std::string url_str;
    112   if (!site_dict->GetString(kUrlKey, &url_str)) {
    113     LOG(ERROR) << "Whitelist is invalid";
    114     return;
    115   }
    116 
    117   GURL url(url_str);
    118   if (!url.is_valid()) {
    119     LOG(ERROR) << "URL " << url_str << " is invalid";
    120     return;
    121   }
    122 
    123   patterns->push_back(url.host());
    124 }
    125 
    126 }  // namespace
    127 
    128 SupervisedUserSiteList::Site::Site(const base::string16& name,
    129                                    int category_id)
    130     : name(name),
    131       category_id(category_id) {}
    132 
    133 SupervisedUserSiteList::Site::~Site() {}
    134 
    135 SupervisedUserSiteList::SupervisedUserSiteList(
    136     const std::string& extension_id,
    137     const base::FilePath& path)
    138     : extension_id_(extension_id),
    139       path_(path) {
    140 }
    141 
    142 SupervisedUserSiteList::~SupervisedUserSiteList() {
    143 }
    144 
    145 SupervisedUserSiteList* SupervisedUserSiteList::Clone() {
    146   return new SupervisedUserSiteList(extension_id_, path_);
    147 }
    148 
    149 // static
    150 void SupervisedUserSiteList::GetCategoryNames(
    151     std::vector<base::string16>* categories) {
    152   // TODO(bauerb): Collect custom categories from extensions.
    153   for (size_t i = 0; i < arraysize(g_categories); ++i) {
    154     categories->push_back(base::ASCIIToUTF16(g_categories[i].name));
    155   }
    156 }
    157 
    158 void SupervisedUserSiteList::GetSites(std::vector<Site>* sites) {
    159   if (!LazyLoad())
    160     return;
    161 
    162   for (base::ListValue::iterator entry_it = sites_->begin();
    163        entry_it != sites_->end(); ++entry_it) {
    164     base::DictionaryValue* entry = NULL;
    165     if (!(*entry_it)->GetAsDictionary(&entry)) {
    166       LOG(ERROR) << "Entry is invalid";
    167       continue;
    168     }
    169 
    170     base::string16 name;
    171     entry->GetString(kNameKey, &name);
    172 
    173     // TODO(bauerb): We need to distinguish between "no category assigned" and
    174     // "not on any site list".
    175     int category_id = 0;
    176     const base::ListValue* categories = NULL;
    177     if (entry->GetList(kCategoriesKey, &categories)) {
    178       for (base::ListValue::const_iterator it = categories->begin();
    179            it != categories->end(); ++it) {
    180         std::string category;
    181         if (!(*it)->GetAsString(&category)) {
    182           LOG(ERROR) << "Invalid category";
    183           continue;
    184         }
    185         category_id = GetCategoryId(category);
    186         break;
    187       }
    188     }
    189     sites->push_back(Site(name, category_id));
    190     AddWhitelistEntries(entry, &sites->back());
    191   }
    192 }
    193 
    194 bool SupervisedUserSiteList::LazyLoad() {
    195   if (sites_.get())
    196     return true;
    197 
    198   JSONFileValueSerializer serializer(path_);
    199   std::string error;
    200   scoped_ptr<base::Value> value(serializer.Deserialize(NULL, &error));
    201   if (!value.get()) {
    202     LOG(ERROR) << "Couldn't load site list " << path_.value() << ": "
    203                << error;
    204     return false;
    205   }
    206 
    207   base::DictionaryValue* dict = NULL;
    208   if (!value->GetAsDictionary(&dict)) {
    209     LOG(ERROR) << "Site list " << path_.value() << " is invalid";
    210     return false;
    211   }
    212 
    213   int version = 0;
    214   if (!dict->GetInteger(kSitelistFormatVersionKey, &version)) {
    215     LOG(ERROR) << "Site list " << path_.value() << " has invalid version";
    216     return false;
    217   }
    218 
    219   if (version > kSitelistFormatVersion) {
    220     LOG(ERROR) << "Site list " << path_.value() << " has a too new format";
    221     return false;
    222   }
    223 
    224   base::ListValue* sites = NULL;
    225   if (dict->GetList(kSitesKey, &sites))
    226     sites_.reset(sites->DeepCopy());
    227 
    228   base::DictionaryValue* categories = NULL;
    229   if (dict->GetDictionary(kCategoriesKey, &categories))
    230     categories_.reset(categories->DeepCopy());
    231 
    232   return true;
    233 }
    234 
    235 void SupervisedUserSiteList::CopyThumbnailUrl(
    236     const base::DictionaryValue* source,
    237     base::DictionaryValue* dest) {
    238   if (!source->HasKey(kThumbnailKey))
    239     return;
    240 
    241   std::string thumbnail;
    242   if (!source->GetString(kThumbnailKey, &thumbnail)) {
    243     LOG(ERROR) << "Invalid thumbnail";
    244     return;
    245   }
    246 
    247   GURL base_url =
    248       extensions::Extension::GetBaseURLFromExtensionId(extension_id_);
    249   GURL thumbnail_url = base_url.Resolve(thumbnail);
    250   if (!thumbnail_url.is_valid()) {
    251     LOG(ERROR) << "Invalid thumbnail";
    252     return;
    253   }
    254 
    255   dest->SetString(kThumbnailUrlKey, thumbnail_url.spec());
    256 }
    257