Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(123)

Side by Side Diff: components/policy/core/common/schema.cc

Issue 139853013: Improve error message display for Schema::Validate() (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@expand-policy-schema-3
Patch Set: fix comments Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698