Add the 'privilege' parameter to popup response callback 64/139164/3
authorPiotr Sawicki <p.sawicki2@partner.samsung.com>
Mon, 17 Jul 2017 13:54:18 +0000 (15:54 +0200)
committerZofia Abramowska <z.abramowska@samsung.com>
Tue, 18 Jul 2017 14:17:05 +0000 (14:17 +0000)
Apart from that, a protection against sending multiple requests
with the same privilege has been added. A new status code:
PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS informs
an application when the above error occurs.

Change-Id: Ifb2397c99452d56f7327f0be3f54ce33b766488f

src/capi/impl/privacy_privilege_manager.c
src/capi/include/privacy_privilege_manager.h
src/capi/test/privacy_privilege_manager_test.cpp
src/client/api/ApiInterface.h
src/client/api/askuser-notification-client.cpp
src/client/impl/ApiInterfaceImpl.cpp
src/client/impl/ApiInterfaceImpl.h
src/client/impl/PopupCallbackClosure.h
src/client/include/askuser-notification-client.h

index 3ff87bb580bc0190759b57b411bd0f8acb92dbeb..63c04fdc857b710f1a30d1612a34d9d83f86d8fc 100644 (file)
@@ -71,6 +71,8 @@ static ppm_error_e ask_user_to_ppm_error(int ask_error)
         case ASKUSER_API_CONNECTION_ERROR:
             ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR;
             break;
+        case ASKUSER_API_ALREADY_IN_PROGRESS:
+            ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS;
         default:
             break;
     }
