| 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 SchemaErrorFound(std::string* error_path, |
| 109 std::string* error, |
| 110 const std::string& msg) { |
| 111 if (error_path) |
| 112 *error_path = ""; |
| 113 *error = msg; |
| 114 } |
| 115 |
| 116 void AddListIndexPrefixToPath(int index, std::string* path) { |
| 117 if (path) { |
| 118 if (path->empty()) |
| 119 *path = base::StringPrintf("items[%d]", index); |
| 120 else |
| 121 *path = base::StringPrintf("items[%d].", index) + *path; |
| 122 } |
| 123 } |
| 124 |
| 125 void AddDictKeyPrefixToPath(const std::string& key, std::string* path) { |
| 126 if (path) { |
| 127 if (path->empty()) |
| 128 *path = key; |
| 129 else |
| 130 *path = key + "." + *path; |
| 131 } |
| 132 } |
| 133 |
| 107 } // namespace | 134 } // namespace |
| 108 | 135 |
| 109 // Contains the internal data representation of a Schema. This can either wrap | 136 // 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 | 137 // 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. | 138 // is generated at compile time), or it can own its own SchemaData. |
| 112 class Schema::InternalStorage | 139 class Schema::InternalStorage |
| 113 : public base::RefCountedThreadSafe<InternalStorage> { | 140 : public base::RefCountedThreadSafe<InternalStorage> { |
| 114 public: | 141 public: |
| 115 static scoped_refptr<const InternalStorage> Wrap(const SchemaData* data); | 142 static scoped_refptr<const InternalStorage> Wrap(const SchemaData* data); |
| 116 | 143 |
| (...skipping 509 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 626 } | 653 } |
| 627 | 654 |
| 628 // static | 655 // static |
| 629 Schema Schema::Wrap(const SchemaData* data) { | 656 Schema Schema::Wrap(const SchemaData* data) { |
| 630 scoped_refptr<const InternalStorage> storage = InternalStorage::Wrap(data); | 657 scoped_refptr<const InternalStorage> storage = InternalStorage::Wrap(data); |
| 631 return Schema(storage, storage->root_node()); | 658 return Schema(storage, storage->root_node()); |
| 632 } | 659 } |
| 633 | 660 |
| 634 bool Schema::Validate(const base::Value& value, | 661 bool Schema::Validate(const base::Value& value, |
| 635 SchemaOnErrorStrategy strategy, | 662 SchemaOnErrorStrategy strategy, |
| 663 std::string* error_path, |
| 636 std::string* error) const { | 664 std::string* error) const { |
| 637 if (!valid()) { | 665 if (!valid()) { |
| 638 *error = "The schema is invalid."; | 666 SchemaErrorFound(error_path, error, "The schema is invalid."); |
| 639 return false; | 667 return false; |
| 640 } | 668 } |
| 641 | 669 |
| 642 if (!value.IsType(type())) { | 670 if (!value.IsType(type())) { |
| 643 // Allow the integer to double promotion. Note that range restriction on | 671 // Allow the integer to double promotion. Note that range restriction on |
| 644 // double is not supported now. | 672 // double is not supported now. |
| 645 if (value.IsType(base::Value::TYPE_INTEGER) && | 673 if (value.IsType(base::Value::TYPE_INTEGER) && |
| 646 type() == base::Value::TYPE_DOUBLE) { | 674 type() == base::Value::TYPE_DOUBLE) { |
| 647 return true; | 675 return true; |
| 648 } | 676 } |
| 649 | 677 |
| 650 *error = "The value type doesn't match the schema type."; | 678 SchemaErrorFound( |
| 679 error_path, error, "The value type doesn't match the schema type."); |
| 651 return false; | 680 return false; |
| 652 } | 681 } |
| 653 | 682 |
| 654 const base::DictionaryValue* dict = NULL; | 683 const base::DictionaryValue* dict = NULL; |
| 655 const base::ListValue* list = NULL; | 684 const base::ListValue* list = NULL; |
| 656 int int_value; | 685 int int_value; |
| 657 std::string str_value; | 686 std::string str_value; |
| 658 if (value.GetAsDictionary(&dict)) { | 687 if (value.GetAsDictionary(&dict)) { |
| 659 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); | 688 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); |
| 660 it.Advance()) { | 689 it.Advance()) { |
| 661 Schema subschema = GetProperty(it.key()); | 690 Schema subschema = GetProperty(it.key()); |
| 662 if (!subschema.valid()) { | 691 if (!subschema.valid()) { |
| 663 // Unknown property was detected. | 692 // Unknown property was detected. |
| 664 if (!StrategyAllowUnknownOnTopLevel(strategy)) { | 693 SchemaErrorFound(error_path, error, "Unknown property: " + it.key()); |
| 665 *error = "Unknown property: " + it.key(); | 694 if (!StrategyAllowUnknownOnTopLevel(strategy)) |
| 666 return false; | 695 return false; |
| 667 } | 696 } else if (!subschema.Validate(it.value(), |
| 668 } else { | 697 StrategyForNextLevel(strategy), |
| 669 std::string sub_error; | 698 error_path, |
| 670 if (!subschema.Validate( | 699 error)) { |
| 671 it.value(), StrategyForNextLevel(strategy), &sub_error)) { | 700 // Invalid property was detected. |
| 672 // Invalid property was detected. | 701 AddDictKeyPrefixToPath(it.key(), error_path); |
| 673 if (!StrategyAllowInvalidOnTopLevel(strategy)) { | 702 if (!StrategyAllowInvalidOnTopLevel(strategy)) |
| 674 *error = sub_error; | 703 return false; |
| 675 return false; | |
| 676 } | |
| 677 } | |
| 678 } | 704 } |
| 679 } | 705 } |
| 680 } else if (value.GetAsList(&list)) { | 706 } else if (value.GetAsList(&list)) { |
| 681 for (base::ListValue::const_iterator it = list->begin(); it != list->end(); | 707 for (base::ListValue::const_iterator it = list->begin(); it != list->end(); |
| 682 ++it) { | 708 ++it) { |
| 683 if (!*it || | 709 if (!*it || |
| 684 !GetItems().Validate(**it, StrategyForNextLevel(strategy), error)) { | 710 !GetItems().Validate(**it, |
| 711 StrategyForNextLevel(strategy), |
| 712 error_path, |
| 713 error)) { |
| 685 // Invalid list item was detected. | 714 // Invalid list item was detected. |
| 715 AddListIndexPrefixToPath(it - list->begin(), error_path); |
| 686 if (!StrategyAllowInvalidOnTopLevel(strategy)) | 716 if (!StrategyAllowInvalidOnTopLevel(strategy)) |
| 687 return false; | 717 return false; |
| 688 } | 718 } |
| 689 } | 719 } |
| 690 } else if (value.GetAsInteger(&int_value)) { | 720 } else if (value.GetAsInteger(&int_value)) { |
| 691 if (node_->extra != kInvalid && | 721 if (node_->extra != kInvalid && |
| 692 !ValidateIntegerRestriction(node_->extra, int_value)) { | 722 !ValidateIntegerRestriction(node_->extra, int_value)) { |
| 693 *error = "Invalid value for integer"; | 723 SchemaErrorFound(error_path, error, "Invalid value for integer"); |
| 694 return false; | 724 return false; |
| 695 } | 725 } |
| 696 } else if (value.GetAsString(&str_value)) { | 726 } else if (value.GetAsString(&str_value)) { |
| 697 if (node_->extra != kInvalid && | 727 if (node_->extra != kInvalid && |
| 698 !ValidateStringRestriction(node_->extra, str_value.c_str())) { | 728 !ValidateStringRestriction(node_->extra, str_value.c_str())) { |
| 699 *error = "Invalid value for string"; | 729 SchemaErrorFound(error_path, error, "Invalid value for string"); |
| 700 return false; | 730 return false; |
| 701 } | 731 } |
| 702 } | 732 } |
| 703 | 733 |
| 704 return true; | 734 return true; |
| 705 } | 735 } |
| 706 | 736 |
| 707 bool Schema::Normalize(base::Value* value, | 737 bool Schema::Normalize(base::Value* value, |
| 708 SchemaOnErrorStrategy strategy, | 738 SchemaOnErrorStrategy strategy, |
| 739 std::string* error_path, |
| 709 std::string* error) const { | 740 std::string* error) const { |
| 710 if (!valid()) { | 741 if (!valid()) { |
| 711 *error = "The schema is invalid."; | 742 SchemaErrorFound(error_path, error, "The schema is invalid."); |
| 712 return false; | 743 return false; |
| 713 } | 744 } |
| 714 | 745 |
| 715 if (!value->IsType(type())) { | 746 if (!value->IsType(type())) { |
| 716 // Allow the integer to double promotion. Note that range restriction on | 747 // Allow the integer to double promotion. Note that range restriction on |
| 717 // double is not supported now. | 748 // double is not supported now. |
| 718 if (value->IsType(base::Value::TYPE_INTEGER) && | 749 if (value->IsType(base::Value::TYPE_INTEGER) && |
| 719 type() == base::Value::TYPE_DOUBLE) { | 750 type() == base::Value::TYPE_DOUBLE) { |
| 720 return true; | 751 return true; |
| 721 } | 752 } |
| 722 | 753 |
| 723 *error = "The value type doesn't match the schema type."; | 754 SchemaErrorFound( |
| 755 error_path, error, "The value type doesn't match the schema type."); |
| 724 return false; | 756 return false; |
| 725 } | 757 } |
| 726 | 758 |
| 727 base::DictionaryValue* dict = NULL; | 759 base::DictionaryValue* dict = NULL; |
| 728 base::ListValue* list = NULL; | 760 base::ListValue* list = NULL; |
| 729 if (value->GetAsDictionary(&dict)) { | 761 if (value->GetAsDictionary(&dict)) { |
| 730 std::vector<std::string> drop_list; // Contains the keys to drop. | 762 std::vector<std::string> drop_list; // Contains the keys to drop. |
| 731 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); | 763 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); |
| 732 it.Advance()) { | 764 it.Advance()) { |
| 733 Schema subschema = GetProperty(it.key()); | 765 Schema subschema = GetProperty(it.key()); |
| 734 if (!subschema.valid()) { | 766 if (!subschema.valid()) { |
| 735 // Unknown property was detected. | 767 // Unknown property was detected. |
| 736 if (StrategyAllowUnknownOnTopLevel(strategy)) { | 768 SchemaErrorFound(error_path, error, "Unknown property: " + it.key()); |
| 769 if (StrategyAllowUnknownOnTopLevel(strategy)) |
| 737 drop_list.push_back(it.key()); | 770 drop_list.push_back(it.key()); |
| 738 } else { | 771 else |
| 739 *error = "Unknown property: " + it.key(); | |
| 740 return false; | 772 return false; |
| 741 } | |
| 742 } else { | 773 } else { |
| 743 base::Value* sub_value = NULL; | 774 base::Value* sub_value = NULL; |
| 744 dict->GetWithoutPathExpansion(it.key(), &sub_value); | 775 dict->GetWithoutPathExpansion(it.key(), &sub_value); |
| 745 std::string sub_error; | 776 if (!subschema.Normalize(sub_value, |
| 746 if (!subschema.Normalize( | 777 StrategyForNextLevel(strategy), |
| 747 sub_value, StrategyForNextLevel(strategy), &sub_error)) { | 778 error_path, |
| 779 error)) { |
| 748 // Invalid property was detected. | 780 // Invalid property was detected. |
| 749 if (StrategyAllowInvalidOnTopLevel(strategy)) { | 781 AddDictKeyPrefixToPath(it.key(), error_path); |
| 782 if (StrategyAllowInvalidOnTopLevel(strategy)) |
| 750 drop_list.push_back(it.key()); | 783 drop_list.push_back(it.key()); |
| 751 } else { | 784 else |
| 752 *error = sub_error; | |
| 753 return false; | 785 return false; |
| 754 } | |
| 755 } | 786 } |
| 756 } | 787 } |
| 757 } | 788 } |
| 758 for (std::vector<std::string>::const_iterator it = drop_list.begin(); | 789 for (std::vector<std::string>::const_iterator it = drop_list.begin(); |
| 759 it != drop_list.end(); | 790 it != drop_list.end(); |
| 760 ++it) { | 791 ++it) { |
| 761 dict->RemoveWithoutPathExpansion(*it, NULL); | 792 dict->RemoveWithoutPathExpansion(*it, NULL); |
| 762 } | 793 } |
| 763 return true; | 794 return true; |
| 764 } else if (value->GetAsList(&list)) { | 795 } else if (value->GetAsList(&list)) { |
| 765 std::vector<size_t> drop_list; // Contains the indexes to drop. | 796 std::vector<size_t> drop_list; // Contains the indexes to drop. |
| 766 for (size_t index = 0; index < list->GetSize(); index++) { | 797 for (size_t index = 0; index < list->GetSize(); index++) { |
| 767 base::Value* sub_value = NULL; | 798 base::Value* sub_value = NULL; |
| 768 std::string sub_error; | |
| 769 list->Get(index, &sub_value); | 799 list->Get(index, &sub_value); |
| 770 if (!sub_value || | 800 if (!sub_value || |
| 771 !GetItems().Normalize( | 801 !GetItems().Normalize(sub_value, |
| 772 sub_value, StrategyForNextLevel(strategy), &sub_error)) { | 802 StrategyForNextLevel(strategy), |
| 803 error_path, |
| 804 error)) { |
| 773 // Invalid list item was detected. | 805 // Invalid list item was detected. |
| 774 if (StrategyAllowInvalidOnTopLevel(strategy)) { | 806 AddListIndexPrefixToPath(index, error_path); |
| 807 if (StrategyAllowInvalidOnTopLevel(strategy)) |
| 775 drop_list.push_back(index); | 808 drop_list.push_back(index); |
| 776 } else { | 809 else |
| 777 *error = sub_error; | |
| 778 return false; | 810 return false; |
| 779 } | |
| 780 } | 811 } |
| 781 } | 812 } |
| 782 for (std::vector<size_t>::reverse_iterator it = drop_list.rbegin(); | 813 for (std::vector<size_t>::reverse_iterator it = drop_list.rbegin(); |
| 783 it != drop_list.rend(); ++it) { | 814 it != drop_list.rend(); ++it) { |
| 784 list->Remove(*it, NULL); | 815 list->Remove(*it, NULL); |
| 785 } | 816 } |
| 786 return true; | 817 return true; |
| 787 } | 818 } |
| 788 | 819 |
| 789 return Validate(*value, strategy, error); | 820 return Validate(*value, strategy, error_path, error); |
| 790 } | 821 } |
| 791 | 822 |
| 792 // static | 823 // static |
| 793 Schema Schema::Parse(const std::string& content, std::string* error) { | 824 Schema Schema::Parse(const std::string& content, std::string* error) { |
| 794 // Validate as a generic JSON schema, and ignore unknown attributes; they | 825 // Validate as a generic JSON schema, and ignore unknown attributes; they |
| 795 // may become used in a future version of the schema format. | 826 // may become used in a future version of the schema format. |
| 796 scoped_ptr<base::DictionaryValue> dict = JSONSchemaValidator::IsValidSchema( | 827 scoped_ptr<base::DictionaryValue> dict = JSONSchemaValidator::IsValidSchema( |
| 797 content, JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, error); | 828 content, JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, error); |
| 798 if (!dict) | 829 if (!dict) |
| 799 return Schema(); | 830 return Schema(); |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 884 } else { | 915 } else { |
| 885 for (int i = rnode->enumeration_restriction.offset_begin; | 916 for (int i = rnode->enumeration_restriction.offset_begin; |
| 886 i < rnode->enumeration_restriction.offset_end; i++) { | 917 i < rnode->enumeration_restriction.offset_end; i++) { |
| 887 if (*storage_->int_enums(i) == value) | 918 if (*storage_->int_enums(i) == value) |
| 888 return true; | 919 return true; |
| 889 } | 920 } |
| 890 return false; | 921 return false; |
| 891 } | 922 } |
| 892 } | 923 } |
| 893 | 924 |
| 894 bool Schema::ValidateStringRestriction(int index, const char *str) const { | 925 bool Schema::ValidateStringRestriction(int index, const char* str) const { |
| 895 const RestrictionNode* rnode = storage_->restriction(index); | 926 const RestrictionNode* rnode = storage_->restriction(index); |
| 896 for (int i = rnode->enumeration_restriction.offset_begin; | 927 for (int i = rnode->enumeration_restriction.offset_begin; |
| 897 i < rnode->enumeration_restriction.offset_end; i++) { | 928 i < rnode->enumeration_restriction.offset_end; i++) { |
| 898 if (strcmp(*storage_->string_enums(i), str) == 0) | 929 if (strcmp(*storage_->string_enums(i), str) == 0) |
| 899 return true; | 930 return true; |
| 900 } | 931 } |
| 901 return false; | 932 return false; |
| 902 } | 933 } |
| 903 | 934 |
| 904 } // namespace policy | 935 } // namespace policy |
| OLD | NEW |