pull in new policy updates
[profile/ivi/smartdevicelink.git] / src / components / policy / src / policy / src / policy_helper.cc
1 /*
2  Copyright (c) 2013, Ford Motor Company
3  All rights reserved.
4
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7
8  Redistributions of source code must retain the above copyright notice, this
9  list of conditions and the following disclaimer.
10
11  Redistributions in binary form must reproduce the above copyright notice,
12  this list of conditions and the following
13  disclaimer in the documentation and/or other materials provided with the
14  distribution.
15
16  Neither the name of the Ford Motor Company nor the names of its contributors
17  may be used to endorse or promote products derived from this software
18  without specific prior written permission.
19
20  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <algorithm>
34 #include <sstream>
35 #include <string.h>
36 #include "utils/logger.h"
37 #include "policy/policy_helper.h"
38 #include "policy/policy_manager_impl.h"
39
40 namespace policy {
41
42 namespace {
43
44 CREATE_LOGGERPTR_GLOBAL(logger_, "PolicyManagerImpl")
45
46 bool Match(const StringsValueType& first_name,
47            const StringsValueType& second_name) {
48   const std::string& first = first_name;
49   const std::string& second = second_name;
50   return (strcasecmp(first.c_str(), second.c_str()) == 0);
51 }
52
53 bool Compare(const StringsValueType& first, const StringsValueType& second) {
54   const std::string& first_str = first;
55   const std::string& second_str = second;
56   return (strcasecmp(first_str.c_str(), second_str.c_str()) < 0);
57 }
58
59 }
60
61 CompareGroupName::CompareGroupName(const StringsValueType& group_name)
62   : group_name_(group_name) {
63 }
64
65 bool CompareGroupName::operator()(
66   const StringsValueType& group_name_to_compare) const {
67   const std::string gn_ = group_name_;
68   const std::string gn_compare = group_name_to_compare;
69   return !(strcasecmp(gn_.c_str(), gn_compare.c_str()));
70 }
71
72 bool operator!=(const policy_table::ApplicationParams& first,
73                 const policy_table::ApplicationParams& second) {
74   // TODO(AOleynik): Add comparing of Ford-specific and other parameters
75   if (first.groups.size() != second.groups.size()) {
76     return true;
77   }
78   StringsConstItr it_first = first.groups.begin();
79   StringsConstItr it_first_end = first.groups.end();
80   StringsConstItr it_second = second.groups.begin();
81   StringsConstItr it_second_end = second.groups.end();
82   for (; it_first != it_first_end; ++it_first) {
83     CompareGroupName gp(*it_first);
84     StringsConstItr it = std::find_if(it_second, it_second_end, gp);
85     if (it_first_end == it) {
86       return true;
87     }
88   }
89   return false;
90 }
91
92 CheckAppPolicy::CheckAppPolicy(
93   PolicyManagerImpl* pm, const utils::SharedPtr<policy_table::Table> update)
94   : pm_(pm),
95     update_(update) {
96 }
97
98 bool CheckAppPolicy::HasSameGroups(const AppPoliciesValueType& app_policy,
99                                    AppPermissions* perms) const {
100   const std::string app_id = app_policy.first;
101   AppPoliciesConstItr it = pm_->policy_table_snapshot_->policy_table
102                            .app_policies.find(app_id);
103
104   if (app_policy.second.is_string()) {
105     return (it->second.is_string() &&
106             app_policy.second.get_string().compare(it->second.get_string()) == 0);
107   } else {
108     if (it->second.is_string()) {
109       return false;
110     }
111   }
112
113   policy_table::Strings groups_new = app_policy.second.groups;
114   std::sort(groups_new.begin(), groups_new.end(), Compare);
115
116   policy_table::Strings groups_curr = (*it).second.groups;
117   std::sort(groups_curr.begin(), groups_curr.end(), Compare);
118
119   StringsConstItr it_groups_new = groups_new.begin();
120   StringsConstItr it_groups_new_end = groups_new.end();
121
122   StringsConstItr it_groups_curr = groups_curr.begin();
123   StringsConstItr it_groups_curr_end = groups_curr.end();
124
125   StringsConstItr new_it = it_groups_new;
126   StringsConstItr old_it = it_groups_curr;
127
128   std::pair<StringsConstItr, StringsConstItr> diff;
129
130   while (it_groups_new_end != new_it && it_groups_curr_end != old_it) {
131     size_t size = ((it_groups_new_end - new_it) > (it_groups_curr_end - old_it)) ? it_groups_curr_end - old_it : it_groups_new_end - new_it;
132     diff = std::mismatch(old_it, old_it + size, new_it, Match);
133     if (it_groups_curr_end == diff.first || it_groups_new_end == diff.second) {
134       new_it = diff.second;
135       old_it = diff.first;
136       break;
137     }
138     if (Compare(*diff.first, *diff.second)) {
139       perms->isAppPermissionsRevoked = true;
140       perms->appRevokedPermissions.push_back(*(diff.first));
141       old_it = ++diff.first;
142       new_it = diff.second;
143     } else {
144       perms->appPermissionsConsentNeeded = true;
145       old_it = diff.first;
146       new_it = ++diff.second;
147     }
148   }
149
150   for (StringsConstItr it = old_it; it != it_groups_curr_end; ++it) {
151     perms->isAppPermissionsRevoked = true;
152     perms->appRevokedPermissions.push_back(*it);
153   }
154
155   if (it_groups_new_end != new_it) {
156     perms->appPermissionsConsentNeeded = true;
157   }
158
159   return !(perms->appRevokedPermissions.size() > 0
160            || perms->appPermissionsConsentNeeded);
161 }
162
163 bool CheckAppPolicy::IsNewAppication(const std::string& application_id) const {
164   const policy_table::ApplicationPolicies& current_policies = pm_
165       ->policy_table_snapshot_->policy_table.app_policies;
166   AppPoliciesConstItr it_app_policies_curr = current_policies.begin();
167   AppPoliciesConstItr it_app_policies_curr_end = current_policies.end();
168
169   for (; it_app_policies_curr != it_app_policies_curr_end;
170        ++it_app_policies_curr) {
171
172     // Find necessary application in current snapshot
173     const std::string application_id_curr = (*it_app_policies_curr).first;
174     if (application_id == application_id_curr) {
175       return false;
176     }
177   }
178   return true;
179 }
180
181 void CheckAppPolicy::SendNotification(
182   const AppPoliciesValueType& app_policy) const {
183   // Collecting all available rpcs and their parameters from updated
184   // policies and fill notification data struct
185   Permissions notification_data;
186
187   // Get current user permissions for groups from DB
188   std::vector<FunctionalGroupPermission> group_permissons;
189   // Get current device_id from application id
190   const std::string device_id = pm_->GetCurrentDeviceId(app_policy.first);
191   if (device_id.empty()) {
192     LOG4CXX_WARN(logger_, "Couldn't find device info for application id "
193                  "'" << app_policy.first << "'");
194     return;
195   }
196   pm_->GetUserPermissionsForApp(device_id, app_policy.first, group_permissons);
197
198   pm_->PrepareNotificationData(update_->policy_table.functional_groupings,
199                                app_policy.second.groups,
200                                group_permissons, notification_data);
201
202   const std::string app_id = app_policy.first;
203   LOG4CXX_INFO(logger_, "Send notification for application_id:" << app_id);
204 #if defined (EXTENDED_POLICY)
205   pm_->listener()->OnPermissionsUpdated(app_id, notification_data,
206                                         policy_table::EnumToJsonString(app_policy.second.default_hmi));
207 #else
208   // Default_hmi is Ford-specific and should not be used with basic policy
209   const std::string default_hmi;
210   pm_->listener()->OnPermissionsUpdated(app_id, notification_data, default_hmi);
211 #endif
212 }
213
214 void CheckAppPolicy::SendOnPendingPermissions(
215   const AppPoliciesValueType& app_policy, AppPermissions permissions) const {
216   // TODO(AOleynik): Exclude default group(s)
217   if (permissions.appPermissionsConsentNeeded) {
218 #if defined(EXTENDED_POLICY)
219     const policy_table::Strings& groups = app_policy.second.groups;
220     // TODO(IKozyrenko): Check logic if optional container is missing
221     const policy_table::Strings& preconsented_groups = *app_policy.second
222         .preconsented_groups;
223
224     // TODO(KKolodiy): Use consent_groups to filtrate groups
225     PermissionsList list_of_permissions;
226     std::for_each(
227       groups.begin(), groups.end(),
228       FunctionalGroupInserter(preconsented_groups, list_of_permissions));
229     // TODO(PV): logic has changed.
230     if (!list_of_permissions.empty()) {
231       pm_->app_permissions_diff_.insert(
232         std::make_pair(app_policy.first, permissions));
233       pm_->listener()->OnPendingPermissionChange(app_policy.first);
234       return;
235     }
236 #endif
237   }
238   if (permissions.isAppPermissionsRevoked) {
239     pm_->app_permissions_diff_.insert(
240       std::make_pair(app_policy.first, permissions));
241     pm_->listener()->OnPendingPermissionChange(app_policy.first);
242   }
243 }
244
245 bool CheckAppPolicy::IsAppRevoked(
246   const AppPoliciesValueType& app_policy) const {
247   // Application params are not initialized = application revoked
248   // i.e. "123":null
249   return app_policy.second.is_null();
250 }
251
252 bool CheckAppPolicy::NicknamesMatch(const std::string app_id,
253                                     const AppPoliciesValueType& app_policy) const {
254   std::string app_name = pm_->listener()->GetAppName(app_id);
255   if (!app_name.empty() && app_policy.second.nicknames && !app_policy.second.nicknames->empty()) {
256     for (policy_table::Strings::const_iterator it = app_policy.second.nicknames->begin();
257          app_policy.second.nicknames->end() != it; ++it) {
258       std::string temp = *it;
259       if (temp.compare(app_name) == 0) {
260         return true;
261       }
262     }
263     return false;
264   }
265   return true;
266 }
267
268 bool CheckAppPolicy::operator()(const AppPoliciesValueType& app_policy) {
269   policy_table::ApplicationPolicies& current_policies = pm_
270       ->policy_table_snapshot_->policy_table.app_policies;
271
272   const std::string app_id = app_policy.first;
273
274   AppPermissions permissions_diff(app_id);
275 #if defined (EXTENDED_POLICY)
276   permissions_diff.priority = policy_table::EnumToJsonString(
277                                 app_policy.second.priority);
278 #endif
279
280   // Check revocation for any numeric application ids
281   if (atoi(app_id.c_str()) && IsAppRevoked(app_policy)) {
282     permissions_diff.appRevoked = true;
283     pm_->app_permissions_diff_.insert(std::make_pair(app_id, permissions_diff));
284     pm_->listener()->OnAppRevoked(app_id);
285     policy_table::ApplicationPolicies::iterator it = current_policies.find(
286           app_id);
287     // Update snapshot with new policies for application
288     if (it != current_policies.end()) {
289       (*it).second = app_policy.second;
290       it->second.set_to_null();
291     }
292     return true;
293   }
294
295   // TODO(PV): do we really need this check?
296   if (IsNewAppication(app_id)) {
297     // Update snapshot with policies for new application
298     current_policies[app_id] = app_policy.second;
299     SendNotification(app_policy);
300     SendOnPendingPermissions(app_policy, permissions_diff);
301     return true;
302   }
303
304   if (!NicknamesMatch(app_id, app_policy)) {
305     permissions_diff.appUnauthorized = true;
306     pm_->app_permissions_diff_.insert(std::make_pair(app_id, permissions_diff));
307     pm_->listener()->OnPendingPermissionChange(app_policy.first);
308     policy_table::ApplicationPolicies::iterator it = current_policies.find(
309           app_id);
310     // Update snapshot with new policies for application
311     if (it != current_policies.end()) {
312       (*it).second = app_policy.second;
313       it->second.set_to_null();
314     }
315     return true;
316   }
317
318   if (HasSameGroups(app_policy, &permissions_diff)) {
319     LOG4CXX_INFO(
320       logger_,
321       "Permissions for application:" << app_id << " wasn't changed.");
322     return true;
323   }
324
325   policy_table::ApplicationPolicies::iterator it = current_policies.find(
326         app_id);
327   // Update snapshot with new policies for application
328   (*it).second = app_policy.second;
329
330   // Don't sent notification for "default"
331   if (kDefaultId != app_id && kDeviceId != app_id) {
332     SendNotification(app_policy);
333     SendOnPendingPermissions(app_policy, permissions_diff);
334   }
335
336   // if 'device' was update with new/other func groups => user consent
337   // for device should be cleared.
338   if (kDeviceId == app_id) {
339 #if defined (EXTENDED_POLICY)
340     // TODO(AOleynik): Check design for more convenient access to policy ext data
341     PTExtRepresentation* pt_ext = dynamic_cast<PTExtRepresentation*>(pm_->policy_table_
342                                   .pt_data().get());
343     if (pt_ext) {
344       if (!pt_ext->ResetDeviceConsents()) {
345         return false;
346       }
347     }
348 #endif
349   }
350
351   return true;
352 }
353
354 FillNotificationData::FillNotificationData(Permissions& data,
355     GroupConsent group_state)
356   : data_(data) {
357   switch (group_state) {
358     case kGroupAllowed:
359       current_key_ = kAllowedKey;
360       break;
361     default:
362       current_key_ = kUserDisallowedKey;
363       break;
364   }
365 }
366
367 bool FillNotificationData::operator()(const RpcValueType& rpc) {
368   Permissions::iterator it = data_.find(rpc.first);
369   // If rpc is present already - update its permissions
370   if (data_.end() != it) {
371     UpdateHMILevels(rpc.second.hmi_levels,
372                     (*it).second.hmi_permissions[current_key_]);
373     // TODO(IKozyrenko): Check logic if optional container is missing
374     UpdateParameters(*rpc.second.parameters,
375                      (*it).second.parameter_permissions[current_key_]);
376     ExcludeDisAllowed();
377   } else {
378     // If rpc is not present - add its permissions
379     UpdateHMILevels(rpc.second.hmi_levels,
380                     data_[rpc.first].hmi_permissions[current_key_]);
381     // TODO(IKozyrenko): Check logic if optional container is missing
382     UpdateParameters(*rpc.second.parameters,
383                      data_[rpc.first].parameter_permissions[current_key_]);
384     ExcludeDisAllowed();
385   }
386   return true;
387 }
388
389 void FillNotificationData::UpdateHMILevels(
390   const policy_table::HmiLevels& in_hmi, std::set<HMILevel>& out_hmi) {
391   HMILevelsConstItr it_hmi_levels = in_hmi.begin();
392   HMILevelsConstItr it_hmi_levels_end = in_hmi.end();
393
394   for (; it_hmi_levels != it_hmi_levels_end; ++it_hmi_levels) {
395     out_hmi.insert(policy_table::EnumToJsonString(*it_hmi_levels));
396   }
397 }
398
399 void FillNotificationData::UpdateParameters(
400   const policy_table::Parameters& in_parameters,
401   std::set<Parameter>& out_parameter) {
402   ParametersConstItr it_parameters = in_parameters.begin();
403   ParametersConstItr it_parameters_end = in_parameters.end();
404
405   for (; it_parameters != it_parameters_end; ++it_parameters) {
406     out_parameter.insert(policy_table::EnumToJsonString(*it_parameters));
407   }
408 }
409
410 void FillNotificationData::ExcludeDisAllowed() {
411   // Search through filled data and remove permission from allowed, if it was
412   // found in disallowed section
413   Permissions::iterator it = data_.begin();
414   Permissions::const_iterator it_end = data_.end();
415   // Groups
416   for (; it != it_end; ++it) {
417     std::set<HMILevel>& allowed_hmi_level = (*it).second.hmi_permissions[kAllowedKey];
418     std::set<HMILevel>& disallowed_hmi_level =
419       (*it).second.hmi_permissions[kUserDisallowedKey];
420     std::set<HMILevel> diff_hmi;
421
422     std::set_difference(allowed_hmi_level.begin(), allowed_hmi_level.end(),
423                         disallowed_hmi_level.begin(), disallowed_hmi_level.end(),
424                         std::inserter(diff_hmi, diff_hmi.begin()));
425     // Leave levels, which are not present in disallowed
426     allowed_hmi_level = diff_hmi;
427
428     std::set<Parameter>& allowed_parameters =
429       (*it).second.parameter_permissions[kAllowedKey];
430     std::set<Parameter>& disallowed_parameters =
431       (*it).second.parameter_permissions[kUserDisallowedKey];
432
433     std::set<Parameter> diff_params;
434
435     std::set_difference(allowed_parameters.begin(), allowed_parameters.end(),
436                         disallowed_parameters.begin(), disallowed_parameters.end(),
437                         std::inserter(diff_params, diff_params.begin()));
438
439     // Leave parameters, which are not present in disallowed
440     allowed_parameters = diff_params;
441   }
442 }
443
444 ProcessFunctionalGroup::ProcessFunctionalGroup(
445   const policy_table::FunctionalGroupings& fg,
446   const std::vector<FunctionalGroupPermission>& group_permissions,
447   Permissions& data)
448   : fg_(fg),
449     group_permissions_(group_permissions),
450     data_(data) {
451 }
452
453 bool ProcessFunctionalGroup::operator()(const StringsValueType& group_name) {
454   const std::string group_name_str = group_name;
455   FuncGroupConstItr it = fg_.find(group_name_str);
456
457   if (fg_.end() != it) {
458     const policy_table::Rpc& rpcs = (*it).second.rpcs;
459     FillNotificationData filler(data_, GetGroupState(group_name_str));
460     std::for_each(rpcs.begin(), rpcs.end(), filler);
461   }
462   return true;
463 }
464
465 GroupConsent ProcessFunctionalGroup::GetGroupState(
466   const std::string& group_name) {
467   std::vector<FunctionalGroupPermission>::const_iterator it =
468     group_permissions_.begin();
469   std::vector<FunctionalGroupPermission>::const_iterator it_end =
470     group_permissions_.end();
471   for (; it != it_end; ++it) {
472     if (group_name == (*it).group_name) {
473       return (*it).state;
474     }
475   }
476   return kGroupUndefined;
477 }
478
479 FunctionalGroupInserter::FunctionalGroupInserter(
480   const policy_table::Strings& preconsented_groups, PermissionsList& list)
481   : list_(list),
482     preconsented_(preconsented_groups) {
483 }
484
485 void FunctionalGroupInserter::operator()(const StringsValueType& group_name) {
486   CompareGroupName name(group_name);
487   if (std::find_if(preconsented_.begin(), preconsented_.end(), name)
488       == preconsented_.end()) {
489     list_.push_back(group_name);
490   }
491 }
492
493 void FillFunctionalGroupPermissions(FunctionalGroupIDs& ids,
494                                     FunctionalGroupNames& names,
495                                     GroupConsent state,
496                                     std::vector<FunctionalGroupPermission>& permissions) {
497   FunctionalGroupIDs::const_iterator it = ids.begin();
498   FunctionalGroupIDs::const_iterator it_end = ids.end();
499   for (; it != it_end; ++it) {
500     FunctionalGroupPermission current_group;
501     current_group.group_id = *it;
502     current_group.group_alias = names[*it].first;
503     current_group.group_name = names[*it].second;
504     current_group.state = state;
505     permissions.push_back(current_group);
506   }
507 }
508
509 }