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 |