// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/common/policy/policy_schema.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/stl_util.h" #include "chrome/common/json_schema/json_schema_constants.h" #include "chrome/common/json_schema/json_schema_validator.h" namespace policy { namespace { const char kJSONSchemaVersion[] = "http://json-schema.org/draft-03/schema#"; // Describes the properties of a TYPE_DICTIONARY policy schema. class DictionaryPolicySchema : public PolicySchema { public: static scoped_ptr Parse(const base::DictionaryValue& schema, std::string* error); virtual ~DictionaryPolicySchema(); virtual const PolicySchemaMap* GetProperties() const OVERRIDE; virtual const PolicySchema* GetSchemaForAdditionalProperties() const OVERRIDE; private: DictionaryPolicySchema(); PolicySchemaMap properties_; scoped_ptr additional_properties_; DISALLOW_COPY_AND_ASSIGN(DictionaryPolicySchema); }; // Describes the items of a TYPE_LIST policy schema. class ListPolicySchema : public PolicySchema { public: static scoped_ptr Parse(const base::DictionaryValue& schema, std::string* error); virtual ~ListPolicySchema(); virtual const PolicySchema* GetSchemaForItems() const OVERRIDE; private: ListPolicySchema(); scoped_ptr items_schema_; DISALLOW_COPY_AND_ASSIGN(ListPolicySchema); }; bool SchemaTypeToValueType(const std::string& type_string, base::Value::Type* type) { // Note: "any" is not an accepted type. static const struct { const char* schema_type; base::Value::Type value_type; } kSchemaToValueTypeMap[] = { { json_schema_constants::kArray, base::Value::TYPE_LIST }, { json_schema_constants::kBoolean, base::Value::TYPE_BOOLEAN }, { json_schema_constants::kInteger, base::Value::TYPE_INTEGER }, { json_schema_constants::kNull, base::Value::TYPE_NULL }, { json_schema_constants::kNumber, base::Value::TYPE_DOUBLE }, { json_schema_constants::kObject, base::Value::TYPE_DICTIONARY }, { json_schema_constants::kString, base::Value::TYPE_STRING }, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) { if (kSchemaToValueTypeMap[i].schema_type == type_string) { *type = kSchemaToValueTypeMap[i].value_type; return true; } } return false; } scoped_ptr ParseSchema(const base::DictionaryValue& schema, std::string* error) { std::string type_string; if (!schema.GetString(json_schema_constants::kType, &type_string)) { *error = "The schema type must be declared."; return scoped_ptr(); } base::Value::Type type = base::Value::TYPE_NULL; if (!SchemaTypeToValueType(type_string, &type)) { *error = "The \"any\" type can't be used."; return scoped_ptr(); } switch (type) { case base::Value::TYPE_DICTIONARY: return DictionaryPolicySchema::Parse(schema, error); case base::Value::TYPE_LIST: return ListPolicySchema::Parse(schema, error); default: return make_scoped_ptr(new PolicySchema(type)); } } DictionaryPolicySchema::DictionaryPolicySchema() : PolicySchema(base::Value::TYPE_DICTIONARY) {} DictionaryPolicySchema::~DictionaryPolicySchema() { STLDeleteValues(&properties_); } const PolicySchemaMap* DictionaryPolicySchema::GetProperties() const { return &properties_; } const PolicySchema* DictionaryPolicySchema::GetSchemaForAdditionalProperties() const { return additional_properties_.get(); } // static scoped_ptr DictionaryPolicySchema::Parse( const base::DictionaryValue& schema, std::string* error) { scoped_ptr dict_schema(new DictionaryPolicySchema()); const base::DictionaryValue* dict = NULL; const base::DictionaryValue* properties = NULL; if (schema.GetDictionary(json_schema_constants::kProperties, &properties)) { for (base::DictionaryValue::Iterator it(*properties); !it.IsAtEnd(); it.Advance()) { // This should have been verified by the JSONSchemaValidator. CHECK(it.value().GetAsDictionary(&dict)); scoped_ptr sub_schema = ParseSchema(*dict, error); if (!sub_schema) return scoped_ptr(); dict_schema->properties_[it.key()] = sub_schema.release(); } } if (schema.GetDictionary(json_schema_constants::kAdditionalProperties, &dict)) { scoped_ptr sub_schema = ParseSchema(*dict, error); if (!sub_schema) return scoped_ptr(); dict_schema->additional_properties_ = sub_schema.Pass(); } return dict_schema.PassAs(); } ListPolicySchema::ListPolicySchema() : PolicySchema(base::Value::TYPE_LIST) {} ListPolicySchema::~ListPolicySchema() {} const PolicySchema* ListPolicySchema::GetSchemaForItems() const { return items_schema_.get(); } scoped_ptr ListPolicySchema::Parse( const base::DictionaryValue& schema, std::string* error) { const base::DictionaryValue* dict = NULL; if (!schema.GetDictionary(json_schema_constants::kItems, &dict)) { *error = "Arrays must declare a single schema for their items."; return scoped_ptr(); } scoped_ptr items_schema = ParseSchema(*dict, error); if (!items_schema) return scoped_ptr(); scoped_ptr list_schema(new ListPolicySchema()); list_schema->items_schema_ = items_schema.Pass(); return list_schema.PassAs(); } } // namespace PolicySchema::PolicySchema(base::Value::Type type) : type_(type) {} PolicySchema::~PolicySchema() {} const PolicySchemaMap* PolicySchema::GetProperties() const { NOTREACHED(); return NULL; } const PolicySchema* PolicySchema::GetSchemaForAdditionalProperties() const { NOTREACHED(); return NULL; } const PolicySchema* PolicySchema::GetSchemaForProperty( const std::string& key) const { const PolicySchemaMap* properties = GetProperties(); PolicySchemaMap::const_iterator it = properties->find(key); return it == properties->end() ? GetSchemaForAdditionalProperties() : it->second; } const PolicySchema* PolicySchema::GetSchemaForItems() const { NOTREACHED(); return NULL; } // static scoped_ptr PolicySchema::Parse(const std::string& content, std::string* error) { // Validate as a generic JSON schema. scoped_ptr dict = JSONSchemaValidator::IsValidSchema(content, error); if (!dict) return scoped_ptr(); // Validate the schema version. std::string string_value; if (!dict->GetString(json_schema_constants::kSchema, &string_value) || string_value != kJSONSchemaVersion) { *error = "Must declare JSON Schema v3 version in \"$schema\"."; return scoped_ptr(); } // Validate the main type. if (!dict->GetString(json_schema_constants::kType, &string_value) || string_value != json_schema_constants::kObject) { *error = "The main schema must have a type attribute with \"object\" value."; return scoped_ptr(); } // Checks for invalid attributes at the top-level. if (dict->HasKey(json_schema_constants::kAdditionalProperties) || dict->HasKey(json_schema_constants::kPatternProperties)) { *error = "\"additionalProperties\" and \"patternProperties\" are not " "supported at the main schema."; return scoped_ptr(); } return ParseSchema(*dict, error); } } // namespace policy