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