From e3efd9596feaa49a11a0108c3aead76e92747872 Mon Sep 17 00:00:00 2001 From: Tomasz Swierczek Date: Tue, 22 May 2018 09:28:33 +0200 Subject: [PATCH] Adjust server & client logic to handle many privileges in input This commit is a preparation for new API with many privileges as input. Change-Id: I9964bbdb1fa81211bdd0117e48a5462604407d17 Signed-off-by: Ernest Borowski --- src/capi/impl/privacy_privilege_manager.c | 109 +++++++++++-- src/client/api/ApiInterface.h | 5 + src/client/api/askuser-notification-client.cpp | 60 +++++++ src/client/impl/ApiInterfaceImpl.cpp | 103 +++++++++--- src/client/impl/ApiInterfaceImpl.h | 32 +++- src/client/impl/ClientCallbacks.cpp | 2 +- src/client/include/askuser-notification-client.h | 107 ++++++++++++- src/notification-daemon/Logic.cpp | 195 ++++++++++++++--------- src/notification-daemon/Logic.h | 23 ++- src/notification-daemon/ServerCallbacks.cpp | 2 +- 10 files changed, 512 insertions(+), 126 deletions(-) diff --git a/src/capi/impl/privacy_privilege_manager.c b/src/capi/impl/privacy_privilege_manager.c index 7545252..a99ea09 100644 --- a/src/capi/impl/privacy_privilege_manager.c +++ b/src/capi/impl/privacy_privilege_manager.c @@ -18,6 +18,7 @@ * @file privacy_privilege_manager.c * @author Piotr Sawicki * @author Ernest Borowski + * @author Tomasz Swierczek * @brief The implementation of Privacy Privilege Manager CAPI. */ @@ -48,6 +49,12 @@ struct ppm_callback_closure_s { }; typedef struct ppm_callback_closure_s ppm_callback_closure; +struct ppm_multiple_callback_closure_s { + void *user_data; + ppm_request_multiple_response_cb callback; +}; +typedef struct ppm_multiple_callback_closure_s ppm_multiple_callback_closure; + static ppm_private *ppm_handle = NULL; static void ppm_free_channel(gpointer data) @@ -220,6 +227,41 @@ static void ppm_popup_response_callback(UNUSED int request_id, askuser_call_caus free(callback_closure); } +static void ppm_popup_multiple_response_callback(UNUSED int request_id, askuser_call_cause cause, + const askuser_popup_result *results, const char **privileges, + size_t privileges_count, void *p_user_data) +{ + ppm_multiple_callback_closure *callback_closure = (ppm_multiple_callback_closure *) p_user_data; + + /* Don't invoke callback while the application is finishing. The user data + * may already have been destroyed. + */ + if (cause == ASKUSER_CALL_CAUSE_FINALIZE) { + free(callback_closure); + return; + } + + ppm_call_cause_e ppm_cause = askuser_client_request_cause_to_ppm(cause); + + ppm_request_result_e *ppm_results = malloc(privileges_count * sizeof(ppm_request_result_e)); + + if (!ppm_results) { + callback_closure->callback(ASKUSER_CALL_CAUSE_ERROR, NULL, privileges, privileges_count, callback_closure->user_data); + free(callback_closure); + return; + } + + for (size_t iterator = 0; iterator < privileges_count; iterator++) { + ppm_results[iterator] = askuser_client_request_result_to_ppm(results[iterator]); + } + + callback_closure->callback(ppm_cause, ppm_results, privileges, privileges_count, callback_closure->user_data); + + free(ppm_results); + free(callback_closure); +} + + static void ppm_free_client() { if (ppm_handle != NULL) { @@ -295,10 +337,37 @@ EXPORT_API int ppm_check_permissions(const char **privileges, size_t privileges_count, ppm_check_result_e *results) { - (void)privileges; - (void)privileges_count; - (void)results; - return PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN; + if (!privileges_count || !privileges || !results) { + return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER; + } + for (size_t iterator = 0; iterator < privileges_count; iterator++) { + if (!privileges[iterator]) { + return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER; + } + } + + int ret = ppm_init_client(); + if (ret != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) { + return ret; + } + + askuser_check_result *check_results = malloc(privileges_count * sizeof(askuser_check_result)); + if (!check_results) { + return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY; + } + + ret = askuser_client_check_privileges(ppm_handle->client, privileges, privileges_count, check_results); + if (ret != ASKUSER_API_SUCCESS) { + free(check_results); + return ask_user_to_ppm_error(ret); + } + + for (size_t iterator = 0; iterator < privileges_count; iterator++) { + results[iterator] = ask_user_check_result_to_ppm(check_results[iterator]); + } + + free(check_results); + return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE; } EXPORT_API @@ -336,9 +405,31 @@ int ppm_request_permissions(const char **privileges, size_t privileges_count, ppm_request_multiple_response_cb callback, void *user_data) { - (void)privileges; - (void)privileges_count; - (void)callback; - (void)user_data; - return PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN; + if (!privileges_count || !privileges || !callback) { + return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER; + } + + int ret = ppm_init_client(); + if (ret != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) { + return ret; + } + + ppm_multiple_callback_closure *callback_closure = (ppm_multiple_callback_closure *) + calloc(1, sizeof(ppm_multiple_callback_closure)); + if(callback_closure == NULL) { + return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY; + } + + callback_closure->callback = callback; + callback_closure->user_data = user_data; + + ret = askuser_client_popup_multiple_request(ppm_handle->client, privileges, privileges_count, + ppm_popup_multiple_response_callback, + callback_closure, NULL); + if (ret != ASKUSER_API_SUCCESS) { + free(callback_closure); + return ask_user_to_ppm_error(ret); + } + + return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE; } diff --git a/src/client/api/ApiInterface.h b/src/client/api/ApiInterface.h index c1b5d2c..2f6fd80 100644 --- a/src/client/api/ApiInterface.h +++ b/src/client/api/ApiInterface.h @@ -17,12 +17,14 @@ /** * @file ApiInterface.h * @author Piotr Sawicki + * @author Ernest Borowski * @brief This file contains the declaration of ApiInterface. */ #pragma once #include +#include #include @@ -42,6 +44,9 @@ public: virtual RequestId popupRequest(const std::string &privilege, const askuser_popup_response_callback callback, void *userData) = 0; + virtual RequestId popupRequest(const std::vector &privileges, + const askuser_popup_multiple_response_callback callback, + void *userData) = 0; virtual bool popupRequestInProgress(const std::string &privilege) const = 0; }; diff --git a/src/client/api/askuser-notification-client.cpp b/src/client/api/askuser-notification-client.cpp index 1067816..5da69fa 100644 --- a/src/client/api/askuser-notification-client.cpp +++ b/src/client/api/askuser-notification-client.cpp @@ -17,15 +17,19 @@ /** * @file askuser-notification-client.cpp * @author Piotr Sawicki + * @author Ernest Borowski * @brief This file contains the implementation of the askuser-notification client API. */ #include #include +#include +#include #include #include #include +#include #include #include @@ -111,6 +115,24 @@ int askuser_client_check_privilege(askuser_client *p_client, } API +int askuser_client_check_privileges(askuser_client *p_client, const char **privileges, + size_t privileges_count, askuser_check_result *p_results) +{ + if (!p_client || !privileges || !p_results || + privileges_count == 0 || privileges_count > AskUser::Protocol::MAX_PRIVS_NUMBER) { + return ASKUSER_API_INVALID_PARAM; + } + + int ret = ASKUSER_API_SUCCESS; + for (size_t i = 0; i < privileges_count; ++i) { + ret = askuser_client_check_privilege(p_client, privileges[i], &(p_results[i])); + if (ret != ASKUSER_API_SUCCESS) + break; + } + return ret; +} + +API int askuser_client_popup_request(askuser_client *p_client, const char *privilege, askuser_popup_response_callback response_callback, void *p_user_data, int *p_request_id) @@ -137,3 +159,41 @@ int askuser_client_popup_request(askuser_client *p_client, const char *privilege return ASKUSER_API_SUCCESS; }); } + +API +int askuser_client_popup_multiple_request(askuser_client *p_client, const char **privileges, + size_t privileges_count, + askuser_popup_multiple_response_callback response_callback, + void *p_user_data, int *p_request_id) +{ + if (!privileges_count || !p_client || !privileges || !response_callback) { + return ASKUSER_API_INVALID_PARAM; + } + + return AskUser::Client::tryCatch([&]() { + std::vector privilegeVector; + for (size_t it = 0 ; it < privileges_count; it++) { + if (!privileges[it] || std::strlen(privileges[it]) == 0) { + return ASKUSER_API_INVALID_PARAM; + } + if (p_client->impl->popupRequestInProgress(privileges[it])) { + return ASKUSER_API_ALREADY_IN_PROGRESS; + } + privilegeVector.push_back(privileges[it]); + } + + std::unordered_set uniquePrivileges(privilegeVector.begin(), privilegeVector.end()); + if (privileges_count != uniquePrivileges.size()) { + ALOGE("Privileges passed to multiple request are not unique."); + return ASKUSER_API_INVALID_PARAM; + } + AskUser::Client::RequestId id = p_client->impl->popupRequest(privilegeVector, response_callback, p_user_data); + + if (p_request_id) { + *p_request_id = id; + } + + return ASKUSER_API_SUCCESS; + }); + +} diff --git a/src/client/impl/ApiInterfaceImpl.cpp b/src/client/impl/ApiInterfaceImpl.cpp index f7972ef..5b6ed44 100644 --- a/src/client/impl/ApiInterfaceImpl.cpp +++ b/src/client/impl/ApiInterfaceImpl.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -63,12 +64,13 @@ inline askuser_popup_result responseToAskUserPopupResult(int response) return ASKUSER_POPUP_RESULT_DENY_ONCE; } -inline askuser_call_cause deduceCauseFromResponse(int response) +inline askuser_call_cause deduceCauseFromResponses(const std::vector &responses) { - if (response == ASKUSER_UNKNOWN_ERROR) { - return ASKUSER_CALL_CAUSE_ERROR; + for (const auto response : responses) { + if (response == ASKUSER_UNKNOWN_ERROR) { + return ASKUSER_CALL_CAUSE_ERROR; + } } - return ASKUSER_CALL_CAUSE_ANSWER; } @@ -125,8 +127,23 @@ RequestId ApiInterfaceImpl::popupRequest(const std::string &privilege, const askuser_popup_response_callback callback, void *userData) { - std::vector privileges({privilege}); - Protocol::ConnectionContext conCtx = m_channel->popupRequest(std::move(privileges)); + return popupRequestInternal({privilege}, NULL, callback, userData); +} + +RequestId ApiInterfaceImpl::popupRequest(const std::vector &privileges, + const askuser_popup_multiple_response_callback callback, + void *userData) +{ + return popupRequestInternal(privileges, callback, NULL, userData); +} + + +RequestId ApiInterfaceImpl::popupRequestInternal(const std::vector &privileges, + const askuser_popup_multiple_response_callback multipleCallback, + const askuser_popup_response_callback singleCallback, + void *userData) +{ + Protocol::ConnectionContext conCtx = m_channel->popupRequest(privileges); auto sameRequest = [&] (const Request &req) { return req.m_requestId == conCtx.m_requestId; @@ -135,15 +152,12 @@ RequestId ApiInterfaceImpl::popupRequest(const std::string &privilege, auto reqIt = std::find_if(m_requests.begin(), m_requests.end(), sameRequest); if (reqIt != m_requests.end()) { ALOGE("Popup closure exists for id: " << conCtx.m_requestId << - " privilege: " << reqIt->m_privilege << ", replacing"); - popupResponse(conCtx.m_requestId, ASKUSER_UNKNOWN_ERROR); + " Request: " << *reqIt << ", replacing"); + std::vector responses(reqIt->m_privileges.size(), ASKUSER_UNKNOWN_ERROR); + popupResponses(conCtx.m_requestId, responses); } - if (popupRequestInProgress(privilege)) { - ALOGE("Privilege " << privilege << " already exists in the pending requests"); - } - - m_requests.push_back({ conCtx.m_requestId, conCtx.m_fd, privilege, callback, userData }); + m_requests.push_back({ conCtx.m_requestId, conCtx.m_fd, privileges, multipleCallback, singleCallback, userData }); return conCtx.m_requestId; } @@ -151,7 +165,10 @@ RequestId ApiInterfaceImpl::popupRequest(const std::string &privilege, bool ApiInterfaceImpl::popupRequestInProgress(const std::string &privilege) const { auto samePrivilege = [&] (const Request &req) { - return req.m_privilege == privilege; + auto result = std::find(req.m_privileges.begin(), req.m_privileges.end(), privilege); + if (result != req.m_privileges.end()) + return true; + return false; }; return std::find_if(m_requests.begin(), m_requests.end(), samePrivilege) != m_requests.end(); @@ -170,16 +187,53 @@ void ApiInterfaceImpl::updateConnection(Protocol::ConnectionFd fd, int mask) if (reqIt == m_requests.end()) { return; } - ALOGW("askuser-notification has been unexpectedly stopped, " - "sending the error response for privilege: " - << reqIt->m_privilege << " id: " << reqIt->m_requestId); + "sending the error response for request: " << *reqIt); + + std::vector responses(reqIt->m_privileges.size(), ASKUSER_UNKNOWN_ERROR); - popupResponse(reqIt->m_requestId, ASKUSER_UNKNOWN_ERROR); + popupResponses(reqIt->m_requestId, responses); } } -void ApiInterfaceImpl::popupResponse(Protocol::RequestId id, int response) +void ApiInterfaceImpl::popupResponsesHelper(Protocol::RequestId id, const Request &request, + askuser_call_cause cause, const std::vector &responses) +{ + if (request.m_singleCallback) { + if (responses.empty()) { + ALOGE("Responses are empty"); + request.m_singleCallback(id, ASKUSER_CALL_CAUSE_ERROR, + responseToAskUserPopupResult(ASKUSER_DENY_ONCE), NULL, request.m_userData); + return; + } + if (responses.size() > 1) { + ALOGE("Responses count: " + std::to_string(responses.size()) + " should be 1."); + } + request.m_singleCallback(id, cause, responseToAskUserPopupResult(responses[0]), + request.m_privileges[0].c_str(), request.m_userData); + return; + } + + size_t dataSize = responses.size(); + if (dataSize != request.m_privileges.size()) { + ALOGE("Size mismatched: responses.size() " << responses.size() << " != privileges.size() " + << request.m_privileges.size()); + request.m_multipleCallback(id, ASKUSER_CALL_CAUSE_ERROR, NULL, NULL, 0, request.m_userData); + return; + } + std::vector privileges; + privileges.reserve(dataSize); + std::transform(request.m_privileges.begin(), request.m_privileges.end(), std::back_inserter(privileges), + [](const std::string &priv) -> const char* { return priv.c_str(); }); + + std::vector results; + results.reserve(dataSize); + std::transform(responses.begin(), responses.end(), std::back_inserter(results), + [](int response) -> askuser_popup_result { return responseToAskUserPopupResult(response); }); + + request.m_multipleCallback(id, cause, results.data(), privileges.data(), dataSize, request.m_userData); +} +void ApiInterfaceImpl::popupResponses(Protocol::RequestId id, const std::vector &responses) { auto sameRequestId = [&] (const Request &req) { return req.m_requestId == id; @@ -191,19 +245,16 @@ void ApiInterfaceImpl::popupResponse(Protocol::RequestId id, int response) return; } - askuser_call_cause cause = deduceCauseFromResponse(response); - askuser_popup_result res = responseToAskUserPopupResult(response); - - reqIt->m_callback(id, cause, res, reqIt->m_privilege.c_str(), reqIt->m_userData); - + askuser_call_cause cause = deduceCauseFromResponses(responses); + popupResponsesHelper(id, *reqIt, cause, responses); m_requests.erase(reqIt); } void ApiInterfaceImpl::respondToAllRequests(askuser_call_cause cause, askuser_popup_result result) { for (const auto &req : m_requests) { - req.m_callback(req.m_requestId, cause, result, - req.m_privilege.c_str(), req.m_userData); + std::vector results(req.m_privileges.size(), result); + popupResponsesHelper(req.m_requestId, req, cause, results); } m_requests.clear(); diff --git a/src/client/impl/ApiInterfaceImpl.h b/src/client/impl/ApiInterfaceImpl.h index 34e5cee..1c829a4 100644 --- a/src/client/impl/ApiInterfaceImpl.h +++ b/src/client/impl/ApiInterfaceImpl.h @@ -17,12 +17,16 @@ /** * @file ApiInterfaceImpl.h * @author Piotr Sawicki + * @author Ernest Borowski * @brief The declaration of ApiInterfaceImpl. */ #pragma once +#include #include +#include +#include #include #include @@ -51,25 +55,45 @@ public: virtual RequestId popupRequest(const std::string &privilege, const askuser_popup_response_callback callback, void *userData); + virtual RequestId popupRequest(const std::vector &privileges, + const askuser_popup_multiple_response_callback callback, + void *userData); virtual bool popupRequestInProgress(const std::string &privilege) const; void updateConnection(Protocol::ConnectionFd fd, int mask); - void popupResponse(Protocol::RequestId id, int response); + void popupResponses(Protocol::RequestId id, const std::vector &responses); private: + RequestId popupRequestInternal(const std::vector &privileges, + const askuser_popup_multiple_response_callback multipleCallback, + const askuser_popup_response_callback singleCallback, + void *userData); + void respondToAllRequests(askuser_call_cause cause, askuser_popup_result result); struct Request { Protocol::RequestId m_requestId; Protocol::ConnectionFd m_fd; - std::string m_privilege; - askuser_popup_response_callback m_callback; + std::vector m_privileges; + askuser_popup_multiple_response_callback m_multipleCallback; + askuser_popup_response_callback m_singleCallback; void *m_userData; + + friend std::ostream& operator << (std::ostream &os, const Request &request) { + os << "ConnectionFd: " << request.m_fd << " RequestId: " << request.m_requestId << + " N: " << request.m_privileges.size() << " privileges: "; + for (const auto &privilege : request.m_privileges) + os << privilege << " "; + return os; + } }; + void popupResponsesHelper(Protocol::RequestId id, const Request &request, + askuser_call_cause cause, const std::vector &responses); + StatusCallbackClosure m_statusClosure; std::unique_ptr m_channel; - std::vector m_requests; + std::list m_requests; AulAppInfo m_appInfo; AulPkgInfo m_pkgInfo; }; diff --git a/src/client/impl/ClientCallbacks.cpp b/src/client/impl/ClientCallbacks.cpp index 5ac1551..7d8f91f 100644 --- a/src/client/impl/ClientCallbacks.cpp +++ b/src/client/impl/ClientCallbacks.cpp @@ -34,7 +34,7 @@ void ClientCallbacks::updateConnection(Protocol::ConnectionFd fd, int mask) void ClientCallbacks::popupResponses(Protocol::RequestId id, const std::vector &responses) { - m_api->popupResponse(id, responses[0]); + m_api->popupResponses(id, responses); } } // namespace Client diff --git a/src/client/include/askuser-notification-client.h b/src/client/include/askuser-notification-client.h index 8398fdd..8175516 100644 --- a/src/client/include/askuser-notification-client.h +++ b/src/client/include/askuser-notification-client.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2017 - 2018 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ /** * @file askuser-notification-client.h * @author Piotr Sawicki + * @author Ernest Borowski * @brief The declaration of the askuser-notification client API. */ @@ -213,6 +214,33 @@ typedef void (*askuser_popup_response_callback) (int request_id, void *p_user_data); /** + * \brief This callback function is called when a client receives a response + * upon calling askuser_client_popup_multiple_request(). + * + * \attention This callback is called from the askuser_client_process() function context. + * + * \param[in] request_id A number identifying the request. It will be equal to + * the value of *p_request_id generated by a corresponding + * askuser_client_popup_multiple_request() function call. + * \param[in] cause A value representing the reason why this callback has + * been called. + * \param[in] results It is a results array of response to request created by + * askuser_client_popup_multiple_request(). This should be + * interpreted as a valid value only if cause is equal to + * askuser_call_cause::ASKUSER_CALL_CAUSE_ANSWER. + * \param[in] privileges A privileges array that has been checked. + * \param[in] privileges_count The number of elements of the privileges and results arrays. + * \param[in] p_user_data User specific data, this parameter was previously + * passed to askuser_client_popup_request(). + */ +typedef void (*askuser_popup_multiple_response_callback) (int request_id, + askuser_call_cause cause, + const askuser_popup_result results[], + const char **privileges, + size_t privileges_count, + void *p_user_data); + +/** * \brief Description * Initialize askuser-notification client. It allocates structure and * initializes it using a given status callback and user data. @@ -367,6 +395,34 @@ int askuser_client_check_privilege(askuser_client *p_client, const char *privile /** * \brief Description + * This function is called to check if an application has access to given + * privileges. + * + * \par Purpose: + * This API function should be called if an application wants to check if it has + * access to given privileges. + * + * \par Sync (or) Async: + * This is a synchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. + * + * \param[in] p_client An instance of the askuser_client structure previously + * created by askuser_client_initialize(). + * \param[in] privileges Privileges array that is to be checked. + * \param[in] privileges_count The number of elements of the privileges and results arrays. + * The parameter value should not exceed 100. + * \param[out] p_results The results array of a privileges check. + * + * \return ASKUSER_API_SUCCESS on success + * \return a negative value in case of an error (see "Status return codes") + */ +int askuser_client_check_privileges(askuser_client *p_client, const char **privileges, + size_t privileges_count, askuser_check_result *p_results); + +/** + * \brief Description * This function is called to determine a privacy privilege . * * \par Purpose: @@ -517,6 +573,55 @@ int askuser_client_popup_request(askuser_client *p_client, const char *privilege * \endcode */ + /** + * \brief Description + * This function is called to determine privacy privileges. + * + * \par Purpose: + * This API function should be called if an application wants to determine privacy + * privileges. When this function is called, the askuser-notification service shows + * an appropriate UI dialog box (popup) with questions about gaining access to a list of privileges. + * When the user makes a decision, the service modifies a security policy and sends the response + * back to the application. + * The application is informed about user's decision by calling + * askuser_popup_multiple_response_callback. + * + * \par Method of function operation: + * If the result of calling askuser_client_check_privileges() is + * askuser_check_result::ASKUSER_CHECK_RESULT_ASK, the application should call + * this function to determine whether a user allows an application to use + * privileges. + * + * \par Sync (or) Async: + * This is an asynchronous API. + * + * \par Thread-safety: + * This function is NOT thread-safe. + * + * \param[in] p_client An instance of the askuser_client structure + * previously created by askuser_client_initialize(). + * \param[in] privileges A privileges array for which a popup must be shown. + * \param[in] privileges_count The number of elements of the privileges and results arrays. + * The parameter value should not exceed 100. + * \param[in] response_callback A callback function called when the API receives + * a response. + * \param[in] p_user_data User specific data which will be passed to + * the registered response_callback. + * Set to NULL if you don't use user data. + * \param[out] p_request_id An identifier of a request generated by the API. + * Set NULL if you don't use a request ID. + * + * \return ASKUSER_API_SUCCESS on success + * \return a negative value in case of an error (see "Status return codes") + */ +int askuser_client_popup_multiple_request(askuser_client *p_client, const char **privileges, + size_t privileges_count, + askuser_popup_multiple_response_callback response_callback, + void *p_user_data, int *p_request_id); + +//TODO: add sample code + + #ifdef __cplusplus } #endif diff --git a/src/notification-daemon/Logic.cpp b/src/notification-daemon/Logic.cpp index 009a1e4..3e16add 100644 --- a/src/notification-daemon/Logic.cpp +++ b/src/notification-daemon/Logic.cpp @@ -173,7 +173,7 @@ Eina_Bool Logic::clientChHandler(void *data, Ecore_Fd_Handler *handler) { } Eina_Bool Logic::processChannel(int fd, int mask) { - ALOGD("Porcesing fd " << fd << " with mask " << mask); + ALOGD("Processing fd " << fd << " with mask " << mask); updateChannel(fd, mask); switch (m_fdInfo[fd].status) { @@ -188,7 +188,7 @@ Eina_Bool Logic::processChannel(int fd, int mask) { m_pendingEvents.erase(queueIt, m_pendingEvents.end()); // Ignore error in policy setting, we can't do anything about it nor we can inform user about error - (void)setPolicy(m_connToInfo[fd], "Ask user"); + (void)setPolicy(m_connToInfo[fd]); // TODO: now it doesn't set "Ask user" again m_connToInfo.erase(fd); m_fdInfo.erase(fd); m_serverChannel->process(fd, 0); @@ -208,11 +208,21 @@ Eina_Bool Logic::processChannel(int fd, int mask) { } } -void Logic::addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::vector &privacies) { +void Logic::addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id, + const std::map> &privsToAskablePrivacies, + const std::map &privsToPolicy, + const std::vector &privs, + const std::vector &privacies) { EventId eventId{fd, id}; - PrivaciesSequence &seq = m_eventToPrivaciesSeq[eventId]; + auto &ev = m_eventInfo[eventId]; + PrivaciesSequence &seq = ev.privaciesSeq; seq.setPrivacies(privacies); + ev.privilegesAskablePrivacies = privsToAskablePrivacies; + ev.privilegesPolicy = privsToPolicy; + ev.privileges = privs; + ev.privacyResponses.clear(); + ConnectionInfo &conn = m_connToInfo[fd]; Privacy currentPrivacy; @@ -222,9 +232,11 @@ void Logic::addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id, const st m_pendingEvents.emplace_back(std::move(fdEvent)); } -void Logic::popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::string &privilege) { +void Logic::popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::vector &privileges) { try { - ALOGD("Request for privilege " << privilege << " from fd " << fd << " with id " << id); + ALOGD("Request from fd " << fd << " with id " << id << " for privileges N= " << privileges.size() << " :"); + for (auto &privilege : privileges) + ALOGD("---- " << privilege); auto it = m_connToInfo.find(fd); if (it == m_connToInfo.end()) { @@ -232,40 +244,56 @@ void Logic::popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std:: return; } ConnectionInfo &conn = it->second; - - PrivilegePolicy privPolicy(conn.appId, privilege); PkgMgrAppInfo appInfo; - auto policyLevel = privPolicy.calculatePolicy(appInfo); - - ALOGD("Privilege policy level calculated to : " << policyLevel); - if (policyLevel == "Allow") { - m_serverChannel->popupResponses(fd, id, {ASKUSER_ALLOW_FOREVER}); - return; - } - if (policyLevel == "Deny") { - m_serverChannel->popupResponses(fd, id, {ASKUSER_DENY_FOREVER}); - return; - } - if (policyLevel != "Ask user") { - ALOGE("Unknown policy set : " << policyLevel << " for (" << conn.appId << ", " << conn.user - << ", " << privilege << ")"); - m_serverChannel->popupResponses(fd, id, {ASKUSER_DENY_ONCE}); - return; + std::map> privsToAskablePrivacies; + std::map privsToPolicy; + std::set uniquePrivacies; + + for (auto &privilege : privileges) { + PrivilegePolicy privPolicy(conn.appId, privilege); + privsToPolicy[privilege] = privPolicy.calculatePolicy(appInfo); + ALOGD("Privilege " << privilege << " policy level calculated to : " << privsToPolicy[privilege]); + if (privsToPolicy[privilege] == "Ask user") { + privsToAskablePrivacies[privilege] = privPolicy.calculateAskablePrivacies(appInfo); + if (privsToAskablePrivacies[privilege].empty()) { + ALOGE("All privacies for privilege " << privilege << " are already allowed"); + privsToPolicy[privilege] = "Allow"; + } else { + for (auto &privacy : privsToAskablePrivacies[privilege]) + uniquePrivacies.insert(privacy); + } + + } } - auto privacies = privPolicy.calculateAskablePrivacies(appInfo); - if (privacies.empty()) { - ALOGE("All privacies for privilege " << privilege - << " are already allowed"); - m_serverChannel->popupResponses(fd, id, {ASKUSER_ALLOW_FOREVER}); + if (uniquePrivacies.empty()) { + // nothing to ask about, we can already return answer + std::vector responses; + responses.resize(privileges.size()); + for (size_t i = 0; i < privileges.size(); ++i) { + if (privsToPolicy[privileges[i]] == "Allow") { + responses[i] = ASKUSER_ALLOW_FOREVER; + } else if (privsToPolicy[privileges[i]] == "Deny") { + responses[i] = ASKUSER_DENY_FOREVER; + } else { + ALOGE("Unknown policy set : " << privsToPolicy[privileges[i]] << " for (" << conn.appId << ", " << conn.user + << ", " << privileges[i] << ")"); + responses[i] = ASKUSER_DENY_ONCE; + } + } + m_serverChannel->popupResponses(fd, id, std::move(responses)); return; } - addEvent(fd, id, privacies); + // we do have privacies to ask about, we need to generate event + std::vector privacies(uniquePrivacies.begin(), uniquePrivacies.end()); + addEvent(fd, id, privsToAskablePrivacies, privsToPolicy, privileges, privacies); processEvents(); + } catch (const std::exception &e) { ALOGE("Failed to handle popup request : " << e.what()); - m_serverChannel->popupResponses(fd, id, {ASKUSER_DENY_ONCE}); + std::vector responses(privileges.size(), ASKUSER_DENY_ONCE); + m_serverChannel->popupResponses(fd, id, std::move(responses)); } } @@ -279,7 +307,7 @@ bool Logic::isEventProcessed() { } void Logic::finishCurrentRequest() { - m_eventToPrivaciesSeq.erase(m_currentEvent); + m_eventInfo.erase(m_currentEvent); m_popupper.popupClose(); m_currentEvent = EventId(); @@ -324,7 +352,7 @@ void Logic::registerSignalFd() { void Logic::stop() { if (isEventProcessed()) { ConnectionInfo &conn = m_connToInfo[m_currentEvent.fd]; - processResponse(conn, ASKUSER_DENY_ONCE, "Ask user"); + processResponse(conn); // TODO used to be ASKUSER_DENY_ONCE, "Ask user" finishCurrentRequest(); } m_popupper.stop(); @@ -391,49 +419,51 @@ void Logic::run() { m_popupper.shutdown(); } -bool Logic::setPolicy(const ConnectionInfo &conn, const std::string &lastPolicy) { - ALOGD("Setting policy for app " << conn.appId << " with last policy " << lastPolicy); - std::string currentPrivacy; - - PrivaciesSequence &privSeq = m_eventToPrivaciesSeq[m_currentEvent]; - if (!privSeq.getCurrentPrivacy(currentPrivacy) && lastPolicy == "Allow") { - ALOGD("Privilege is allowed completely"); - } - - privSeq.rewind(); - - Privacy privacy; - while (privSeq.getNextPrivacy(privacy) && privacy != currentPrivacy) { - /* - * When privacy is not allowed with deny forever or deny once, then processing is stopped, - * so all privacies, which were already processed, are allowed. - */ - if (!PolicyUpdater::update(conn.appId, privacy, "Allow")) { - // FIXME : Maybe ignore those errors and try to maintain the policy as consistent as we can? - ALOGE("Couldn't set policy for " << conn.appId << " privacy : " << privacy << " level : Allow"); +bool Logic::setPolicy(const ConnectionInfo &conn) { + ALOGD("Setting policy for app " << conn.appId); + for (auto &pr : m_eventInfo[m_currentEvent].privacyResponses) { + const auto &level = clientResponseToPolicy(pr.second); + if (!PolicyUpdater::update(conn.appId, pr.first, level)) { + ALOGE("Couldn't set policy for " << conn.appId << " privacy : " << pr.first << " level : " << level); return false; } } - - if (!currentPrivacy.empty() && lastPolicy != "Ask user") { - /* - * setPolicy is called when privacy processing is either successfully finished or - * aborted due to error, so currentPrivacy will be empty in the first case and - * we don't set policy when user responds with "Deny once" (this translates to "Ask user") - */ - if (!PolicyUpdater::update(conn.appId, currentPrivacy, lastPolicy)) { - ALOGE("Couldn't set policy for " << conn.appId << " privacy : " << privacy << " level : " << lastPolicy); - } - } return true; } -void Logic::processResponse(const ConnectionInfo &conn, int clientResponse, const Policy &level) { - if (!setPolicy(conn, level)) { - m_serverChannel->popupResponses(m_currentEvent.fd, m_currentEvent.id, {ASKUSER_UNKNOWN_ERROR}); - } else { - m_serverChannel->popupResponses(m_currentEvent.fd, m_currentEvent.id, {clientResponse}); +void Logic::processResponse(const ConnectionInfo &conn) { + auto ¤tEvent = m_eventInfo[m_currentEvent]; + std::vector responses(currentEvent.privileges.size(), ASKUSER_UNKNOWN_ERROR); + + if (setPolicy(conn)) { + for (size_t i = 0; i < responses.size(); ++i) { + const auto &privilege = currentEvent.privileges[i]; + const auto &policy = currentEvent.privilegesPolicy[privilege]; + if (policy == "Allow") { + responses[i] = ASKUSER_ALLOW_FOREVER; + } else if (policy == "Deny") { + responses[i] = ASKUSER_DENY_FOREVER; + } else if (policy != "Ask user") { + responses[i] = ASKUSER_DENY_ONCE; + } else { + // Ask user policy - need to calcualte from existing responses to privacies + // but only privacies askable for this privilege should be used! + responses[i] = ASKUSER_ALLOW_FOREVER; + bool hasAnswer = false; + for (auto &privacy : currentEvent.privilegesAskablePrivacies[privilege]) { + const auto &privResponses = currentEvent.privacyResponses; + const auto privResponseIt = privResponses.find(privacy); + if (privResponseIt != privResponses.end()) { + responses[i] = std::min(privResponseIt->second, responses[i]); + hasAnswer = true; + } + } + if (!hasAnswer) + responses[i] = ASKUSER_DENY_ONCE; + } + } } + m_serverChannel->popupResponses(m_currentEvent.fd, m_currentEvent.id, std::move(responses)); } void Logic::popupResponse(NResponseType response) { @@ -443,17 +473,24 @@ void Logic::popupResponse(NResponseType response) { return; } ConnectionInfo &conn = it->second; - + PrivaciesSequence &seq = m_eventInfo[m_currentEvent].privaciesSeq; int clientResponse = uiResponseToClientResponse(response); - std::string level = clientResponseToPolicy(clientResponse); - if (clientResponse != ASKUSER_ALLOW_FOREVER) { - // Privacy not allowed, request is denied - processResponse(conn, clientResponse, level); - finishCurrentRequest(); + std::string justAnsweredPrivacy; + if (!seq.getCurrentPrivacy(justAnsweredPrivacy)) { + ALOGE("Received response for unknown privacy - this should not happen"); + finishCurrentRequest(); + processEvents(); + return; + } + m_eventInfo[m_currentEvent].privacyResponses[justAnsweredPrivacy] = clientResponse; + if (clientResponse != ASKUSER_ALLOW_FOREVER && m_eventInfo[m_currentEvent].privileges.size() == 1) { + // request for only 1 privilege is being processed and at least 1 privacy is not allowed + // stop iteration over privacies, its pointless as the privilege is alraedy dissallowed + processResponse(conn); + finishCurrentRequest(); } else { - // Privacy allowed - check if more privacies are to process - PrivaciesSequence &seq = m_eventToPrivaciesSeq[m_currentEvent]; + // request for many privileges is being processed or one privilege but still needs to be processed Privacy nextPrivacy; if (seq.getNextPrivacy(nextPrivacy)) { // More privacies to process - replace existing event with new privacy @@ -462,8 +499,8 @@ void Logic::popupResponse(NResponseType response) { // don't call finishCurrentRequest here, because it will pop event, which we replaced m_currentEvent = EventId(); } else { - // No more privacies, request is allowed - processResponse(conn, clientResponse, level); + // No more privacies, request is ready for response + processResponse(conn); finishCurrentRequest(); } } diff --git a/src/notification-daemon/Logic.h b/src/notification-daemon/Logic.h index 7360ae3..e05c405 100644 --- a/src/notification-daemon/Logic.h +++ b/src/notification-daemon/Logic.h @@ -16,6 +16,7 @@ /** * @file src/agent/notification-daemon/Service.h * @author Zofia Abramowska + * @author Tomasz Swierczek * @brief Declaration of Popupper class */ @@ -54,7 +55,7 @@ public: void updateChannelFd(Protocol::ConnectionFd fd, Ecore_Fd_Handler_Flags flags); void removeChannelFd(Protocol::ConnectionFd fd); - void popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::string &privilege); + void popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::vector &privileges); ~Logic(); private: @@ -86,12 +87,16 @@ private: static Eina_Bool timerHandler(void *data); //static Eina_Bool signalHandler(void *data, int type, void *event); - void addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::vector &privacies); + void addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id, + const std::map> &privsToAskablePrivacies, + const std::map &privsToPolicy, + const std::vector &privs, + const std::vector &privacies); Eina_Bool processChannel(int fd, int mask); bool identifyClient(const std::string &label, std::string &appId, std::string &pkgId); void processEvents(); - void processResponse(const ConnectionInfo &conn, int clientResponse, const Policy &level); + void processResponse(const ConnectionInfo &conn); bool isEventProcessed(); void finishCurrentRequest(); @@ -101,7 +106,7 @@ private: void popupResponse(NResponseType response); bool processError(EventId id, int error); - bool setPolicy(const ConnectionInfo &conn, const std::string &lastPolicy); + bool setPolicy(const ConnectionInfo &conn); std::unique_ptr m_serverChannel; Popupper m_popupper; @@ -130,7 +135,15 @@ private: std::map m_connToInfo; - std::map m_eventToPrivaciesSeq; + struct EventInfo { + PrivaciesSequence privaciesSeq; + std::vector privileges; + std::map> privilegesAskablePrivacies; + std::map privilegesPolicy; + std::map privacyResponses; + }; + + std::map m_eventInfo; Ecore_Timer *m_timer; }; diff --git a/src/notification-daemon/ServerCallbacks.cpp b/src/notification-daemon/ServerCallbacks.cpp index e830e84..dece9b2 100644 --- a/src/notification-daemon/ServerCallbacks.cpp +++ b/src/notification-daemon/ServerCallbacks.cpp @@ -50,7 +50,7 @@ void ServerCallbacks::updateConnection(ConnectionFd fd, int mask) { } void ServerCallbacks::popup(ConnectionFd fd, RequestId id, std::vector &&privileges) { - m_service->popup(fd, id, privileges[0]); + m_service->popup(fd, id, privileges); } } /* namespace Notification */ -- 2.7.4