This commit is a preparation for new API with many privileges as input.
Change-Id: I9964bbdb1fa81211bdd0117e48a5462604407d17
Signed-off-by: Ernest Borowski <e.borowski@partner.samsung.com>
* @file privacy_privilege_manager.c
* @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
* @author Ernest Borowski <e.borowski@partner.samsung.com>
+ * @author Tomasz Swierczek <t.swierczek@samsung.com>
* @brief The implementation of Privacy Privilege Manager CAPI.
*/
};
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)
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) {
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
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;
}
/**
* @file ApiInterface.h
* @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @author Ernest Borowski <e.borowski@partner.samsung.com>
* @brief This file contains the declaration of ApiInterface.
*/
#pragma once
#include <string>
+#include <vector>
#include <askuser-notification-client.h>
virtual RequestId popupRequest(const std::string &privilege,
const askuser_popup_response_callback callback,
void *userData) = 0;
+ virtual RequestId popupRequest(const std::vector<std::string> &privileges,
+ const askuser_popup_multiple_response_callback callback,
+ void *userData) = 0;
virtual bool popupRequestInProgress(const std::string &privilege) const = 0;
};
/**
* @file askuser-notification-client.cpp
* @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @author Ernest Borowski <e.borowski@partner.samsung.com>
* @brief This file contains the implementation of the askuser-notification client API.
*/
#include <cstring>
#include <memory>
+#include <unordered_set>
+#include <vector>
#include <log/alog.h>
#include <askuser-notification-client.h>
#include <attributes/attributes.h>
+#include <message-utils.h>
#include <ApiInterface.h>
#include <ApiInterfaceImpl.h>
}
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)
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<std::string> 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<std::string> 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;
+ });
+
+}
#include <algorithm>
#include <unistd.h>
#include <sys/types.h>
+#include <sstream>
#include <log/alog.h>
#include <policy/Policy.h>
return ASKUSER_POPUP_RESULT_DENY_ONCE;
}
-inline askuser_call_cause deduceCauseFromResponse(int response)
+inline askuser_call_cause deduceCauseFromResponses(const std::vector<int> &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;
}
const askuser_popup_response_callback callback,
void *userData)
{
- std::vector<std::string> privileges({privilege});
- Protocol::ConnectionContext conCtx = m_channel->popupRequest(std::move(privileges));
+ return popupRequestInternal({privilege}, NULL, callback, userData);
+}
+
+RequestId ApiInterfaceImpl::popupRequest(const std::vector<std::string> &privileges,
+ const askuser_popup_multiple_response_callback callback,
+ void *userData)
+{
+ return popupRequestInternal(privileges, callback, NULL, userData);
+}
+
+
+RequestId ApiInterfaceImpl::popupRequestInternal(const std::vector<std::string> &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;
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<int> 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;
}
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();
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<int> 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<int> &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<const char *> 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<askuser_popup_result> 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<int> &responses)
{
auto sameRequestId = [&] (const Request &req) {
return req.m_requestId == id;
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<int> results(req.m_privileges.size(), result);
+ popupResponsesHelper(req.m_requestId, req, cause, results);
}
m_requests.clear();
/**
* @file ApiInterfaceImpl.h
* @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @author Ernest Borowski <e.borowski@partner.samsung.com>
* @brief The declaration of ApiInterfaceImpl.
*/
#pragma once
+#include <iostream>
#include <memory>
+#include <list>
+#include <string>
#include <vector>
#include <ApiInterface.h>
virtual RequestId popupRequest(const std::string &privilege,
const askuser_popup_response_callback callback,
void *userData);
+ virtual RequestId popupRequest(const std::vector<std::string> &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<int> &responses);
private:
+ RequestId popupRequestInternal(const std::vector<std::string> &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<std::string> 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<int> &responses);
+
StatusCallbackClosure m_statusClosure;
std::unique_ptr<Protocol::ClientChannel> m_channel;
- std::vector<Request> m_requests;
+ std::list<Request> m_requests;
AulAppInfo m_appInfo;
AulPkgInfo m_pkgInfo;
};
void ClientCallbacks::popupResponses(Protocol::RequestId id, const std::vector<int> &responses)
{
- m_api->popupResponse(id, responses[0]);
+ m_api->popupResponses(id, responses);
}
} // namespace Client
/*
- * 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.
/**
* @file askuser-notification-client.h
* @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @author Ernest Borowski <e.borowski@partner.samsung.com>
* @brief The declaration of the askuser-notification client API.
*/
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.
/**
* \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:
* \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
}
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) {
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);
}
}
-void Logic::addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::vector<Privacy> &privacies) {
+void Logic::addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id,
+ const std::map<Privilege, std::vector<Privacy>> &privsToAskablePrivacies,
+ const std::map<Privilege, Policy> &privsToPolicy,
+ const std::vector<Privilege> &privs,
+ const std::vector<Privacy> &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;
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<Privilege> &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()) {
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<Privilege, std::vector<Privacy>> privsToAskablePrivacies;
+ std::map<Privilege, Policy> privsToPolicy;
+ std::set<Privacy> 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<int> 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<Privacy> 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<int> responses(privileges.size(), ASKUSER_DENY_ONCE);
+ m_serverChannel->popupResponses(fd, id, std::move(responses));
}
}
}
void Logic::finishCurrentRequest() {
- m_eventToPrivaciesSeq.erase(m_currentEvent);
+ m_eventInfo.erase(m_currentEvent);
m_popupper.popupClose();
m_currentEvent = EventId();
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();
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<int> 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) {
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
// 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();
}
}
/**
* @file src/agent/notification-daemon/Service.h
* @author Zofia Abramowska <z.abramowska@samsung.com>
+ * @author Tomasz Swierczek <t.swierczek@samsung.com>
* @brief Declaration of Popupper class
*/
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<Privilege> &privileges);
~Logic();
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<Privacy> &privacies);
+ void addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id,
+ const std::map<Privilege, std::vector<Privacy>> &privsToAskablePrivacies,
+ const std::map<Privilege, Policy> &privsToPolicy,
+ const std::vector<Privilege> &privs,
+ const std::vector<Privacy> &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();
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<AskUser::Protocol::ServerChannel> m_serverChannel;
Popupper m_popupper;
std::map<Protocol::ConnectionFd, ConnectionInfo> m_connToInfo;
- std::map<EventId, PrivaciesSequence> m_eventToPrivaciesSeq;
+ struct EventInfo {
+ PrivaciesSequence privaciesSeq;
+ std::vector<Privilege> privileges;
+ std::map<Privilege, std::vector<Privacy>> privilegesAskablePrivacies;
+ std::map<Privilege, Policy> privilegesPolicy;
+ std::map<Privacy, int> privacyResponses;
+ };
+
+ std::map<EventId, EventInfo> m_eventInfo;
Ecore_Timer *m_timer;
};
}
void ServerCallbacks::popup(ConnectionFd fd, RequestId id, std::vector<std::string> &&privileges) {
- m_service->popup(fd, id, privileges[0]);
+ m_service->popup(fd, id, privileges);
}
} /* namespace Notification */