2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * @file ace_client.cpp
18 * @author Tomasz Swierczek (t.swierczek@samsung.com)
20 * @brief This file contains implementation of AceThinClient class
27 #include <dpl/optional.h>
28 #include <dpl/string.h>
29 #include <dpl/optional_typedefs.h>
30 #include <dpl/log/log.h>
31 #include <dpl/singleton_safe_impl.h>
32 #include <ace-dao-ro/PromptModel.h>
34 #include <ace_popup_handler.h>
36 #include "ace_server_api.h"
37 #include "popup_response_server_api.h"
38 #include "ace-client/ace_client.h"
39 #include "ace-client/ace_client_helper.h"
40 #include <attribute_facade.h>
41 #include <ace/Request.h>
43 // ACE tests need to use mock implementations
44 #ifdef ACE_CLIENT_TESTS
46 #include "AceDAOReadOnly_mock.h"
47 #include "communication_client_mock.h"
48 #include "PolicyInformationPoint_mock.h"
52 #include <ace-dao-ro/AceDAOReadOnly.h>
53 #include "SecurityCommunicationClient.h"
54 #include <ace/PolicyInformationPoint.h>
56 #endif // ACE_CLIENT_TESTS
58 IMPLEMENT_SAFE_SINGLETON(AceClient::AceThinClient)
60 ace_popup_handler_func_t popup_func = NULL;
65 // These devcaps actually are not requested in config file, so should be treaded
66 // as if were requested (access tags/WARP will block request if desired)
67 const std::string DEVCAP_EXTERNAL_NETWORK_ACCESS = "externalNetworkAccess";
68 const std::string DEVCAP_XML_HTTP_REQUEST = "XMLHttpRequest";
72 std::string AceFunctionParam::aceFunctionParamToken = "param:function";
76 enum class AceCachedPromptResult {
82 // AceThinClient implementation singleton
83 class AceThinClientImpl {
85 bool checkFunctionCall(const AceRequest& ace_request);
86 AcePreference getWidgetResourcePreference(
87 const AceResource& resource,
88 const AceWidgetHandle& handle) const;
89 AceResourcesPreferences* getGlobalResourcesPreferences() const;
90 bool isInitialized() const;
96 bool containsNetworkDevCap(const AceRequest &ace_request);
97 bool checkFeatureList(const AceRequest& ace_request);
99 WebRuntimeImpl* m_wrt;
100 ResourceInformationImpl* m_res;
101 OperationSystemImpl* m_sys;
102 WrtSecurity::Communication::Client *m_communicationClient, *m_popupValidationClient;
104 AceSubject getSubjectForHandle(AceWidgetHandle handle) const;
105 AceCachedPromptResult getCachedPromptResult(
106 WidgetHandle widgetHandle,
108 const AceSessionId& sessionId) const;
109 bool askUser(PolicyEffect popupType,
110 const AceRequest& ace_request,
111 const AceBasicRequest& request);
113 bool validatePopupResponse(
114 const AceRequest& ace_request,
115 const AceBasicRequest& request,
117 Prompt::Validity validity = Prompt::Validity::ALWAYS);
118 mutable PolicyInformationPoint m_pip;
119 DPL::Optional<std::set<DPL::String>> m_grantedDevCaps;
120 std::set<std::string> m_acceptedFeatures;
123 AceThinClientImpl::AceThinClientImpl()
124 : m_communicationClient(NULL),
125 m_popupValidationClient(NULL),
126 m_wrt(new WebRuntimeImpl()),
127 m_res(new ResourceInformationImpl()),
128 m_sys(new OperationSystemImpl()),
129 m_pip(m_wrt, m_res, m_sys)
131 AceDB::AceDAOReadOnly::attachToThreadRO();
133 m_communicationClient = new WrtSecurity::Communication::Client(WrtSecurity::AceServerApi::INTERFACE_NAME());
134 m_popupValidationClient = new WrtSecurity::Communication::Client(WrtSecurity::PopupServerApi::INTERFACE_NAME());
135 } Catch (WrtSecurity::Communication::Client::Exception::SecurityCommunicationClientException) {
136 if(m_communicationClient) delete m_communicationClient;
137 if(m_popupValidationClient) delete m_popupValidationClient;
141 ReThrowMsg(AceThinClient::Exception::AceThinClientException,
142 "Failed to call security daemon");
146 AceThinClientImpl::~AceThinClientImpl()
148 Assert(NULL != m_communicationClient);
149 Assert(NULL != m_popupValidationClient);
150 delete m_communicationClient;
151 delete m_popupValidationClient;
155 m_communicationClient = NULL;
156 m_popupValidationClient = NULL;
157 AceDB::AceDAOReadOnly::detachFromThread();
161 bool AceThinClientImpl::isInitialized() const
163 return NULL != m_communicationClient && NULL != m_popupValidationClient;
166 bool AceThinClientImpl::containsNetworkDevCap(const AceRequest &ace_request)
168 AceDeviceCap deviceCap = ace_request.deviceCapabilities;
169 for (size_t j=0; j<deviceCap.devcapsCount; ++j) {
170 if (!deviceCap.devCapNames[j]) {
173 if (DEVCAP_XML_HTTP_REQUEST == deviceCap.devCapNames[j]
174 || DEVCAP_EXTERNAL_NETWORK_ACCESS == deviceCap.devCapNames[j])
182 bool AceThinClientImpl::checkFeatureList(const AceRequest& ace_request)
184 for (size_t i=0; i<ace_request.apiFeatures.count; ++i) {
185 Assert(ace_request.apiFeatures.apiFeature[i]);
186 std::string featureName(ace_request.apiFeatures.apiFeature[i]);
187 LogInfo("Api feature: " << featureName);
188 if (0 != m_acceptedFeatures.count(featureName)) {
191 LogInfo("Api-feature was not requested in widget config: " <<
197 bool AceThinClientImpl::checkFunctionCall(const AceRequest& ace_request)
201 // fill the m_grantedDevCaps, if not yet initialized
202 // TODO: This is not so pretty. AceThinClient is not explicitly
203 // tied to a widget handle, yet we assume it is always used
204 // with the same handle. This will be amended in a future
205 // refactoring (already planned).
206 if (m_grantedDevCaps.IsNull()) {
207 m_grantedDevCaps = std::set<DPL::String>();
208 m_acceptedFeatures.clear();
210 AceDB::FeatureNameVector fvector;
211 AceDB::AceDAOReadOnly::getAcceptedFeature(ace_request.widgetHandle, &fvector);
212 for(size_t i=0; i<fvector.size(); ++i) {
213 m_acceptedFeatures.insert(DPL::ToUTF8String(fvector[i]));
217 AceSubject subject = getSubjectForHandle(ace_request.widgetHandle);
219 // Create function params
220 const AceDeviceCap& devcaps = ace_request.deviceCapabilities;
222 LogInfo("Checking against config requested api-features.");
224 // Network device caps are not connected with api-features.
225 // We must pass empty api-feature when network dev cap is set.
226 if (!containsNetworkDevCap(ace_request) && !checkFeatureList(ace_request)) {
230 AceFunctionParams functionParams(devcaps.devcapsCount);
231 for (size_t i = 0; i < devcaps.devcapsCount; ++i) {
232 AceFunctionParam functionParam;
233 functionParam.addAttribute(AceFunctionParam::aceFunctionParamToken,
234 NULL == ace_request.functionName ?
235 "" : ace_request.functionName);
236 if (devcaps.paramsCount) {
237 Assert(devcaps.params);
238 for (size_t j = 0; j < devcaps.params[i].count; ++j) {
239 Assert(devcaps.params[i].param &&
240 devcaps.params[i].param[j].name &&
241 devcaps.params[i].param[j].value);
242 functionParam.addAttribute(
243 std::string(devcaps.params[i].param[j].name),
244 std::string(devcaps.params[i].param[j].value));
247 functionParams.push_back(functionParam);
250 // Convert AceRequest to array of AceBasicRequests
251 AceBasicRequests requests;
253 for (size_t i = 0; i < devcaps.devcapsCount; ++i) {
254 // Adding dev cap name here as resource id
255 Assert(devcaps.devCapNames[i]);
256 LogInfo("Device cap: " << devcaps.devCapNames[i]);
257 AceBasicRequest request(subject,
258 devcaps.devCapNames[i],
260 requests.push_back(request);
263 // true means access granted, false - denied
266 FOREACH(it, requests){
267 // Getting attributes from ACE DAO
268 AceBasicRequest& request = *it;
269 AceDB::BaseAttributeSet attributeSet;
270 AceDB::AceDAOReadOnly::getAttributes(&attributeSet);
272 // If true, we need to make popup IPC and ask user for decision
273 bool askPopup = false;
274 // If true, we need to make IPC to security daemon for policy
275 // decision on granting access
276 bool askServer = false;
277 // If askPopup == true, this is the kind of popup to be opened
278 PolicyEffect popupType = PolicyEffect::PROMPT_ONESHOT;
280 if (attributeSet.empty()) {
281 // Treat this case as missed cache - ask security daemon
282 LogInfo("Empty attribute set");
285 // Filling attributes with proper values
286 FunctionParamImpl params;
287 AceParamKeys keys = request.getFunctionParam().getKeys();
288 AceParamValues values = request.getFunctionParam().getValues();
289 for (size_t i = 0; i < keys.size(); ++i) {
290 params.addAttribute(keys[i], values[i]);
292 Request req(ace_request.widgetHandle,
293 WidgetExecutionPhase_Invoke,
295 req.addDeviceCapability(request.getResourceId());
297 m_pip.getAttributesValues(&req, &attributeSet);
299 // Getting cached policy result
300 OptionalExtendedPolicyResult exPolicyResult =
301 AceDB::AceDAOReadOnly::getPolicyResult(attributeSet);
303 if (exPolicyResult.IsNull()) {
304 // Missed cache - ask security daemon
305 LogInfo("Missed policy result cache");
308 // Cached value found - now interpret it
309 LogInfo("Result in cache");
310 OptionalPolicyEffect effect = exPolicyResult->policyResult.getEffect();
311 if (effect.IsNull()) {
312 // PolicyDecision is UNDETERMINED or NOT_APPLICABLE
315 } else if (*effect == PolicyEffect::DENY) {
319 } else if (*effect == PolicyEffect::PERMIT) {
321 if (m_grantedDevCaps->find(
322 DPL::FromASCIIString(request.getResourceId()))
323 != m_grantedDevCaps->end())
329 // Check for cached popup response
330 LogInfo("Checking cached popup response");
331 AceCachedPromptResult promptCached =
332 getCachedPromptResult(ace_request.widgetHandle,
333 exPolicyResult->ruleId,
334 ace_request.sessionId);
335 if (promptCached == AceCachedPromptResult::PERMIT) {
336 // Granted by previous popup
337 LogDebug("Cache found OK");
338 if (m_grantedDevCaps->find(
339 DPL::FromASCIIString(request.getResourceId()))
340 != m_grantedDevCaps->end())
342 LogDebug("SMACK given previously");
345 if (*effect != PolicyEffect::PROMPT_BLANKET) {
346 // This should not happen.
347 LogDebug("This should not happen.");
351 if (!validatePopupResponse(ace_request,
353 LogDebug("Daemon has not validated response.");
357 // Access granted, move on to next request
358 LogDebug("SMACK granted, all OK");
359 m_grantedDevCaps->insert(
360 DPL::FromASCIIString(
361 request.getResourceId()));
366 if (promptCached == AceCachedPromptResult::DENY) {
367 // Access denied by earlier popup
371 if (promptCached == AceCachedPromptResult::ASK_POPUP) {
380 // IPC to security daemon
381 // here we must check if we have a SMACK permission for
382 // the device cap requested
383 LogInfo("Asking security daemon");
384 int serializedPolicyResult = 0;
386 m_communicationClient->call(WrtSecurity::AceServerApi::CHECK_ACCESS_METHOD(),
387 ace_request.widgetHandle,
388 request.getSubjectId(),
389 request.getResourceId(),
390 request.getFunctionParam().getKeys(),
391 request.getFunctionParam().getValues(),
392 ace_request.sessionId,
393 &serializedPolicyResult);
394 } Catch (WrtSecurity::Communication::Client::Exception::SecurityCommunicationClientException) {
395 ReThrowMsg(AceThinClient::Exception::AceThinClientException,
396 "Failed to call security daemon");
398 PolicyResult policyResult = PolicyResult::
399 deserialize(serializedPolicyResult);
400 OptionalPolicyEffect effect = policyResult.getEffect();
401 if (effect.IsNull()) {
402 // PolicyDecision is UNDETERMINED or NOT_APPLICABLE
406 if (*effect == PolicyEffect::DENY) {
411 if (*effect == PolicyEffect::PERMIT) {
412 // Access granted, move on to next request
413 m_grantedDevCaps->insert(
414 DPL::FromASCIIString(request.getResourceId()));
418 // Policy says: ask user - setup popup kind
424 result = askUser(popupType, ace_request, request);
427 LogInfo("Result: " << (result ? "GRANTED" : "DENIED"));
431 bool AceThinClientImpl::askUser(PolicyEffect popupType,
432 const AceRequest& ace_request,
433 const AceBasicRequest& request)
435 LogInfo("Asking popup");
436 Assert(NULL != popup_func);
438 const AceFunctionParam& fParam = request.getFunctionParam();
439 AceParamKeys keys = fParam.getKeys();
440 AceParamValues values = fParam.getValues();
442 ace_popup_t ace_popup_type;
443 ace_resource_t resource = const_cast<ace_session_id_t>(
444 request.getResourceId().c_str());
445 ace_session_id_t session = const_cast<ace_session_id_t>(
446 ace_request.sessionId.c_str());;
447 ace_param_list_t parameters;
448 ace_widget_handle_t handle = ace_request.widgetHandle;
450 parameters.count = keys.size();
451 parameters.items = new ace_param_t[parameters.count];
453 for (i = 0; i < parameters.count; ++i) {
454 parameters.items[i].name =
455 const_cast<ace_string_t>(keys[i].c_str());
456 parameters.items[i].value =
457 const_cast<ace_string_t>(values[i].c_str());
461 case PolicyEffect::PROMPT_ONESHOT: {
462 ace_popup_type = ACE_ONESHOT;
464 case PolicyEffect::PROMPT_SESSION: {
465 ace_popup_type = ACE_SESSION;
467 case PolicyEffect::PROMPT_BLANKET: {
468 ace_popup_type = ACE_BLANKET;
471 LogError("Unknown popup type passed!");
472 LogError("Maybe effect isn't a popup?");
473 LogError("Effect number is: " << static_cast<int>(popupType));
477 ace_bool_t answer = ACE_FALSE;
478 ace_return_t ret = popup_func(ace_popup_type,
485 delete [] parameters.items;
488 LogError("Error in popup handler");
492 if (ACE_TRUE == answer) {
493 m_grantedDevCaps->insert(
494 DPL::FromASCIIString(request.getResourceId()));
501 bool AceThinClientImpl::validatePopupResponse(
502 const AceRequest& ace_request,
503 const AceBasicRequest& request,
505 Prompt::Validity validity
508 bool response = false;
510 m_popupValidationClient->call(
511 WrtSecurity::PopupServerApi::VALIDATION_METHOD(),
513 static_cast<int>(validity),
514 ace_request.widgetHandle,
515 request.getSubjectId(),
516 request.getResourceId(),
517 request.getFunctionParam().getKeys(),
518 request.getFunctionParam().getValues(),
519 ace_request.sessionId,
521 } Catch (WrtSecurity::Communication::Client::Exception::SecurityCommunicationClientException) {
522 ReThrowMsg(AceThinClient::Exception::AceThinClientException,
523 "Failed to call security daemon");
528 AcePreference AceThinClientImpl::getWidgetResourcePreference (
529 const AceResource& resource,
530 const AceWidgetHandle& handle) const
532 return toAcePreference(
533 AceDB::AceDAOReadOnly::getWidgetDevCapSetting(resource, handle));
536 AceResourcesPreferences* AceThinClientImpl::getGlobalResourcesPreferences()
539 AceDB::PreferenceTypesMap globalSettingsMap;
540 AceResourcesPreferences* acePreferences = new AceResourcesPreferences();
541 AceDB::AceDAOReadOnly::getDevCapSettings(&globalSettingsMap);
542 FOREACH(it, globalSettingsMap) {
543 acePreferences->insert(
544 AceResurcePreference((*it).first,
545 toAcePreference((*it).second)));
547 return acePreferences;
550 AceSubject AceThinClientImpl::getSubjectForHandle(AceWidgetHandle handle) const
554 return AceDB::AceDAOReadOnly::getGUID(handle);
556 catch (AceDB::AceDAOReadOnly::Exception::DatabaseError& /*ex*/)
558 LogError("Couldn't find GIUD for handle " << handle);
563 AceCachedPromptResult AceThinClientImpl::getCachedPromptResult(
564 WidgetHandle widgetHandle,
566 const AceSessionId& sessionId) const
568 OptionalCachedPromptDecision promptDecision =
569 AceDB::AceDAOReadOnly::getPromptDecision(
572 if (promptDecision.IsNull()) {
573 LogDebug("No cache");
574 return AceCachedPromptResult::ASK_POPUP;
576 // These should not be stored in DB!
577 Assert(PromptDecision::ALLOW_THIS_TIME
578 != (*promptDecision).decision);
579 Assert(PromptDecision::DENY_THIS_TIME
580 != (*promptDecision).decision);
581 if ((*promptDecision).decision ==
582 PromptDecision::ALLOW_ALWAYS) {
583 // Access granted via earlier popup
584 LogDebug("ALLOW_ALWAYS");
585 return AceCachedPromptResult::PERMIT;
587 if ((*promptDecision).decision ==
588 PromptDecision::DENY_ALWAYS) {
589 LogDebug("DENY_ALWAYS");
590 // Access denied via earlier popup
591 return AceCachedPromptResult::DENY;
593 // Only thing left is per session prompts
594 if ((*promptDecision).session.IsNull()) {
595 LogDebug("NO SESSION");
596 return AceCachedPromptResult::ASK_POPUP;
598 AceSessionId cachedSessionId = DPL::ToUTF8String(*((*promptDecision).session));
599 if ((*promptDecision).decision ==
600 PromptDecision::ALLOW_FOR_SESSION) {
601 if (cachedSessionId == sessionId) {
602 // Access granted for this session.
603 LogDebug("SESSION OK, PERMIT");
604 return AceCachedPromptResult::PERMIT;
606 LogDebug("SESSION NOT OK, ASKING");
607 return AceCachedPromptResult::ASK_POPUP;
610 if ((*promptDecision).decision ==
611 PromptDecision::DENY_FOR_SESSION) {
612 if (cachedSessionId == sessionId) {
613 // Access denied for this session.
614 LogDebug("SESSION OK, DENY");
615 return AceCachedPromptResult::DENY;
617 LogDebug("SESSION NOT OK, ASKING");
618 return AceCachedPromptResult::ASK_POPUP;
622 LogDebug("NO RESULT, ASKING");
623 return AceCachedPromptResult::ASK_POPUP;
628 bool AceThinClient::checkFunctionCall(
629 const AceRequest& ace_request) const
631 return m_impl->checkFunctionCall(ace_request);
634 AcePreference AceThinClient::getWidgetResourcePreference(
635 const AceResource& resource,
636 const AceWidgetHandle& handle) const
638 return m_impl->getWidgetResourcePreference(
642 AceResourcesPreferences* AceThinClient::getGlobalResourcesPreferences()
645 return m_impl->getGlobalResourcesPreferences();
648 AceThinClient::AceThinClient()
650 m_impl = new AceThinClientImpl();
653 AceThinClient::~AceThinClient()
655 Assert(NULL != m_impl);
659 bool AceThinClient::isInitialized() const
661 return NULL != m_impl && m_impl->isInitialized();
665 } // namespace AceClient