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> |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
75 }; | 75 }; |
76 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) { | 76 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) { |
77 if (kSchemaToValueTypeMap[i].schema_type == type_string) { | 77 if (kSchemaToValueTypeMap[i].schema_type == type_string) { |
78 *type = kSchemaToValueTypeMap[i].value_type; | 78 *type = kSchemaToValueTypeMap[i].value_type; |
79 return true; | 79 return true; |
80 } | 80 } |
81 } | 81 } |
82 return false; | 82 return false; |
83 } | 83 } |
84 | 84 |
| 85 bool StrategyAllowInvalidOnTopLevel(SchemaOnErrorStrategy strategy) { |
| 86 return strategy == SCHEMA_ALLOW_INVALID || |
| 87 strategy == SCHEMA_ALLOW_INVALID_TOPLEVEL || |
| 88 strategy == SCHEMA_ALLOW_INVALID_TOPLEVEL_AND_ALLOW_UNKNOWN; |
| 89 } |
| 90 |
| 91 bool StrategyAllowUnknownOnTopLevel(SchemaOnErrorStrategy strategy) { |
| 92 return strategy != SCHEMA_STRICT; |
| 93 } |
| 94 |
| 95 SchemaOnErrorStrategy StrategyForNextLevel(SchemaOnErrorStrategy strategy) { |
| 96 static SchemaOnErrorStrategy next_level_strategy[] = { |
| 97 SCHEMA_STRICT, // SCHEMA_STRICT |
| 98 SCHEMA_STRICT, // SCHEMA_ALLOW_UNKNOWN_TOPLEVEL |
| 99 SCHEMA_ALLOW_UNKNOWN, // SCHEMA_ALLOW_UNKNOWN |
| 100 SCHEMA_STRICT, // SCHEMA_ALLOW_INVALID_TOPLEVEL |
| 101 SCHEMA_ALLOW_UNKNOWN, // SCHEMA_ALLOW_INVALID_TOPLEVEL_AND_ALLOW_UNKNOWN |
| 102 SCHEMA_ALLOW_INVALID, // SCHEMA_ALLOW_INVALID |
| 103 }; |
| 104 return next_level_strategy[(int)strategy]; |
| 105 } |
| 106 |
85 } // namespace | 107 } // namespace |
86 | 108 |
87 // Contains the internal data representation of a Schema. This can either wrap | 109 // Contains the internal data representation of a Schema. This can either wrap |
88 // a SchemaData owned elsewhere (currently used to wrap the Chrome schema, which | 110 // a SchemaData owned elsewhere (currently used to wrap the Chrome schema, which |
89 // is generated at compile time), or it can own its own SchemaData. | 111 // is generated at compile time), or it can own its own SchemaData. |
90 class Schema::InternalStorage | 112 class Schema::InternalStorage |
91 : public base::RefCountedThreadSafe<InternalStorage> { | 113 : public base::RefCountedThreadSafe<InternalStorage> { |
92 public: | 114 public: |
93 static scoped_refptr<const InternalStorage> Wrap(const SchemaData* data); | 115 static scoped_refptr<const InternalStorage> Wrap(const SchemaData* data); |
94 | 116 |
(...skipping 507 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
602 node_ = schema.node_; | 624 node_ = schema.node_; |
603 return *this; | 625 return *this; |
604 } | 626 } |
605 | 627 |
606 // static | 628 // static |
607 Schema Schema::Wrap(const SchemaData* data) { | 629 Schema Schema::Wrap(const SchemaData* data) { |
608 scoped_refptr<const InternalStorage> storage = InternalStorage::Wrap(data); | 630 scoped_refptr<const InternalStorage> storage = InternalStorage::Wrap(data); |
609 return Schema(storage, storage->root_node()); | 631 return Schema(storage, storage->root_node()); |
610 } | 632 } |
611 | 633 |
612 bool Schema::Validate(const base::Value& value) const { | 634 bool Schema::Validate(const base::Value& value, |
| 635 SchemaOnErrorStrategy strategy, |
| 636 std::string* error) const { |
613 if (!valid()) { | 637 if (!valid()) { |
614 // Schema not found, invalid entry. | 638 *error = "The schema is invalid."; |
615 return false; | 639 return false; |
616 } | 640 } |
617 | 641 |
618 if (!value.IsType(type())) | 642 if (!value.IsType(type())) { |
| 643 // Allow the integer to double promotion. Note that range restriction on |
| 644 // double is not supported now. |
| 645 if (value.IsType(base::Value::TYPE_INTEGER) && |
| 646 type() == base::Value::TYPE_DOUBLE) { |
| 647 return true; |
| 648 } |
| 649 |
| 650 *error = "The value type doesn't match the schema type."; |
619 return false; | 651 return false; |
| 652 } |
620 | 653 |
621 const base::DictionaryValue* dict = NULL; | 654 const base::DictionaryValue* dict = NULL; |
622 const base::ListValue* list = NULL; | 655 const base::ListValue* list = NULL; |
623 int int_value; | 656 int int_value; |
624 std::string str_value; | 657 std::string str_value; |
625 if (value.GetAsDictionary(&dict)) { | 658 if (value.GetAsDictionary(&dict)) { |
626 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); | 659 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); |
627 it.Advance()) { | 660 it.Advance()) { |
628 if (!GetProperty(it.key()).Validate(it.value())) | 661 Schema subschema = GetProperty(it.key()); |
629 return false; | 662 if (!subschema.valid()) { |
| 663 // Unknown property was detected. |
| 664 if (!StrategyAllowUnknownOnTopLevel(strategy)) { |
| 665 *error = "Unknown property: " + it.key(); |
| 666 return false; |
| 667 } |
| 668 } else { |
| 669 std::string sub_error; |
| 670 if (!subschema.Validate( |
| 671 it.value(), StrategyForNextLevel(strategy), &sub_error)) { |
| 672 // Invalid property was detected. |
| 673 if (!StrategyAllowInvalidOnTopLevel(strategy)) { |
| 674 *error = sub_error; |
| 675 return false; |
| 676 } |
| 677 } |
| 678 } |
630 } | 679 } |
631 } else if (value.GetAsList(&list)) { | 680 } else if (value.GetAsList(&list)) { |
632 for (base::ListValue::const_iterator it = list->begin(); | 681 for (base::ListValue::const_iterator it = list->begin(); it != list->end(); |
633 it != list->end(); ++it) { | 682 ++it) { |
634 if (!*it || !GetItems().Validate(**it)) | 683 if (!*it || |
635 return false; | 684 !GetItems().Validate(**it, StrategyForNextLevel(strategy), error)) { |
| 685 // Invalid list item was detected. |
| 686 if (!StrategyAllowInvalidOnTopLevel(strategy)) |
| 687 return false; |
| 688 } |
636 } | 689 } |
637 } else if (value.GetAsInteger(&int_value)) { | 690 } else if (value.GetAsInteger(&int_value)) { |
638 return node_->extra == kInvalid || | 691 if (node_->extra != kInvalid && |
639 ValidateIntegerRestriction(node_->extra, int_value); | 692 !ValidateIntegerRestriction(node_->extra, int_value)) { |
| 693 *error = "Invalid value for integer"; |
| 694 return false; |
| 695 } |
640 } else if (value.GetAsString(&str_value)) { | 696 } else if (value.GetAsString(&str_value)) { |
641 return node_->extra == kInvalid || | 697 if (node_->extra != kInvalid && |
642 ValidateStringRestriction(node_->extra, str_value.c_str()); | 698 !ValidateStringRestriction(node_->extra, str_value.c_str())) { |
| 699 *error = "Invalid value for string"; |
| 700 return false; |
| 701 } |
643 } | 702 } |
644 | 703 |
645 return true; | 704 return true; |
646 } | 705 } |
647 | 706 |
| 707 bool Schema::Normalize(base::Value* value, |
| 708 SchemaOnErrorStrategy strategy, |
| 709 std::string* error) const { |
| 710 if (!valid()) { |
| 711 *error = "The schema is invalid."; |
| 712 return false; |
| 713 } |
| 714 |
| 715 if (!value->IsType(type())) { |
| 716 // Allow the integer to double promotion. Note that range restriction on |
| 717 // double is not supported now. |
| 718 if (value->IsType(base::Value::TYPE_INTEGER) && |
| 719 type() == base::Value::TYPE_DOUBLE) { |
| 720 return true; |
| 721 } |
| 722 |
| 723 *error = "The value type doesn't match the schema type."; |
| 724 return false; |
| 725 } |
| 726 |
| 727 base::DictionaryValue* dict = NULL; |
| 728 base::ListValue* list = NULL; |
| 729 if (value->GetAsDictionary(&dict)) { |
| 730 std::vector<std::string> drop_list; // Contains the keys to drop. |
| 731 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); |
| 732 it.Advance()) { |
| 733 Schema subschema = GetProperty(it.key()); |
| 734 if (!subschema.valid()) { |
| 735 // Unknown property was detected. |
| 736 if (StrategyAllowUnknownOnTopLevel(strategy)) { |
| 737 drop_list.push_back(it.key()); |
| 738 } else { |
| 739 *error = "Unknown property: " + it.key(); |
| 740 return false; |
| 741 } |
| 742 } else { |
| 743 base::Value* sub_value = NULL; |
| 744 dict->GetWithoutPathExpansion(it.key(), &sub_value); |
| 745 std::string sub_error; |
| 746 if (!subschema.Normalize( |
| 747 sub_value, StrategyForNextLevel(strategy), &sub_error)) { |
| 748 // Invalid property was detected. |
| 749 if (StrategyAllowInvalidOnTopLevel(strategy)) { |
| 750 drop_list.push_back(it.key()); |
| 751 } else { |
| 752 *error = sub_error; |
| 753 return false; |
| 754 } |
| 755 } |
| 756 } |
| 757 } |
| 758 for (std::vector<std::string>::const_iterator it = drop_list.begin(); |
| 759 it != drop_list.end(); |
| 760 ++it) { |
| 761 dict->RemoveWithoutPathExpansion(*it, NULL); |
| 762 } |
| 763 return true; |
| 764 } else if (value->GetAsList(&list)) { |
| 765 std::vector<size_t> drop_list; // Contains the indexes to drop. |
| 766 for (size_t index = 0; index < list->GetSize(); index++) { |
| 767 base::Value* sub_value = NULL; |
| 768 std::string sub_error; |
| 769 list->Get(index, &sub_value); |
| 770 if (!sub_value || |
| 771 !GetItems().Normalize( |
| 772 sub_value, StrategyForNextLevel(strategy), &sub_error)) { |
| 773 // Invalid list item was detected. |
| 774 if (StrategyAllowInvalidOnTopLevel(strategy)) { |
| 775 drop_list.push_back(index); |
| 776 } else { |
| 777 *error = sub_error; |
| 778 return false; |
| 779 } |
| 780 } |
| 781 } |
| 782 for (std::vector<size_t>::reverse_iterator it = drop_list.rbegin(); |
| 783 it != drop_list.rend(); ++it) { |
| 784 list->Remove(*it, NULL); |
| 785 } |
| 786 return true; |
| 787 } |
| 788 |
| 789 return Validate(*value, strategy, error); |
| 790 } |
| 791 |
648 // static | 792 // static |
649 Schema Schema::Parse(const std::string& content, std::string* error) { | 793 Schema Schema::Parse(const std::string& content, std::string* error) { |
650 // Validate as a generic JSON schema, and ignore unknown attributes; they | 794 // Validate as a generic JSON schema, and ignore unknown attributes; they |
651 // may become used in a future version of the schema format. | 795 // may become used in a future version of the schema format. |
652 scoped_ptr<base::DictionaryValue> dict = JSONSchemaValidator::IsValidSchema( | 796 scoped_ptr<base::DictionaryValue> dict = JSONSchemaValidator::IsValidSchema( |
653 content, JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, error); | 797 content, JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, error); |
654 if (!dict) | 798 if (!dict) |
655 return Schema(); | 799 return Schema(); |
656 | 800 |
657 // Validate the main type. | 801 // Validate the main type. |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
751 const RestrictionNode* rnode = storage_->restriction(index); | 895 const RestrictionNode* rnode = storage_->restriction(index); |
752 for (int i = rnode->enumeration_restriction.offset_begin; | 896 for (int i = rnode->enumeration_restriction.offset_begin; |
753 i < rnode->enumeration_restriction.offset_end; i++) { | 897 i < rnode->enumeration_restriction.offset_end; i++) { |
754 if (strcmp(*storage_->string_enums(i), str) == 0) | 898 if (strcmp(*storage_->string_enums(i), str) == 0) |
755 return true; | 899 return true; |
756 } | 900 } |
757 return false; | 901 return false; |
758 } | 902 } |
759 | 903 |
760 } // namespace policy | 904 } // namespace policy |
OLD | NEW |