Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/policy/core/common/schema.h" | 5 #include "components/policy/core/common/schema.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <climits> | 8 #include <climits> |
| 9 #include <map> | 9 #include <map> |
| 10 #include <utility> | 10 #include <utility> |
| 11 #include <vector> | 11 #include <vector> |
| 12 | 12 |
| 13 #include "base/compiler_specific.h" | 13 #include "base/compiler_specific.h" |
| 14 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/memory/scoped_vector.h" | 15 #include "base/memory/scoped_vector.h" |
| 16 #include "base/strings/stringprintf.h" | |
| 16 #include "components/json_schema/json_schema_constants.h" | 17 #include "components/json_schema/json_schema_constants.h" |
| 17 #include "components/json_schema/json_schema_validator.h" | 18 #include "components/json_schema/json_schema_validator.h" |
| 18 #include "components/policy/core/common/schema_internal.h" | 19 #include "components/policy/core/common/schema_internal.h" |
| 19 | 20 |
| 20 namespace schema = json_schema_constants; | 21 namespace schema = json_schema_constants; |
| 21 | 22 |
| 22 namespace policy { | 23 namespace policy { |
| 23 | 24 |
| 24 using internal::PropertiesNode; | 25 using internal::PropertiesNode; |
| 25 using internal::PropertyNode; | 26 using internal::PropertyNode; |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 SCHEMA_STRICT, // SCHEMA_STRICT | 98 SCHEMA_STRICT, // SCHEMA_STRICT |
| 98 SCHEMA_STRICT, // SCHEMA_ALLOW_UNKNOWN_TOPLEVEL | 99 SCHEMA_STRICT, // SCHEMA_ALLOW_UNKNOWN_TOPLEVEL |
| 99 SCHEMA_ALLOW_UNKNOWN, // SCHEMA_ALLOW_UNKNOWN | 100 SCHEMA_ALLOW_UNKNOWN, // SCHEMA_ALLOW_UNKNOWN |
| 100 SCHEMA_STRICT, // SCHEMA_ALLOW_INVALID_TOPLEVEL | 101 SCHEMA_STRICT, // SCHEMA_ALLOW_INVALID_TOPLEVEL |
| 101 SCHEMA_ALLOW_UNKNOWN, // SCHEMA_ALLOW_INVALID_TOPLEVEL_AND_ALLOW_UNKNOWN | 102 SCHEMA_ALLOW_UNKNOWN, // SCHEMA_ALLOW_INVALID_TOPLEVEL_AND_ALLOW_UNKNOWN |
| 102 SCHEMA_ALLOW_INVALID, // SCHEMA_ALLOW_INVALID | 103 SCHEMA_ALLOW_INVALID, // SCHEMA_ALLOW_INVALID |
| 103 }; | 104 }; |
| 104 return next_level_strategy[(int)strategy]; | 105 return next_level_strategy[(int)strategy]; |
| 105 } | 106 } |
| 106 | 107 |
| 108 void SchemaFindError(std::string* error_path,std::string *error, | |
|
Joao da Silva
2014/01/23 20:59:31
A better name for this is ErrorFound
binjin
2014/01/23 21:21:58
Done.
| |
| 109 const std::string& msg) { | |
|
Joao da Silva
2014/01/23 20:59:31
Format this. One argument per line:
void SchemaFi
binjin
2014/01/23 21:21:58
Done.
| |
| 110 if (error_path) | |
| 111 *error_path = ""; | |
| 112 *error = msg; | |
| 113 } | |
| 114 | |
| 115 void AddListIndexPrefixToPath(int index, std::string* path) { | |
| 116 if (path) { | |
| 117 if (path->size() == 0) | |
|
Joao da Silva
2014/01/23 20:59:31
path->empty
binjin
2014/01/23 21:21:58
Done.
| |
| 118 *path = base::StringPrintf("items[%d]", index); | |
| 119 else | |
| 120 *path = base::StringPrintf("items[%d].", index) + *path; | |
|
Joao da Silva
2014/01/23 20:59:31
Maybe just "[%d]" without the "items" looks OK?
E
binjin
2014/01/23 21:21:58
root schema can be a list, and things like "[4].fi
| |
| 121 } | |
| 122 } | |
| 123 | |
| 124 void AddDictKeyPrefixToPath(const std::string& key, std::string* path) { | |
| 125 if (path) { | |
| 126 if (path->size() == 0) | |
|
Joao da Silva
2014/01/23 20:59:31
path->empty
binjin
2014/01/23 21:21:58
Done.
| |
| 127 *path = key; | |
| 128 else | |
| 129 *path = key + "." + *path; | |
| 130 } | |
| 131 } | |
| 132 | |
| 107 } // namespace | 133 } // namespace |
| 108 | 134 |
| 109 // Contains the internal data representation of a Schema. This can either wrap | 135 // Contains the internal data representation of a Schema. This can either wrap |
| 110 // a SchemaData owned elsewhere (currently used to wrap the Chrome schema, which | 136 // a SchemaData owned elsewhere (currently used to wrap the Chrome schema, which |
| 111 // is generated at compile time), or it can own its own SchemaData. | 137 // is generated at compile time), or it can own its own SchemaData. |
| 112 class Schema::InternalStorage | 138 class Schema::InternalStorage |
| 113 : public base::RefCountedThreadSafe<InternalStorage> { | 139 : public base::RefCountedThreadSafe<InternalStorage> { |
| 114 public: | 140 public: |
| 115 static scoped_refptr<const InternalStorage> Wrap(const SchemaData* data); | 141 static scoped_refptr<const InternalStorage> Wrap(const SchemaData* data); |
| 116 | 142 |
| (...skipping 509 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 626 } | 652 } |
| 627 | 653 |
| 628 // static | 654 // static |
| 629 Schema Schema::Wrap(const SchemaData* data) { | 655 Schema Schema::Wrap(const SchemaData* data) { |
| 630 scoped_refptr<const InternalStorage> storage = InternalStorage::Wrap(data); | 656 scoped_refptr<const InternalStorage> storage = InternalStorage::Wrap(data); |
| 631 return Schema(storage, storage->root_node()); | 657 return Schema(storage, storage->root_node()); |
| 632 } | 658 } |
| 633 | 659 |
| 634 bool Schema::Validate(const base::Value& value, | 660 bool Schema::Validate(const base::Value& value, |
| 635 SchemaOnErrorStrategy strategy, | 661 SchemaOnErrorStrategy strategy, |
| 662 std::string* error_path, | |
| 636 std::string* error) const { | 663 std::string* error) const { |
| 637 if (!valid()) { | 664 if (!valid()) { |
| 638 *error = "The schema is invalid."; | 665 SchemaFindError(error_path, error, "The schema is invalid."); |
| 639 return false; | 666 return false; |
| 640 } | 667 } |
| 641 | 668 |
| 642 if (!value.IsType(type())) { | 669 if (!value.IsType(type())) { |
| 643 // Allow the integer to double promition. Note that range restriction on | 670 // Allow the integer to double promition. Note that range restriction on |
| 644 // double is not supported now. | 671 // double is not supported now. |
| 645 if (value.IsType(base::Value::TYPE_INTEGER) && | 672 if (value.IsType(base::Value::TYPE_INTEGER) && |
| 646 type() == base::Value::TYPE_DOUBLE) | 673 type() == base::Value::TYPE_DOUBLE) |
| 647 return true; | 674 return true; |
| 648 | 675 |
| 649 *error = "The value type doesn't match the schema type."; | 676 SchemaFindError(error_path, error, |
| 677 "The value type doesn't match the schema type."); | |
|
Joao da Silva
2014/01/23 20:59:31
Format this line.
binjin
2014/01/23 21:21:58
Done.
| |
| 650 return false; | 678 return false; |
| 651 } | 679 } |
| 652 | 680 |
| 653 const base::DictionaryValue* dict = NULL; | 681 const base::DictionaryValue* dict = NULL; |
| 654 const base::ListValue* list = NULL; | 682 const base::ListValue* list = NULL; |
| 655 int int_value; | 683 int int_value; |
| 656 std::string str_value; | 684 std::string str_value; |
| 657 if (value.GetAsDictionary(&dict)) { | 685 if (value.GetAsDictionary(&dict)) { |
| 658 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); | 686 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); |
| 659 it.Advance()) { | 687 it.Advance()) { |
| 660 Schema subschema = GetProperty(it.key()); | 688 Schema subschema = GetProperty(it.key()); |
| 661 if (!subschema.valid()) { | 689 if (!subschema.valid()) { |
| 662 // Unknown property was detected. | 690 // Unknown property was detected. |
| 663 if (!StrategyAllowUnknownOnTopLevel(strategy)) { | 691 SchemaFindError(error_path, error, "Unknown property: " + it.key()); |
| 664 *error = "Unknown property: " + it.key(); | 692 if (!StrategyAllowUnknownOnTopLevel(strategy)) |
| 665 return false; | 693 return false; |
| 666 } | 694 } else if (!subschema.Validate(it.value(), |
| 667 } else { | 695 StrategyForNextLevel(strategy), error_path, error)) { |
|
Joao da Silva
2014/01/23 20:59:31
Format this line
binjin
2014/01/23 21:21:58
Done.
| |
| 668 std::string sub_error; | 696 // Invalid property was detected. |
| 669 if (!subschema.Validate( | 697 AddDictKeyPrefixToPath(it.key(), error_path); |
| 670 it.value(), StrategyForNextLevel(strategy), &sub_error)) { | 698 if (!StrategyAllowInvalidOnTopLevel(strategy)) |
| 671 // Invalid property was detected. | 699 return false; |
| 672 if (!StrategyAllowInvalidOnTopLevel(strategy)) { | |
| 673 *error = sub_error; | |
| 674 return false; | |
| 675 } | |
| 676 } | |
| 677 } | 700 } |
| 678 } | 701 } |
| 679 } else if (value.GetAsList(&list)) { | 702 } else if (value.GetAsList(&list)) { |
| 680 for (base::ListValue::const_iterator it = list->begin(); it != list->end(); | 703 for (base::ListValue::const_iterator it = list->begin(); it != list->end(); |
| 681 ++it) { | 704 ++it) { |
| 682 if (!*it || | 705 if (!*it || |
| 683 !GetItems().Validate(**it, StrategyForNextLevel(strategy), error)) { | 706 !GetItems().Validate( |
| 707 **it, StrategyForNextLevel(strategy), error_path, error)) { | |
| 684 // Invalid list item was detected. | 708 // Invalid list item was detected. |
| 709 AddListIndexPrefixToPath(it - list->begin(), error_path); | |
| 685 if (!StrategyAllowInvalidOnTopLevel(strategy)) | 710 if (!StrategyAllowInvalidOnTopLevel(strategy)) |
| 686 return false; | 711 return false; |
| 687 } | 712 } |
| 688 } | 713 } |
| 689 } else if (value.GetAsInteger(&int_value)) { | 714 } else if (value.GetAsInteger(&int_value)) { |
| 690 if (node_->extra != kInvalid && | 715 if (node_->extra != kInvalid && |
| 691 !ValidateIntegerRestriction(node_->extra, int_value)) { | 716 !ValidateIntegerRestriction(node_->extra, int_value)) { |
| 692 *error = "Invalid value for integer"; | 717 SchemaFindError(error_path, error, "Invalid value for integer"); |
| 693 return false; | 718 return false; |
| 694 } | 719 } |
| 695 } else if (value.GetAsString(&str_value)) { | 720 } else if (value.GetAsString(&str_value)) { |
| 696 if (node_->extra != kInvalid && | 721 if (node_->extra != kInvalid && |
| 697 !ValidateStringRestriction(node_->extra, str_value.c_str())) { | 722 !ValidateStringRestriction(node_->extra, str_value.c_str())) { |
| 698 *error = "Invalid value for string"; | 723 SchemaFindError(error_path, error, "Invalid value for string"); |
| 699 return false; | 724 return false; |
| 700 } | 725 } |
| 701 } | 726 } |
| 702 | 727 |
| 703 return true; | 728 return true; |
| 704 } | 729 } |
| 705 | 730 |
| 706 bool Schema::Normalize(base::Value* value, | 731 bool Schema::Normalize(base::Value* value, |
| 707 SchemaOnErrorStrategy strategy, | 732 SchemaOnErrorStrategy strategy, |
| 733 std::string* error_path, | |
| 708 std::string* error) const { | 734 std::string* error) const { |
| 709 if (!valid()) { | 735 if (!valid()) { |
| 710 *error = "The schema is invalid."; | 736 SchemaFindError(error_path, error, "The schema is invalid."); |
| 711 return false; | 737 return false; |
| 712 } | 738 } |
| 713 | 739 |
| 714 | 740 |
| 715 if (!value->IsType(type())) { | 741 if (!value->IsType(type())) { |
| 716 // Allow the integer to double promition. Note that range restriction on | 742 // Allow the integer to double promition. Note that range restriction on |
| 717 // double is not supported now. | 743 // double is not supported now. |
| 718 if (value->IsType(base::Value::TYPE_INTEGER) && | 744 if (value->IsType(base::Value::TYPE_INTEGER) && |
| 719 type() == base::Value::TYPE_DOUBLE) | 745 type() == base::Value::TYPE_DOUBLE) |
| 720 return true; | 746 return true; |
| 721 | 747 |
| 722 *error = "The value type doesn't match the schema type."; | 748 SchemaFindError(error_path, error, |
| 749 "The value type doesn't match the schema type."); | |
|
Joao da Silva
2014/01/23 20:59:31
Format this line
binjin
2014/01/23 21:21:58
Done.
| |
| 723 return false; | 750 return false; |
| 724 } | 751 } |
| 725 | 752 |
| 726 base::DictionaryValue* dict = NULL; | 753 base::DictionaryValue* dict = NULL; |
| 727 base::ListValue* list = NULL; | 754 base::ListValue* list = NULL; |
| 728 if (value->GetAsDictionary(&dict)) { | 755 if (value->GetAsDictionary(&dict)) { |
| 729 std::vector<std::string> drop_list; // Contains the keys to drop. | 756 std::vector<std::string> drop_list; // Contains the keys to drop. |
| 730 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); | 757 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); |
| 731 it.Advance()) { | 758 it.Advance()) { |
| 732 Schema subschema = GetProperty(it.key()); | 759 Schema subschema = GetProperty(it.key()); |
| 733 if (!subschema.valid()) { | 760 if (!subschema.valid()) { |
| 734 // Unknown property was detected. | 761 // Unknown property was detected. |
| 735 if (StrategyAllowUnknownOnTopLevel(strategy)) { | 762 SchemaFindError(error_path, error, "Unknown property: " + it.key()); |
| 763 if (StrategyAllowUnknownOnTopLevel(strategy)) | |
| 736 drop_list.push_back(it.key()); | 764 drop_list.push_back(it.key()); |
| 737 } else { | 765 else |
| 738 *error = "Unknown property: " + it.key(); | |
| 739 return false; | 766 return false; |
| 740 } | |
| 741 } else { | 767 } else { |
| 742 base::Value* sub_value = NULL; | 768 base::Value* sub_value = NULL; |
| 743 dict->GetWithoutPathExpansion(it.key(), &sub_value); | 769 dict->GetWithoutPathExpansion(it.key(), &sub_value); |
| 744 std::string sub_error; | 770 if (!subschema.Normalize(sub_value, |
| 745 if (!subschema.Normalize( | 771 StrategyForNextLevel(strategy), |
| 746 sub_value, StrategyForNextLevel(strategy), &sub_error)) { | 772 error_path, |
| 773 error)) { | |
| 747 // Invalid property was detected. | 774 // Invalid property was detected. |
| 748 if (StrategyAllowInvalidOnTopLevel(strategy)) { | 775 AddDictKeyPrefixToPath(it.key(), error_path); |
| 776 if (StrategyAllowInvalidOnTopLevel(strategy)) | |
| 749 drop_list.push_back(it.key()); | 777 drop_list.push_back(it.key()); |
| 750 } else { | 778 else |
| 751 *error = sub_error; | |
| 752 return false; | 779 return false; |
| 753 } | |
| 754 } | 780 } |
| 755 } | 781 } |
| 756 } | 782 } |
| 757 for (std::vector<std::string>::const_iterator it = drop_list.begin(); | 783 for (std::vector<std::string>::const_iterator it = drop_list.begin(); |
| 758 it != drop_list.end(); | 784 it != drop_list.end(); |
| 759 ++it) { | 785 ++it) { |
| 760 dict->RemoveWithoutPathExpansion(*it, NULL); | 786 dict->RemoveWithoutPathExpansion(*it, NULL); |
| 761 } | 787 } |
| 762 return true; | 788 return true; |
| 763 } else if (value->GetAsList(&list)) { | 789 } else if (value->GetAsList(&list)) { |
| 764 std::vector<size_t> drop_list; // Contains the indexes to drop. | 790 std::vector<size_t> drop_list; // Contains the indexes to drop. |
| 765 for (size_t index = 0; index < list->GetSize(); index++) { | 791 for (size_t index = 0; index < list->GetSize(); index++) { |
| 766 base::Value* sub_value = NULL; | 792 base::Value* sub_value = NULL; |
| 767 std::string sub_error; | |
| 768 list->Get(index, &sub_value); | 793 list->Get(index, &sub_value); |
| 769 if (!sub_value || | 794 if (!sub_value || |
| 770 !GetItems().Normalize( | 795 !GetItems().Normalize( |
| 771 sub_value, StrategyForNextLevel(strategy), &sub_error)) { | 796 sub_value, StrategyForNextLevel(strategy), error_path, error)) { |
| 772 // Invalid list item was detected. | 797 // Invalid list item was detected. |
| 773 if (StrategyAllowInvalidOnTopLevel(strategy)) { | 798 AddListIndexPrefixToPath(index, error_path); |
| 799 if (StrategyAllowInvalidOnTopLevel(strategy)) | |
| 774 drop_list.push_back(index); | 800 drop_list.push_back(index); |
| 775 } else { | 801 else |
| 776 *error = sub_error; | |
| 777 return false; | 802 return false; |
| 778 } | |
| 779 } | 803 } |
| 780 } | 804 } |
| 781 for (std::vector<size_t>::reverse_iterator it = drop_list.rbegin(); | 805 for (std::vector<size_t>::reverse_iterator it = drop_list.rbegin(); |
| 782 it != drop_list.rend(); ++it) { | 806 it != drop_list.rend(); ++it) { |
| 783 list->Remove(*it, NULL); | 807 list->Remove(*it, NULL); |
| 784 } | 808 } |
| 785 return true; | 809 return true; |
| 786 } | 810 } |
| 787 | 811 |
| 788 return Validate(*value, strategy, error); | 812 return Validate(*value, strategy, error_path, error); |
| 789 } | 813 } |
| 790 | 814 |
| 791 // static | 815 // static |
| 792 Schema Schema::Parse(const std::string& content, std::string* error) { | 816 Schema Schema::Parse(const std::string& content, std::string* error) { |
| 793 // Validate as a generic JSON schema, and ignore unknown attributes; they | 817 // Validate as a generic JSON schema, and ignore unknown attributes; they |
| 794 // may become used in a future version of the schema format. | 818 // may become used in a future version of the schema format. |
| 795 scoped_ptr<base::DictionaryValue> dict = JSONSchemaValidator::IsValidSchema( | 819 scoped_ptr<base::DictionaryValue> dict = JSONSchemaValidator::IsValidSchema( |
| 796 content, JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, error); | 820 content, JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, error); |
| 797 if (!dict) | 821 if (!dict) |
| 798 return Schema(); | 822 return Schema(); |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 883 } else { | 907 } else { |
| 884 for (int i = rnode->enumeration_restriction.offset_begin; | 908 for (int i = rnode->enumeration_restriction.offset_begin; |
| 885 i < rnode->enumeration_restriction.offset_end; i++) { | 909 i < rnode->enumeration_restriction.offset_end; i++) { |
| 886 if (*storage_->int_enums(i) == value) | 910 if (*storage_->int_enums(i) == value) |
| 887 return true; | 911 return true; |
| 888 } | 912 } |
| 889 return false; | 913 return false; |
| 890 } | 914 } |
| 891 } | 915 } |
| 892 | 916 |
| 893 bool Schema::ValidateStringRestriction(int index, const char *str) const { | 917 bool Schema::ValidateStringRestriction(int index, const char* str) const { |
| 894 const RestrictionNode* rnode = storage_->restriction(index); | 918 const RestrictionNode* rnode = storage_->restriction(index); |
| 895 for (int i = rnode->enumeration_restriction.offset_begin; | 919 for (int i = rnode->enumeration_restriction.offset_begin; |
| 896 i < rnode->enumeration_restriction.offset_end; i++) { | 920 i < rnode->enumeration_restriction.offset_end; i++) { |
| 897 if (strcmp(*storage_->string_enums(i), str) == 0) | 921 if (strcmp(*storage_->string_enums(i), str) == 0) |
| 898 return true; | 922 return true; |
| 899 } | 923 } |
| 900 return false; | 924 return false; |
| 901 } | 925 } |
| 902 | 926 |
| 903 } // namespace policy | 927 } // namespace policy |
| OLD | NEW |