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/common/policy/policy_schema.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/logging.h"
      9 #include "base/stl_util.h"
     10 #include "chrome/common/json_schema/json_schema_constants.h"
     11 #include "chrome/common/json_schema/json_schema_validator.h"
     12 
     13 namespace policy {
     14 
     15 namespace {
     16 
     17 const char kJSONSchemaVersion[] = "http://json-schema.org/draft-03/schema#";
     18 
     19 // Describes the properties of a TYPE_DICTIONARY policy schema.
     20 class DictionaryPolicySchema : public PolicySchema {
     21  public:
     22   static scoped_ptr<PolicySchema> Parse(const base::DictionaryValue& schema,
     23                                         std::string* error);
     24 
     25   virtual ~DictionaryPolicySchema();
     26 
     27   virtual const PolicySchemaMap* GetProperties() const OVERRIDE;
     28   virtual const PolicySchema* GetSchemaForAdditionalProperties() const OVERRIDE;
     29 
     30  private:
     31   DictionaryPolicySchema();
     32 
     33   PolicySchemaMap properties_;
     34   scoped_ptr<PolicySchema> additional_properties_;
     35 
     36   DISALLOW_COPY_AND_ASSIGN(DictionaryPolicySchema);
     37 };
     38 
     39 // Describes the items of a TYPE_LIST policy schema.
     40 class ListPolicySchema : public PolicySchema {
     41  public:
     42   static scoped_ptr<PolicySchema> Parse(const base::DictionaryValue& schema,
     43                                         std::string* error);
     44 
     45   virtual ~ListPolicySchema();
     46 
     47   virtual const PolicySchema* GetSchemaForItems() const OVERRIDE;
     48 
     49  private:
     50   ListPolicySchema();
     51 
     52   scoped_ptr<PolicySchema> items_schema_;
     53 
     54   DISALLOW_COPY_AND_ASSIGN(ListPolicySchema);
     55 };
     56 
     57 bool SchemaTypeToValueType(const std::string& type_string,
     58                            base::Value::Type* type) {
     59   // Note: "any" is not an accepted type.
     60   static const struct {
     61     const char* schema_type;
     62     base::Value::Type value_type;
     63   } kSchemaToValueTypeMap[] = {
     64     { json_schema_constants::kArray,        base::Value::TYPE_LIST       },
     65     { json_schema_constants::kBoolean,      base::Value::TYPE_BOOLEAN    },
     66     { json_schema_constants::kInteger,      base::Value::TYPE_INTEGER    },
     67     { json_schema_constants::kNull,         base::Value::TYPE_NULL       },
     68     { json_schema_constants::kNumber,       base::Value::TYPE_DOUBLE     },
     69     { json_schema_constants::kObject,       base::Value::TYPE_DICTIONARY },
     70     { json_schema_constants::kString,       base::Value::TYPE_STRING     },
     71   };
     72   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) {
     73     if (kSchemaToValueTypeMap[i].schema_type == type_string) {
     74       *type = kSchemaToValueTypeMap[i].value_type;
     75       return true;
     76     }
     77   }
     78   return false;
     79 }
     80 
     81 scoped_ptr<PolicySchema> ParseSchema(const base::DictionaryValue& schema,
     82                                      std::string* error) {
     83   std::string type_string;
     84   if (!schema.GetString(json_schema_constants::kType, &type_string)) {
     85     *error = "The schema type must be declared.";
     86     return scoped_ptr<PolicySchema>();
     87   }
     88 
     89   base::Value::Type type = base::Value::TYPE_NULL;
     90   if (!SchemaTypeToValueType(type_string, &type)) {
     91     *error = "The \"any\" type can't be used.";
     92     return scoped_ptr<PolicySchema>();
     93   }
     94 
     95   switch (type) {
     96     case base::Value::TYPE_DICTIONARY:
     97       return DictionaryPolicySchema::Parse(schema, error);
     98     case base::Value::TYPE_LIST:
     99       return ListPolicySchema::Parse(schema, error);
    100     default:
    101       return make_scoped_ptr(new PolicySchema(type));
    102   }
    103 }
    104 
    105 DictionaryPolicySchema::DictionaryPolicySchema()
    106     : PolicySchema(base::Value::TYPE_DICTIONARY) {}
    107 
    108 DictionaryPolicySchema::~DictionaryPolicySchema() {
    109   STLDeleteValues(&properties_);
    110 }
    111 
    112 const PolicySchemaMap* DictionaryPolicySchema::GetProperties() const {
    113   return &properties_;
    114 }
    115 
    116 const PolicySchema*
    117     DictionaryPolicySchema::GetSchemaForAdditionalProperties() const {
    118   return additional_properties_.get();
    119 }
    120 
    121 // static
    122 scoped_ptr<PolicySchema> DictionaryPolicySchema::Parse(
    123     const base::DictionaryValue& schema,
    124     std::string* error) {
    125   scoped_ptr<DictionaryPolicySchema> dict_schema(new DictionaryPolicySchema());
    126 
    127   const base::DictionaryValue* dict = NULL;
    128   const base::DictionaryValue* properties = NULL;
    129   if (schema.GetDictionary(json_schema_constants::kProperties, &properties)) {
    130     for (base::DictionaryValue::Iterator it(*properties);
    131          !it.IsAtEnd(); it.Advance()) {
    132       // This should have been verified by the JSONSchemaValidator.
    133       CHECK(it.value().GetAsDictionary(&dict));
    134       scoped_ptr<PolicySchema> sub_schema = ParseSchema(*dict, error);
    135       if (!sub_schema)
    136         return scoped_ptr<PolicySchema>();
    137       dict_schema->properties_[it.key()] = sub_schema.release();
    138     }
    139   }
    140 
    141   if (schema.GetDictionary(json_schema_constants::kAdditionalProperties,
    142                            &dict)) {
    143     scoped_ptr<PolicySchema> sub_schema = ParseSchema(*dict, error);
    144     if (!sub_schema)
    145       return scoped_ptr<PolicySchema>();
    146     dict_schema->additional_properties_ = sub_schema.Pass();
    147   }
    148 
    149   return dict_schema.PassAs<PolicySchema>();
    150 }
    151 
    152 ListPolicySchema::ListPolicySchema()
    153     : PolicySchema(base::Value::TYPE_LIST) {}
    154 
    155 ListPolicySchema::~ListPolicySchema() {}
    156 
    157 const PolicySchema* ListPolicySchema::GetSchemaForItems() const {
    158   return items_schema_.get();
    159 }
    160 
    161 scoped_ptr<PolicySchema> ListPolicySchema::Parse(
    162     const base::DictionaryValue& schema,
    163     std::string* error) {
    164   const base::DictionaryValue* dict = NULL;
    165   if (!schema.GetDictionary(json_schema_constants::kItems, &dict)) {
    166     *error = "Arrays must declare a single schema for their items.";
    167     return scoped_ptr<PolicySchema>();
    168   }
    169   scoped_ptr<PolicySchema> items_schema = ParseSchema(*dict, error);
    170   if (!items_schema)
    171     return scoped_ptr<PolicySchema>();
    172 
    173   scoped_ptr<ListPolicySchema> list_schema(new ListPolicySchema());
    174   list_schema->items_schema_ = items_schema.Pass();
    175   return list_schema.PassAs<PolicySchema>();
    176 }
    177 
    178 }  // namespace
    179 
    180 PolicySchema::PolicySchema(base::Value::Type type)
    181     : type_(type) {}
    182 
    183 PolicySchema::~PolicySchema() {}
    184 
    185 const PolicySchemaMap* PolicySchema::GetProperties() const {
    186   NOTREACHED();
    187   return NULL;
    188 }
    189 
    190 const PolicySchema* PolicySchema::GetSchemaForAdditionalProperties() const {
    191   NOTREACHED();
    192   return NULL;
    193 }
    194 
    195 const PolicySchema* PolicySchema::GetSchemaForProperty(
    196     const std::string& key) const {
    197   const PolicySchemaMap* properties = GetProperties();
    198   PolicySchemaMap::const_iterator it = properties->find(key);
    199   return it == properties->end() ? GetSchemaForAdditionalProperties()
    200                                  : it->second;
    201 }
    202 
    203 const PolicySchema* PolicySchema::GetSchemaForItems() const {
    204   NOTREACHED();
    205   return NULL;
    206 }
    207 
    208 // static
    209 scoped_ptr<PolicySchema> PolicySchema::Parse(const std::string& content,
    210                                              std::string* error) {
    211   // Validate as a generic JSON schema.
    212   scoped_ptr<base::DictionaryValue> dict =
    213       JSONSchemaValidator::IsValidSchema(content, error);
    214   if (!dict)
    215     return scoped_ptr<PolicySchema>();
    216 
    217   // Validate the schema version.
    218   std::string string_value;
    219   if (!dict->GetString(json_schema_constants::kSchema, &string_value) ||
    220       string_value != kJSONSchemaVersion) {
    221     *error = "Must declare JSON Schema v3 version in \"$schema\".";
    222     return scoped_ptr<PolicySchema>();
    223   }
    224 
    225   // Validate the main type.
    226   if (!dict->GetString(json_schema_constants::kType, &string_value) ||
    227       string_value != json_schema_constants::kObject) {
    228     *error =
    229         "The main schema must have a type attribute with \"object\" value.";
    230     return scoped_ptr<PolicySchema>();
    231   }
    232 
    233   // Checks for invalid attributes at the top-level.
    234   if (dict->HasKey(json_schema_constants::kAdditionalProperties) ||
    235       dict->HasKey(json_schema_constants::kPatternProperties)) {
    236     *error = "\"additionalProperties\" and \"patternProperties\" are not "
    237              "supported at the main schema.";
    238     return scoped_ptr<PolicySchema>();
    239   }
    240 
    241   return ParseSchema(*dict, error);
    242 }
    243 
    244 }  // namespace policy
    245