blob: 8c3145ce43afdd1e79d2c34e5c7dd8ac7e9e5caa [file] [log] [blame]
// 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 "components/policy/core/common/policy_schema.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "components/json_schema/json_schema_constants.h"
#include "components/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<PolicySchema> 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<PolicySchema> additional_properties_;
DISALLOW_COPY_AND_ASSIGN(DictionaryPolicySchema);
};
// Describes the items of a TYPE_LIST policy schema.
class ListPolicySchema : public PolicySchema {
public:
static scoped_ptr<PolicySchema> Parse(const base::DictionaryValue& schema,
std::string* error);
virtual ~ListPolicySchema();
virtual const PolicySchema* GetSchemaForItems() const OVERRIDE;
private:
ListPolicySchema();
scoped_ptr<PolicySchema> 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<PolicySchema> 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<PolicySchema>();
}
base::Value::Type type = base::Value::TYPE_NULL;
if (!SchemaTypeToValueType(type_string, &type)) {
*error = "The \"any\" type can't be used.";
return scoped_ptr<PolicySchema>();
}
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<PolicySchema> DictionaryPolicySchema::Parse(
const base::DictionaryValue& schema,
std::string* error) {
scoped_ptr<DictionaryPolicySchema> 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<PolicySchema> sub_schema = ParseSchema(*dict, error);
if (!sub_schema)
return scoped_ptr<PolicySchema>();
dict_schema->properties_[it.key()] = sub_schema.release();
}
}
if (schema.GetDictionary(json_schema_constants::kAdditionalProperties,
&dict)) {
scoped_ptr<PolicySchema> sub_schema = ParseSchema(*dict, error);
if (!sub_schema)
return scoped_ptr<PolicySchema>();
dict_schema->additional_properties_ = sub_schema.Pass();
}
return dict_schema.PassAs<PolicySchema>();
}
ListPolicySchema::ListPolicySchema()
: PolicySchema(base::Value::TYPE_LIST) {}
ListPolicySchema::~ListPolicySchema() {}
const PolicySchema* ListPolicySchema::GetSchemaForItems() const {
return items_schema_.get();
}
scoped_ptr<PolicySchema> 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<PolicySchema>();
}
scoped_ptr<PolicySchema> items_schema = ParseSchema(*dict, error);
if (!items_schema)
return scoped_ptr<PolicySchema>();
scoped_ptr<ListPolicySchema> list_schema(new ListPolicySchema());
list_schema->items_schema_ = items_schema.Pass();
return list_schema.PassAs<PolicySchema>();
}
} // 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> PolicySchema::Parse(const std::string& content,
std::string* error) {
// Validate as a generic JSON schema.
scoped_ptr<base::DictionaryValue> dict =
JSONSchemaValidator::IsValidSchema(content, error);
if (!dict)
return scoped_ptr<PolicySchema>();
// 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<PolicySchema>();
}
// 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<PolicySchema>();
}
// 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<PolicySchema>();
}
return ParseSchema(*dict, error);
}
} // namespace policy