- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / policy / cloud / component_cloud_policy_store.cc
1 // Copyright (c) 2013 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 "chrome/browser/policy/cloud/component_cloud_policy_store.h"
6
7 #include "base/callback.h"
8 #include "base/json/json_reader.h"
9 #include "base/logging.h"
10 #include "base/sha1.h"
11 #include "base/strings/string_util.h"
12 #include "base/values.h"
13 #include "chrome/browser/policy/cloud/cloud_policy_constants.h"
14 #include "chrome/browser/policy/cloud/cloud_policy_validator.h"
15 #include "chrome/browser/policy/cloud/resource_cache.h"
16 #include "chrome/browser/policy/external_data_fetcher.h"
17 #include "chrome/browser/policy/policy_map.h"
18 #include "chrome/browser/policy/proto/cloud/chrome_extension_policy.pb.h"
19 #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h"
20 #include "url/gurl.h"
21
22 namespace em = enterprise_management;
23
24 namespace policy {
25
26 namespace {
27
28 const char kValue[] = "Value";
29 const char kLevel[] = "Level";
30 const char kRecommended[] = "Recommended";
31
32 const struct DomainConstants {
33   PolicyDomain domain;
34   const char* proto_cache_key;
35   const char* data_cache_key;
36   const char* policy_type;
37 } kDomains[] = {
38   {
39     POLICY_DOMAIN_EXTENSIONS,
40     "extension-policy",
41     "extension-policy-data",
42     dm_protocol::kChromeExtensionPolicyType,
43   },
44 };
45
46 const DomainConstants* GetDomainConstants(PolicyDomain domain) {
47   for (size_t i = 0; i < arraysize(kDomains); ++i) {
48     if (kDomains[i].domain == domain)
49       return &kDomains[i];
50   }
51   return NULL;
52 }
53
54 const DomainConstants* GetDomainConstantsForType(const std::string& type) {
55   for (size_t i = 0; i < arraysize(kDomains); ++i) {
56     if (kDomains[i].policy_type == type)
57       return &kDomains[i];
58   }
59   return NULL;
60 }
61
62 }  // namespace
63
64 ComponentCloudPolicyStore::Delegate::~Delegate() {}
65
66 ComponentCloudPolicyStore::ComponentCloudPolicyStore(
67     Delegate* delegate,
68     ResourceCache* cache)
69     : delegate_(delegate),
70       cache_(cache) {}
71
72 ComponentCloudPolicyStore::~ComponentCloudPolicyStore() {
73   DCHECK(CalledOnValidThread());
74 }
75
76 // static
77 bool ComponentCloudPolicyStore::SupportsDomain(PolicyDomain domain) {
78   return GetDomainConstants(domain) != NULL;
79 }
80
81 // static
82 bool ComponentCloudPolicyStore::GetPolicyType(PolicyDomain domain,
83                                               std::string* policy_type) {
84   const DomainConstants* constants = GetDomainConstants(domain);
85   if (constants)
86     *policy_type = constants->policy_type;
87   return constants != NULL;
88 }
89
90 // static
91 bool ComponentCloudPolicyStore::GetPolicyDomain(const std::string& policy_type,
92                                                 PolicyDomain* domain) {
93   const DomainConstants* constants = GetDomainConstantsForType(policy_type);
94   if (constants)
95     *domain = constants->domain;
96   return constants != NULL;
97 }
98
99 const std::string& ComponentCloudPolicyStore::GetCachedHash(
100     const PolicyNamespace& ns) const {
101   DCHECK(CalledOnValidThread());
102   std::map<PolicyNamespace, std::string>::const_iterator it =
103       cached_hashes_.find(ns);
104   return it == cached_hashes_.end() ? EmptyString() : it->second;
105 }
106
107 void ComponentCloudPolicyStore::SetCredentials(const std::string& username,
108                                                const std::string& dm_token) {
109   DCHECK(CalledOnValidThread());
110   DCHECK(username_.empty() || username == username_);
111   DCHECK(dm_token_.empty() || dm_token == dm_token_);
112   username_ = username;
113   dm_token_ = dm_token;
114 }
115
116 void ComponentCloudPolicyStore::Load() {
117   DCHECK(CalledOnValidThread());
118   typedef std::map<std::string, std::string> ContentMap;
119
120   // Load all cached policy protobufs for each domain.
121   for (size_t domain = 0; domain < arraysize(kDomains); ++domain) {
122     const DomainConstants& constants = kDomains[domain];
123     ContentMap protos;
124     cache_->LoadAllSubkeys(constants.proto_cache_key, &protos);
125     for (ContentMap::iterator it = protos.begin(); it != protos.end(); ++it) {
126       const std::string& id(it->first);
127       PolicyNamespace ns(constants.domain, id);
128
129       // Validate each protobuf.
130       scoped_ptr<em::PolicyFetchResponse> proto(new em::PolicyFetchResponse);
131       em::ExternalPolicyData payload;
132       if (!proto->ParseFromString(it->second) ||
133           !ValidateProto(
134               proto.Pass(), constants.policy_type, id, &payload, NULL)) {
135         Delete(ns);
136         continue;
137       }
138
139       // The protobuf looks good; load the policy data.
140       std::string data;
141       PolicyMap policy;
142       if (cache_->Load(constants.data_cache_key, id, &data) &&
143           ValidateData(data, payload.secure_hash(), &policy)) {
144         // The data is also good; expose the policies.
145         policy_bundle_.Get(ns).Swap(&policy);
146         cached_hashes_[ns] = payload.secure_hash();
147       } else {
148         // The data for this proto couldn't be loaded or is corrupted.
149         Delete(ns);
150       }
151     }
152   }
153 }
154
155 bool ComponentCloudPolicyStore::Store(const PolicyNamespace& ns,
156                                       const std::string& serialized_policy,
157                                       const std::string& secure_hash,
158                                       const std::string& data) {
159   DCHECK(CalledOnValidThread());
160   const DomainConstants* constants = GetDomainConstants(ns.domain);
161   PolicyMap policy;
162   // |serialized_policy| has already been validated; validate the data now.
163   if (!constants || !ValidateData(data, secure_hash, &policy))
164     return false;
165
166   // Flush the proto and the data to the cache.
167   cache_->Store(constants->proto_cache_key, ns.component_id, serialized_policy);
168   cache_->Store(constants->data_cache_key, ns.component_id, data);
169   // And expose the policy.
170   policy_bundle_.Get(ns).Swap(&policy);
171   cached_hashes_[ns] = secure_hash;
172   delegate_->OnComponentCloudPolicyStoreUpdated();
173   return true;
174 }
175
176 void ComponentCloudPolicyStore::Delete(const PolicyNamespace& ns) {
177   DCHECK(CalledOnValidThread());
178   const DomainConstants* constants = GetDomainConstants(ns.domain);
179   if (!constants)
180     return;
181
182   cache_->Delete(constants->proto_cache_key, ns.component_id);
183   cache_->Delete(constants->data_cache_key, ns.component_id);
184
185   if (!policy_bundle_.Get(ns).empty()) {
186     policy_bundle_.Get(ns).Clear();
187     delegate_->OnComponentCloudPolicyStoreUpdated();
188   }
189 }
190
191 void ComponentCloudPolicyStore::Purge(PolicyDomain domain,
192                                       const std::set<std::string>& keep) {
193   DCHECK(CalledOnValidThread());
194   const DomainConstants* constants = GetDomainConstants(domain);
195   if (!constants)
196     return;
197
198   cache_->PurgeOtherSubkeys(constants->proto_cache_key, keep);
199   cache_->PurgeOtherSubkeys(constants->data_cache_key, keep);
200
201   // Stop serving policies for purged namespaces.
202   bool purged_current_policies = false;
203   for (PolicyBundle::const_iterator it = policy_bundle_.begin();
204        it != policy_bundle_.end(); ++it) {
205     if (it->first.domain == domain &&
206         keep.find(it->first.component_id) == keep.end() &&
207         !policy_bundle_.Get(it->first).empty()) {
208       policy_bundle_.Get(it->first).Clear();
209       purged_current_policies = true;
210     }
211   }
212
213   // Purge cached hashes, so that those namespaces can be fetched again if the
214   // policy state changes.
215   std::map<PolicyNamespace, std::string>::iterator it = cached_hashes_.begin();
216   while (it != cached_hashes_.end()) {
217     if (it->first.domain == domain &&
218         keep.find(it->first.component_id) == keep.end()) {
219       std::map<PolicyNamespace, std::string>::iterator prev = it;
220       ++it;
221       cached_hashes_.erase(prev);
222     } else {
223       ++it;
224     }
225   }
226
227   if (purged_current_policies)
228     delegate_->OnComponentCloudPolicyStoreUpdated();
229 }
230
231 bool ComponentCloudPolicyStore::ValidatePolicy(
232     scoped_ptr<em::PolicyFetchResponse> proto,
233     PolicyNamespace* ns,
234     em::ExternalPolicyData* payload) {
235   em::PolicyData policy_data;
236   if (!ValidateProto(
237           proto.Pass(), std::string(), std::string(), payload, &policy_data)) {
238     return false;
239   }
240
241   if (!policy_data.has_policy_type())
242     return false;
243
244   const DomainConstants* constants =
245       GetDomainConstantsForType(policy_data.policy_type());
246   if (!constants || !policy_data.has_settings_entity_id())
247     return false;
248
249   ns->domain = constants->domain;
250   ns->component_id = policy_data.settings_entity_id();
251   return true;
252 }
253
254 bool ComponentCloudPolicyStore::ValidateProto(
255     scoped_ptr<em::PolicyFetchResponse> proto,
256     const std::string& policy_type,
257     const std::string& settings_entity_id,
258     em::ExternalPolicyData* payload,
259     em::PolicyData* policy_data) {
260   if (username_.empty() || dm_token_.empty())
261     return false;
262
263   scoped_ptr<ComponentCloudPolicyValidator> validator(
264       ComponentCloudPolicyValidator::Create(
265           proto.Pass(), scoped_refptr<base::SequencedTaskRunner>()));
266   validator->ValidateUsername(username_);
267   validator->ValidateDMToken(dm_token_,
268                              ComponentCloudPolicyValidator::DM_TOKEN_REQUIRED);
269   if (!policy_type.empty())
270     validator->ValidatePolicyType(policy_type);
271   if (!settings_entity_id.empty())
272     validator->ValidateSettingsEntityId(settings_entity_id);
273   validator->ValidatePayload();
274   // TODO(joaodasilva): validate signature.
275   validator->RunValidation();
276   if (!validator->success())
277     return false;
278
279   em::ExternalPolicyData* data = validator->payload().get();
280   // The download URL must be empty, or must be a valid URL.
281   // An empty download URL signals that this component doesn't have cloud
282   // policy, or that the policy has been removed.
283   if (data->has_download_url() && !data->download_url().empty()) {
284     if (!GURL(data->download_url()).is_valid() ||
285         !data->has_secure_hash() ||
286         data->secure_hash().empty()) {
287       return false;
288     }
289   } else if (data->has_secure_hash()) {
290     return false;
291   }
292
293   if (payload)
294     payload->Swap(validator->payload().get());
295   if (policy_data)
296     policy_data->Swap(validator->policy_data().get());
297   return true;
298 }
299
300 bool ComponentCloudPolicyStore::ValidateData(
301     const std::string& data,
302     const std::string& secure_hash,
303     PolicyMap* policy) {
304   return base::SHA1HashString(data) == secure_hash && ParsePolicy(data, policy);
305 }
306
307 bool ComponentCloudPolicyStore::ParsePolicy(const std::string& data,
308                                             PolicyMap* policy) {
309   scoped_ptr<base::Value> json(base::JSONReader::Read(
310       data, base::JSON_PARSE_RFC | base::JSON_DETACHABLE_CHILDREN));
311   base::DictionaryValue* dict = NULL;
312   if (!json || !json->GetAsDictionary(&dict))
313     return false;
314
315   // Each top-level key maps a policy name to its description.
316   //
317   // Each description is an object that contains the policy value under the
318   // "Value" key. The optional "Level" key is either "Mandatory" (default) or
319   // "Recommended".
320   for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
321     base::DictionaryValue* description = NULL;
322     if (!dict->GetDictionaryWithoutPathExpansion(it.key(), &description))
323       return false;
324
325     scoped_ptr<base::Value> value;
326     if (!description->RemoveWithoutPathExpansion(kValue, &value))
327       return false;
328
329     PolicyLevel level = POLICY_LEVEL_MANDATORY;
330     std::string level_string;
331     if (description->GetStringWithoutPathExpansion(kLevel, &level_string) &&
332         level_string == kRecommended) {
333       level = POLICY_LEVEL_RECOMMENDED;
334     }
335
336     // If policy for components is ever used for device-level settings then
337     // this must support a configurable scope; assuming POLICY_SCOPE_USER is
338     // fine for now.
339     policy->Set(it.key(), level, POLICY_SCOPE_USER, value.release(), NULL);
340   }
341
342   return true;
343 }
344
345 }  // namespace policy