1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/policy/core/common/policy_proto_decoders.h"
6
7 #include <limits>
8 #include <memory>
9
10 #include "base/json/json_reader.h"
11 #include "base/logging.h"
12 #include "base/values.h"
13 #include "components/policy/core/common/cloud/cloud_external_data_manager.h"
14 #include "components/policy/core/common/policy_map.h"
15 #include "components/policy/policy_constants.h"
16 #include "components/policy/proto/cloud_policy.pb.h"
17
18 namespace policy {
19
20 namespace em = enterprise_management;
21
22 namespace {
23
24 // Returns true and sets |level| to a PolicyLevel if the policy has been set
25 // at that level. Returns false if the policy is not set, or has been set at
26 // the level of PolicyOptions::UNSET.
27 template <class AnyPolicyProto>
GetPolicyLevel(const AnyPolicyProto & policy_proto,PolicyLevel * level)28 bool GetPolicyLevel(const AnyPolicyProto& policy_proto, PolicyLevel* level) {
29 if (!policy_proto.has_value()) {
30 return false;
31 }
32 if (!policy_proto.has_policy_options()) {
33 *level = POLICY_LEVEL_MANDATORY; // Default level.
34 return true;
35 }
36 switch (policy_proto.policy_options().mode()) {
37 case em::PolicyOptions::MANDATORY:
38 *level = POLICY_LEVEL_MANDATORY;
39 return true;
40 case em::PolicyOptions::RECOMMENDED:
41 *level = POLICY_LEVEL_RECOMMENDED;
42 return true;
43 case em::PolicyOptions::UNSET:
44 return false;
45 }
46 }
47
48 // Convert a BooleanPolicyProto to a bool base::Value.
DecodeBooleanProto(const em::BooleanPolicyProto & proto)49 std::unique_ptr<base::Value> DecodeBooleanProto(
50 const em::BooleanPolicyProto& proto) {
51 return std::make_unique<base::Value>(proto.value());
52 }
53
54 // Convert an IntegerPolicyProto to an int base::Value.
DecodeIntegerProto(const em::IntegerPolicyProto & proto,std::string * error)55 std::unique_ptr<base::Value> DecodeIntegerProto(
56 const em::IntegerPolicyProto& proto,
57 std::string* error) {
58 google::protobuf::int64 value = proto.value();
59
60 if (value < std::numeric_limits<int>::min() ||
61 value > std::numeric_limits<int>::max()) {
62 LOG(WARNING) << "Integer value " << value << " out of numeric limits";
63 *error = "Number out of range - invalid int32";
64 return std::make_unique<base::Value>(std::to_string(value));
65 }
66
67 return std::make_unique<base::Value>(static_cast<int>(value));
68 }
69
70 // Convert a StringPolicyProto to a string base::Value.
DecodeStringProto(const em::StringPolicyProto & proto)71 std::unique_ptr<base::Value> DecodeStringProto(
72 const em::StringPolicyProto& proto) {
73 return std::make_unique<base::Value>(proto.value());
74 }
75
76 // Convert a StringListPolicyProto to a List base::Value, where each list value
77 // is of Type::STRING.
DecodeStringListProto(const em::StringListPolicyProto & proto)78 std::unique_ptr<base::Value> DecodeStringListProto(
79 const em::StringListPolicyProto& proto) {
80 auto list_value = std::make_unique<base::ListValue>();
81 for (const auto& entry : proto.value().entries())
82 list_value->AppendString(entry);
83 return std::move(list_value);
84 }
85
86 // Convert a StringPolicyProto to a base::Value of any type (for example,
87 // Type::DICTIONARY or Type::LIST) by parsing it as JSON.
DecodeJsonProto(const em::StringPolicyProto & proto,std::string * error)88 std::unique_ptr<base::Value> DecodeJsonProto(const em::StringPolicyProto& proto,
89 std::string* error) {
90 const std::string& json = proto.value();
91 std::unique_ptr<base::Value> parsed_value =
92 base::JSONReader::ReadAndReturnError(
93 json, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, error);
94
95 if (!parsed_value) {
96 // Can't parse as JSON so return it as a string, and leave it to the handler
97 // to validate.
98 LOG(WARNING) << "Invalid JSON: " << json;
99 return std::make_unique<base::Value>(json);
100 }
101
102 // Accept any Value type that parsed as JSON, and leave it to the handler to
103 // convert and check the concrete type.
104 error->clear();
105 return parsed_value;
106 }
107
108 } // namespace
109
DecodeProtoFields(const em::CloudPolicySettings & policy,base::WeakPtr<CloudExternalDataManager> external_data_manager,PolicySource source,PolicyScope scope,PolicyMap * map)110 void DecodeProtoFields(
111 const em::CloudPolicySettings& policy,
112 base::WeakPtr<CloudExternalDataManager> external_data_manager,
113 PolicySource source,
114 PolicyScope scope,
115 PolicyMap* map) {
116 PolicyLevel level;
117
118 // Access arrays are terminated by a struct that contains only nullptrs.
119 for (const BooleanPolicyAccess* access = &kBooleanPolicyAccess[0];
120 access->policy_key; access++) {
121 if (!(policy.*access->has_proto)())
122 continue;
123
124 const em::BooleanPolicyProto& proto = (policy.*access->get_proto)();
125 if (!GetPolicyLevel(proto, &level))
126 continue;
127
128 map->Set(access->policy_key, level, scope, source,
129 DecodeBooleanProto(proto), nullptr);
130 }
131
132 for (const IntegerPolicyAccess* access = &kIntegerPolicyAccess[0];
133 access->policy_key; access++) {
134 if (!(policy.*access->has_proto)())
135 continue;
136
137 const em::IntegerPolicyProto& proto = (policy.*access->get_proto)();
138 if (!GetPolicyLevel(proto, &level))
139 continue;
140
141 std::string error;
142 map->Set(access->policy_key, level, scope, source,
143 DecodeIntegerProto(proto, &error), nullptr);
144 if (!error.empty())
145 map->SetError(access->policy_key, error);
146 }
147
148 for (const StringPolicyAccess* access = &kStringPolicyAccess[0];
149 access->policy_key; access++) {
150 if (!(policy.*access->has_proto)())
151 continue;
152
153 const em::StringPolicyProto& proto = (policy.*access->get_proto)();
154 if (!GetPolicyLevel(proto, &level))
155 continue;
156
157 std::string error;
158 std::unique_ptr<base::Value> value =
159 (access->type == StringPolicyType::STRING)
160 ? DecodeStringProto(proto)
161 : DecodeJsonProto(proto, &error);
162
163 std::unique_ptr<ExternalDataFetcher> external_data_fetcher =
164 (access->type == StringPolicyType::EXTERNAL)
165 ? std::make_unique<ExternalDataFetcher>(external_data_manager,
166 access->policy_key)
167 : nullptr;
168
169 map->Set(access->policy_key, level, scope, source, std::move(value),
170 std::move(external_data_fetcher));
171 if (!error.empty())
172 map->SetError(access->policy_key, error);
173 }
174
175 for (const StringListPolicyAccess* access = &kStringListPolicyAccess[0];
176 access->policy_key; access++) {
177 if (!(policy.*access->has_proto)())
178 continue;
179
180 const em::StringListPolicyProto& proto = (policy.*access->get_proto)();
181 if (!GetPolicyLevel(proto, &level))
182 continue;
183
184 map->Set(access->policy_key, level, scope, source,
185 DecodeStringListProto(proto), nullptr);
186 }
187 }
188
189 } // namespace policy