@@ -194,7 +196,8 @@ static void ask_status_callback(int fd, int events, void *p_user_data)
 }
 
 static void ppm_popup_response_callback(UNUSED int request_id, askuser_call_cause cause,
-                                        askuser_popup_result result, void *p_user_data)
+                                        askuser_popup_result result, const char *privilege,
+                                        void *p_user_data)
 {
     ppm_callback_closure *callback_closure = (ppm_callback_closure *) p_user_data;
 
@@ -209,7 +212,7 @@ static void ppm_popup_response_callback(UNUSED int request_id, askuser_call_caus
     ppm_call_cause_e ppm_cause = askuser_client_popup_cause_to_ppm(cause);
     ppm_check_result_e ppm_result = askuser_client_popup_result_to_ppm(result);
 
-    callback_closure->callback(ppm_cause, ppm_result, callback_closure->user_data);
+    callback_closure->callback(ppm_cause, ppm_result, privilege, callback_closure->user_data);
 
     free(callback_closure);
 }
index a433a18a5c3c6d7a9a3746b3c6c4a24d60247214..e3a2e08db1cca2e53a94c83b61be2ce97129a492 100644 (file)
@@ -40,6 +40,8 @@ typedef enum
     PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR             = TIZEN_ERROR_IO_ERROR,
     /**< Invalid parameter */
     PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER    = TIZEN_ERROR_INVALID_PARAMETER,
+    /**< Operation already in progress */
+    PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS  = TIZEN_ERROR_ALREADY_IN_PROGRESS,
     /**< Out of memory */
     PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY        = TIZEN_ERROR_OUT_OF_MEMORY,
     /**< Unknown error */
@@ -93,13 +95,15 @@ typedef enum {
  * @param[in]   result      A result of a response triggered by calling ppm_popup_request().
  *                          This is a valid value only if the @a cause parameter is equal to
  *                          **PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER**.
- * @param[in]   user_data   User specific data, this parameter is passed
+ * @param[in]   privilege   A privilege that has been checked.
+ * @param[in]   user_data   User specific data, this parameter has been passed
  *                          to ppm_popup_request().
  *
  * @see ppm_popup_request()
  */
 typedef void (*ppm_popup_response_cb) (ppm_call_cause_e cause,
                                        ppm_popup_result_e result,
+                                       const char *privilege,
                                        void *user_data);
 
 /**
@@ -153,11 +157,12 @@ int ppm_check_privilege(const char *privilege, ppm_check_result_e *result);
  *                          the registered callback.
  *
  * @return 0 on success, otherwise a negative error value
- * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE               Successful
- * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR           I/O error
- * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER  Invalid parameter
- * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY      Out of memory
- * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN            Unknown error
+ * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE                Successful
+ * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR            I/O error
+ * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER   Invalid parameter
+ * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY       Out of memory
+ * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS Operation already in progress
+ * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN             Unknown error
  *
  * @post ppm_popup_response_cb() will be invoked.
  * @see ppm_popup_response_cb()
index b7866c7e94f661bee6b82c09b1ce2513f83090da..5138b89a70c907908cce7950223e9bcb7b1b9a00 100644 (file)
@@ -60,7 +60,6 @@ typedef std::set<ServerPrivilegeRequest> PrivilegeRequestSet;
 
 struct ClientRequest {
     RequestId m_id;
-    Privilege m_privilege;
     GMainLoop *m_mainLoop;
     bool m_stopAppAfterDeny;
 };
@@ -156,6 +155,9 @@ void printClientError(ppm_error_e error)
         case PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN:
             printf("Unknown error\n");
             break;
+        case PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS:
+            printf("Operation already in progress\n");
+            break;
     }
 }
 
@@ -439,14 +441,15 @@ public:
 
 private:
 
-    static void popupResponseCallback(ppm_call_cause_e cause, ppm_popup_result_e result, void *user_data) {
+    static void popupResponseCallback(ppm_call_cause_e cause, ppm_popup_result_e result,
+                                      const char *privilege, void *user_data) {
         ClientRequest *request = static_cast<ClientRequest *>(user_data);
         if (request == nullptr) {
             printf("ERROR: User data is nullptr\n");
             return;
         }
 
-        printf("localId: %d privilege: \"%s\" ", request->m_id, request->m_privilege.c_str());
+        printf("localId: %d privilege: \"%s\" ", request->m_id, privilege);
 
         switch (cause) {
             case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER:
@@ -498,7 +501,7 @@ private:
 
                 printf("sending localId: %d privilege: \"%s\"\n", clientRequestId, privilege);
 
-                request = new ClientRequest{ clientRequestId++, privilege, m_mainloop, stopAppAfterDeny };
+                request = new ClientRequest{ clientRequestId++, m_mainloop, stopAppAfterDeny };
                 err = static_cast<ppm_error_e>(ppm_popup_request(privilege, &AppContext::popupResponseCallback, request));
                 if (err != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
                     delete request;
index 9aef9986de63c6c67dfdc163408033f4b4e190a9..87bb603013ad380ac2e875c7df8eba2bb0498024 100644 (file)
@@ -42,6 +42,7 @@ public:
     virtual askuser_check_result checkPrivilege(const std::string &privilege) = 0;
     virtual RequestId popupRequest(const PopupCallbackClosure &closure,
                                    const std::string &privilege) = 0;
+    virtual bool popupRequestInProgress(const std::string &privilege) const = 0;
 };
 
 } // namespace Client
index f0d8ff36f658c676b3b36e030d802cae7858c1ee..9de7c6d4c500378bd7d0188373fac0da95e95374 100644 (file)
@@ -116,7 +116,11 @@ int askuser_client_popup_request(askuser_client *p_client, const char *privilege
     }
 
     return AskUser::Client::tryCatch([&]() {
-        AskUser::Client::PopupCallbackClosure closure(response_callback, p_user_data);
+        if (p_client->impl->popupRequestInProgress(privilege)) {
+            return ASKUSER_API_ALREADY_IN_PROGRESS;
+        }
+
+        AskUser::Client::PopupCallbackClosure closure(response_callback, privilege, p_user_data);
         AskUser::Client::RequestId id = p_client->impl->popupRequest(closure, privilege);
 
         if (p_request_id) {
index 8cc25938af9ddddda3f05796dd8ce468bcb0828f..12021ed5c208f1d23802b16ff59e9252c5f5dec4 100644 (file)
 
 namespace {
 
-int eventsToAskUserMask(int events)
+inline int eventsToAskUserMask(int events)
 {
     return ((events & ASKUSER_READ_EVENT) ? AskUser::Protocol::FdMask::READ : 0) |
            ((events & ASKUSER_WRITE_EVENT) ? AskUser::Protocol::FdMask::WRITE : 0);
 }
 
-int askUserMaskToEvents(int mask)
+inline int askUserMaskToEvents(int mask)
 {
     return ((AskUser::Protocol::FdMask::READ & mask) ? ASKUSER_READ_EVENT : 0) |
            ((AskUser::Protocol::FdMask::WRITE & mask) ? ASKUSER_WRITE_EVENT : 0);
 }
 
-askuser_popup_result responseToAskUserPopupResult(int response)
+inline askuser_popup_result responseToAskUserPopupResult(int response)
 {
     switch (response) {
         case ASKUSER_ALLOW_FOREVER:
@@ -59,6 +59,15 @@ askuser_popup_result responseToAskUserPopupResult(int response)
     return ASKUSER_POPUP_RESULT_DENY_ONCE;
 }
 
+inline askuser_call_cause deduceCauseFromResponse(int response)
+{
+    if (response == ASKUSER_UNKNOWN_ERROR) {
+        return ASKUSER_CALL_CAUSE_ERROR;
+    }
+
+    return ASKUSER_CALL_CAUSE_ANSWER;
+}
+
 } // namespace
 
 namespace AskUser {
@@ -75,12 +84,8 @@ ApiInterfaceImpl::ApiInterfaceImpl(const StatusCallbackClosure &statusClosure)
 
 ApiInterfaceImpl::~ApiInterfaceImpl()
 {
-    for (const auto &closure : m_callbacks) {
-        closure.second(closure.first, askuser_call_cause::ASKUSER_CALL_CAUSE_FINALIZE,
-                       askuser_popup_result::ASKUSER_POPUP_RESULT_DENY_ONCE);
-    }
+    respondToAllRequests(ASKUSER_CALL_CAUSE_FINALIZE, ASKUSER_POPUP_RESULT_DENY_ONCE);
 
-    m_callbacks.clear();
     m_channel.reset();
 }
 
@@ -102,7 +107,8 @@ askuser_check_result ApiInterfaceImpl::checkPrivilege(const std::string &privile
     auto policies = fetch.fetchPolicy();
 
     if (policies.size() != 1) {
-        ALOGE("Unusual situation, there are " << policies.size() << " policies for (" << appId << ", " << geteuid() << ", " << privilege << ")");
+        ALOGE("Unusual situation, there are " << policies.size() <<
+              " policies for (" << appId << ", " << geteuid() << ", " << privilege << ")");
         return ASKUSER_CHECK_RESULT_DENY;
     }
 
@@ -126,48 +132,70 @@ askuser_check_result ApiInterfaceImpl::checkPrivilege(const std::string &privile
 RequestId ApiInterfaceImpl::popupRequest(const PopupCallbackClosure &closure,
                                          const std::string &privilege)
 {
-    Client::RequestId id = static_cast<Client::RequestId>(m_channel->popupRequest(privilege));
+    RequestId id = static_cast<RequestId>(m_channel->popupRequest(privilege));
+
+    auto it = m_popupClosures.find(id);
+    if (it != m_popupClosures.end()) {
+        ALOGE("Popup closure exists for id: " << id <<
+              " privilege: " << it->second.privilege() << ", replacing");
+        popupResponse(id, ASKUSER_UNKNOWN_ERROR);
+    }
 
-    auto it = m_callbacks.find(id);
-    if (it != m_callbacks.end()) {
-        it->second(it->first, ASKUSER_CALL_CAUSE_ERROR, ASKUSER_POPUP_RESULT_DENY_ONCE);
-        m_callbacks.erase(it);
+    if (popupRequestInProgress(privilege)) {
+        ALOGE("Privilege " << closure.privilege() << " already exists in the pending privileges set");
     }
 
-    m_callbacks.insert({id, closure});
+    m_requestedPrivileges.insert(privilege);
+    m_popupClosures.insert({id, closure});
 
     return id;
 }
 
+bool ApiInterfaceImpl::popupRequestInProgress(const std::string &privilege) const
+{
+    return m_requestedPrivileges.find(privilege) != m_requestedPrivileges.end();
+}
+
 void ApiInterfaceImpl::updateConnection(Protocol::ConnectionFd fd, int mask)
 {
     m_statusClosure(fd, askUserMaskToEvents(mask));
 
-    // remove all pending events
+    // the connection is about to close, respond to all pending requests
     if (mask == ASKUSER_EMPTY_EVENTS) {
-        for (const auto &closure : m_callbacks) {
-            closure.second(closure.first, ASKUSER_CALL_CAUSE_ERROR, ASKUSER_POPUP_RESULT_DENY_ONCE);
-        }
-
-        m_callbacks.clear();
+        respondToAllRequests(ASKUSER_CALL_CAUSE_ERROR, ASKUSER_POPUP_RESULT_DENY_ONCE);
     }
 }
 
 void ApiInterfaceImpl::popupResponse(Protocol::RequestId id, int response)
 {
-    auto it = m_callbacks.find(id);
-    if (it == m_callbacks.end()) {
+    auto it = m_popupClosures.find(id);
+    if (it == m_popupClosures.end()) {
+        ALOGE("Couldn't find popup callback closure for id: " << id);
         return;
     }
 
-    askuser_call_cause cause = ASKUSER_CALL_CAUSE_ANSWER;
-    if (response == ASKUSER_UNKNOWN_ERROR) {
-        cause = ASKUSER_CALL_CAUSE_ERROR;
+    const auto &closure = it->second;
+    if (!popupRequestInProgress(closure.privilege())) {
+        ALOGE("Couldn't find privilege " << closure.privilege() << " in the pending privileges set");
     }
 
+    askuser_call_cause cause = deduceCauseFromResponse(response);
     askuser_popup_result res = responseToAskUserPopupResult(response);
-    it->second(id, cause, res);
-    m_callbacks.erase(it);
+
+    closure(id, cause, res);
+
+    m_requestedPrivileges.erase(closure.privilege());
+    m_popupClosures.erase(it);
+}
+
+void ApiInterfaceImpl::respondToAllRequests(askuser_call_cause cause, askuser_popup_result result)
+{
+    for (const auto &closure : m_popupClosures) {
+        closure.second(closure.first, cause, result);
+    }
+
+    m_requestedPrivileges.clear();
+    m_popupClosures.clear();
 }
 
 } // namespace Client
index 3734a68e045fc016d90045da2c657a095fbbaf08..efa97c6f86567cd4931e9b09a4245d109b9702a3 100644 (file)
@@ -22,7 +22,9 @@
 
 #pragma once
 
+#include <map>
 #include <memory>
+#include <set>
 
 #include <ApiInterface.h>
 #include <PopupCallbackClosure.h>
@@ -48,14 +50,18 @@ public:
     virtual askuser_check_result checkPrivilege(const std::string &privilege);
     virtual RequestId popupRequest(const PopupCallbackClosure &closure,
                                    const std::string &privilege);
+    virtual bool popupRequestInProgress(const std::string &privilege) const;
 
     void updateConnection(Protocol::ConnectionFd fd, int mask);
     void popupResponse(Protocol::RequestId id, int response);
 
 private:
+    void respondToAllRequests(askuser_call_cause cause, askuser_popup_result result);
+
     StatusCallbackClosure m_statusClosure;
     std::unique_ptr<Protocol::ClientChannel> m_channel;
-    std::map<RequestId, PopupCallbackClosure> m_callbacks;
+    std::map<RequestId, PopupCallbackClosure> m_popupClosures;
+    std::set<std::string> m_requestedPrivileges;
 };
 
 } // namespace Client
index 7f395c7ea3226a56241f976d49eeb80072767839..9bb2953aca07ae5a857bc1a519c8d27427b8a933 100644 (file)
@@ -22,6 +22,8 @@
 
 #pragma once
 
+#include <string>
+
 #include <askuser-notification-client.h>
 
 namespace AskUser {
@@ -30,18 +32,24 @@ namespace Client {
 
 class PopupCallbackClosure {
 public:
-    PopupCallbackClosure(askuser_popup_response_callback callback, void *userData)
+    PopupCallbackClosure(askuser_popup_response_callback callback, const char *privilege, void *userData)
     : m_callback(callback)
+    , m_privilege(privilege)
     , m_userData(userData)
     {}
 
     void operator()(int requestId, askuser_call_cause cause, askuser_popup_result result) const
     {
-        m_callback(requestId, cause, result, m_userData);
+        m_callback(requestId, cause, result, m_privilege.c_str(), m_userData);
+    }
+
+    const std::string &privilege() const {
+        return m_privilege;
     }
 
 private:
     askuser_popup_response_callback m_callback;
+    std::string m_privilege;
     void *m_userData;
 };
 
index 1b32d143abdc2a4099b6aa01975d197db84bf901..8398fddcb45ea48656b23470d1eab8b6f6b44ca6 100644 (file)
@@ -43,6 +43,9 @@
 /*! \brief indicating that a connection error occurred */
 #define ASKUSER_API_CONNECTION_ERROR -4
 
+/*! \brief indicating that a request has already been sent */
+#define ASKUSER_API_ALREADY_IN_PROGRESS -5
+
 /** @} */
 
 /**
@@ -199,12 +202,14 @@ typedef void (*askuser_status_callback) (int fd, int events, void *p_user_data);
  *                          askuser_client_popup_request(). This should be
  *                          interpreted as a valid value only if cause is equal to
  *                          askuser_call_cause::ASKUSER_CALL_CAUSE_ANSWER.
+ * \param[in]   privilege   A privilege that has been checked.
  * \param[in]   p_user_data User specific data, this parameter was previously
  *                          passed to askuser_client_popup_request().
  */
 typedef void (*askuser_popup_response_callback) (int request_id,
                                                  askuser_call_cause cause,
                                                  askuser_popup_result result,
+                                                 const char *privilege,
                                                  void *p_user_data);
 
 /**