From: Piotr Sawicki Date: Fri, 30 Jun 2017 10:34:27 +0000 (+0200) Subject: Implement CAPI X-Git-Tag: submit/tizen/20170727.154157~1^2~39 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=023142df9a545e0e72a14ccf0dba88a431b36ecb;p=platform%2Fcore%2Fsecurity%2Faskuser.git Implement CAPI * Bind askuser-client API to the GLib main loop mechanism. * Add simple test program. * Add an unknown error code type to API Change-Id: Icc04d7021003bc7547f2294920011ec46eaa0344 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 429a8ac..1b81172 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,7 @@ SET(TARGET_ASKUSER_NOTIFICATION_LIB "askuser-notification-ipc") SET(TARGET_ASKUSER_NOTIFICATION_LIB_TEST "askuser-notification-test") SET(TARGET_ASKUSER_NOTIFICATION_CLIENT_LIB "askuser-notification-client") SET(TARGET_PRIVACY_PRIVILEGE_MANAGER_CAPI_LIB "capi-privacy-privilege-manager") +SET(TARGET_PRIVACY_PRIVILEGE_MANAGER_TEST "capi-privacy-privilege-manager-test") ADD_SUBDIRECTORY(src/plugin) ADD_SUBDIRECTORY(src/notification-daemon) diff --git a/packaging/askuser-notification.spec b/packaging/askuser-notification.spec index f76aa0d..f0ac888 100644 --- a/packaging/askuser-notification.spec +++ b/packaging/askuser-notification.spec @@ -105,6 +105,13 @@ Summary: Privacy Privilege Manager in TIZEN C API development files %description -n capi-privacy-privilege-manager-devel Privacy Privilege Manager in TIZEN C API development files +%package -n capi-privacy-privilege-manager-test +Summary: Tests for Privacy Privilege Manager in TIZEN C API +Requires: capi-privacy-privilege-manager + +%description -n capi-privacy-privilege-manager-test +Provides tests for checking the API provided by capi-privacy-privilege-manager + %prep %setup -q cp -a %{SOURCE1001} . @@ -223,3 +230,6 @@ systemctl restart cynara.service %{_libdir}/pkgconfig/capi-privacy-privilege-manager.pc %{_libdir}/libcapi-privacy-privilege-manager.so +%files -n capi-privacy-privilege-manager-test +%manifest default.manifest +%attr(755,root,root) %{_bindir}/capi-privacy-privilege-manager-test diff --git a/src/capi/CMakeLists.txt b/src/capi/CMakeLists.txt index 46736c9..4e2cca7 100644 --- a/src/capi/CMakeLists.txt +++ b/src/capi/CMakeLists.txt @@ -34,7 +34,12 @@ SET(PRIVACY_PRIVILEGE_MANAGER_SOURCES ${PRIVACY_PRIVILEGE_MANAGER_PATH}/impl/privacy_privilege_manager.c ) +SET(PRIVACY_PRIVILEGE_MANAGER_SOURCES_TEST_SOURCES + ${PRIVACY_PRIVILEGE_MANAGER_PATH}/test/privacy_privilege_manager_test.c + ) + ADD_LIBRARY(${TARGET_PRIVACY_PRIVILEGE_MANAGER_CAPI_LIB} SHARED ${PRIVACY_PRIVILEGE_MANAGER_SOURCES}) +ADD_EXECUTABLE(${TARGET_PRIVACY_PRIVILEGE_MANAGER_TEST} ${PRIVACY_PRIVILEGE_MANAGER_SOURCES_TEST_SOURCES}) SET_TARGET_PROPERTIES(${TARGET_PRIVACY_PRIVILEGE_MANAGER_CAPI_LIB} PROPERTIES @@ -51,6 +56,8 @@ TARGET_LINK_LIBRARIES( ${PRIVACY_PRIVILEGE_MANAGER_DEP_LIBRARIES} ) +TARGET_LINK_LIBRARIES(${TARGET_PRIVACY_PRIVILEGE_MANAGER_TEST} ${TARGET_PRIVACY_PRIVILEGE_MANAGER_CAPI_LIB}) + INSTALL(TARGETS ${TARGET_PRIVACY_PRIVILEGE_MANAGER_CAPI_LIB} DESTINATION ${LIB_INSTALL_DIR}) @@ -58,3 +65,5 @@ FILE(GLOB HEADERS ${PRIVACY_PRIVILEGE_MANAGER_PATH}/include/*.h) INSTALL(FILES ${HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/privacy-privilege-manager) +INSTALL(TARGETS ${TARGET_PRIVACY_PRIVILEGE_MANAGER_TEST} + DESTINATION ${BIN_INSTALL_DIR}) diff --git a/src/capi/impl/privacy_privilege_manager.c b/src/capi/impl/privacy_privilege_manager.c index cc96bf0..ff97b23 100644 --- a/src/capi/impl/privacy_privilege_manager.c +++ b/src/capi/impl/privacy_privilege_manager.c @@ -18,7 +18,7 @@ * @file privacy_privilege_manager.c * @author Piotr Sawicki * @version 1.0 - * @brief This file contains implementation of privacy_privilege_manager.h CAPI. + * @brief The implementation of Privacy Privilege Manager CAPI. */ #include @@ -27,9 +27,30 @@ #include -askuser_client *client = NULL; -struct ppm_private_struct {} *ppm_private = NULL; +#define UNUSED __attribute__((unused)) +struct ppm_private_s { + askuser_client *client; + GIOChannel *channel; + GIOCondition condition; + guint watch_id; +}; +typedef struct ppm_private_s ppm_private; + +struct ppm_callback_closure_s { + void *user_data; + ppm_popup_response_cb callback; +}; +typedef struct ppm_callback_closure_s ppm_callback_closure; + +static ppm_private *ppm_handle = NULL; + +static void ppm_private_init(ppm_private *handle) +{ + handle->channel = NULL; + handle->condition = 0; + handle->watch_id = 0; +} static ppm_error_e ask_user_to_ppm_error(int ask_error) { @@ -40,7 +61,7 @@ static ppm_error_e ask_user_to_ppm_error(int ask_error) ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE; break; case ASKUSER_API_UNKNOWN_ERROR: - ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR; + ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN; break; case ASKUSER_API_OUT_OF_MEMORY: ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY; @@ -58,7 +79,7 @@ static ppm_error_e ask_user_to_ppm_error(int ask_error) return ret; } -static ppm_check_result_e ask_user_check_result_to_pps(askuser_check_result result) +static ppm_check_result_e ask_user_check_result_to_ppm(askuser_check_result result) { switch (result) { case ASKUSER_CHECK_RESULT_ALLOW: @@ -72,6 +93,33 @@ static ppm_check_result_e ask_user_check_result_to_pps(askuser_check_result resu return PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY; } +static ppm_popup_result_e askuser_client_popup_result_to_ppm(askuser_popup_result result) +{ + switch (result) { + case ASKUSER_POPUP_RESULT_ALLOW_FOREVER: + return PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_ALLOW_FOREVER; + case ASKUSER_POPUP_RESULT_DENY_FOREVER: + return PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_FOREVER; + case ASKUSER_POPUP_RESULT_DENY_ONCE: + return PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_ONCE; + } + + return PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_ONCE; +} + +static ppm_call_cause_e askuser_client_popup_cause_to_ppm(askuser_call_cause cause) +{ + switch (cause) { + case ASKUSER_CALL_CAUSE_ANSWER: + return PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER; + case ASKUSER_CALL_CAUSE_ERROR: + case ASKUSER_CALL_CAUSE_FINALIZE: + return PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR; + } + + return ASKUSER_CALL_CAUSE_ERROR; +} + static int g_io_condition_to_askuser_events(GIOCondition cond) { return ((cond & G_IO_IN) ? ASKUSER_READ_EVENT : 0) | @@ -84,32 +132,122 @@ static GIOCondition askuser_events_to_g_io_condition(int events) ((events & ASKUSER_WRITE_EVENT) ? G_IO_OUT : 0); } +static gboolean ppm_error_condition(GIOCondition cond) +{ + return !!(cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)); +} + +static gboolean ppm_is_connected(ppm_private *handle) +{ + return handle->channel != NULL; +} + +static gboolean ppm_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data) +{ + int fd, events; + ppm_private *handle = (ppm_private *) data; + + fd = g_io_channel_unix_get_fd(src); + events = g_io_condition_to_askuser_events(cond); + + (void) askuser_client_process(handle->client, fd, + ppm_error_condition(cond) ? ASKUSER_EMPTY_EVENTS : events); + + return TRUE; +} + static void ask_status_callback(int fd, int events, void *p_user_data) { - (void) fd; - (void) events; - (void) p_user_data; - g_io_condition_to_askuser_events(0); - askuser_events_to_g_io_condition(0); - // TODO register GIOChannel and GIOSource + ppm_private *handle = (ppm_private *) p_user_data; + GIOCondition gio_condition = askuser_events_to_g_io_condition(events); + + if (events == ASKUSER_EMPTY_EVENTS) { + if (ppm_is_connected(handle)) { + g_source_remove(handle->watch_id); + g_io_channel_unref(handle->channel); + ppm_private_init(handle); + } + return; + } + + if (!ppm_is_connected(handle)) { + handle->condition = gio_condition; + handle->channel = g_io_channel_unix_new(fd); + + g_io_channel_set_encoding (handle->channel, NULL, NULL); + g_io_channel_set_close_on_unref(handle->channel, FALSE); - // TODO register callback + handle->watch_id = g_io_add_watch(handle->channel, + handle->condition | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + ppm_gio_cb, + handle); + return; + } + + if (handle->condition != gio_condition) { + handle->condition = gio_condition; + g_source_remove(handle->watch_id); + handle->watch_id = g_io_add_watch(handle->channel, + handle->condition | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + ppm_gio_cb, + handle); + } +} + +static void ppm_popup_response_callback(UNUSED int request_id, askuser_call_cause cause, + askuser_popup_result result, void *p_user_data) +{ + ppm_callback_closure *callback_closure = (ppm_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_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); + + free(callback_closure); +} + +static void ppm_free_client() +{ + if (ppm_handle != NULL) { + askuser_client_finalize(ppm_handle->client); + free(ppm_handle); + ppm_handle = NULL; + } } static int ppm_init_client() { - if (ppm_private == NULL) { - ppm_private = (struct ppm_private_struct *) malloc(sizeof(struct ppm_private_struct)); - if (ppm_private == NULL) { + if (ppm_handle == NULL) { + ppm_handle = (ppm_private *) calloc(1, sizeof(ppm_private)); + if (ppm_handle == NULL) { return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY; } - int ret = askuser_client_initialize(&client, ask_status_callback, ppm_private); + ppm_private_init(ppm_handle); + + int ret = askuser_client_initialize(&ppm_handle->client, ask_status_callback, ppm_handle); if (ret != ASKUSER_API_SUCCESS) { - free(ppm_private); - ppm_private = NULL; + free(ppm_handle); + ppm_handle = NULL; return ask_user_to_ppm_error(ret); } + + ret = atexit(ppm_free_client); + if (ret != 0) { + askuser_client_finalize(ppm_handle->client); + free(ppm_handle); + ppm_handle = NULL; + return PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN; + } } return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE; @@ -118,8 +256,9 @@ static int ppm_init_client() EXPORT_API int ppm_check_privilege(const char *privilege, ppm_check_result_e *result) { - if (!privilege || !result) + if (!privilege || !result) { return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER; + } int ret = ppm_init_client(); if (ret != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) { @@ -127,36 +266,18 @@ int ppm_check_privilege(const char *privilege, ppm_check_result_e *result) } askuser_check_result check_result; - ret = askuser_client_check_privilege(client, privilege, &check_result); + ret = askuser_client_check_privilege(ppm_handle->client, privilege, &check_result); if (ret != ASKUSER_API_SUCCESS) { return ask_user_to_ppm_error(ret); } - *result = ask_user_check_result_to_pps(check_result); + *result = ask_user_check_result_to_ppm(check_result); return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE; } -static void ppm_popup_response_callback(int request_id, askuser_call_cause cause, - askuser_popup_result result, void *p_user_data) -{ - (void) request_id; - (void) cause; - (void) result; - (void) p_user_data; - - // TODO find callback (request_id) and user_data - - // TODO convert cause and result - - // TODO call callback - - // TODO unregister callback and user_data -} - EXPORT_API -int ppm_popup_request(const char *privilege, ppm_popup_response_cb callback, - void *user_data) +int ppm_popup_request(const char *privilege, ppm_popup_response_cb callback, void *user_data) { if (!privilege || !callback) { return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER; @@ -167,16 +288,21 @@ int ppm_popup_request(const char *privilege, ppm_popup_response_cb callback, return ret; } - int request_id; - ret = askuser_client_popup_request(client, privilege, ppm_popup_response_callback, - ppm_private, &request_id); + ppm_callback_closure *callback_closure = (ppm_callback_closure *) calloc(1, sizeof(ppm_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_request(ppm_handle->client, privilege, ppm_popup_response_callback, + callback_closure, NULL); if (ret != ASKUSER_API_SUCCESS) { + free(callback_closure); return ask_user_to_ppm_error(ret); } - // TODO store callback and user_data under request_id key - (void) callback; - (void) user_data; - return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE; } + diff --git a/src/capi/include/privacy_privilege_manager.h b/src/capi/include/privacy_privilege_manager.h index da5a7c0..780a76a 100644 --- a/src/capi/include/privacy_privilege_manager.h +++ b/src/capi/include/privacy_privilege_manager.h @@ -51,6 +51,8 @@ typedef enum PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /** Out of memory */ PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, + /** Unknown error */ + PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN = TIZEN_ERROR_UNKNOWN, } ppm_error_e; /** @@ -136,6 +138,7 @@ typedef void (*ppm_popup_response_cb) (ppm_call_cause_e cause, * @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 */ int ppm_check_privilege(const char *privilege, ppm_check_result_e *result); @@ -179,6 +182,7 @@ int ppm_check_privilege(const char *privilege, ppm_check_result_e *result); * @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 * * @post ppm_popup_response_cb() will be invoked. * @see ppm_popup_response_cb() diff --git a/src/capi/test/privacy_privilege_manager_test.c b/src/capi/test/privacy_privilege_manager_test.c new file mode 100644 index 0000000..c37c62e --- /dev/null +++ b/src/capi/test/privacy_privilege_manager_test.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file privacy_privilege_manager.c + * @author Piotr Sawicki + * @version 1.0 + * @brief This file contains test of CAPI for Privacy Privilege Manager + */ + +#include +#include +#include +#include +#include + +#include + +const char *result_to_str(ppm_popup_result_e result) +{ + switch (result) { + case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_ALLOW_FOREVER: + return "ALLOW_FOREVER"; + case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_FOREVER: + return "DENY_FOREVER"; + case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_ONCE: + return "DENY_ONCE"; + } + + return "UNKNOWN"; +} + +void popup_response_cb(ppm_call_cause_e cause, ppm_popup_result_e result, void *user_data) +{ + int id = (int) user_data; + + printf("resp_id: %d ", id); + switch (cause) { + case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER: + printf("popup answer: %s\n", result_to_str(result)); + break; + case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR: + printf("popup cause PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR\n"); + break; + } + + printf(">"); + fflush(stdout); +} + +static void print_help() +{ + printf("0 - check privilege status\n"); + printf("1 - send popup request\n"); + printf("h - print help\n"); + printf("q - quit\n"); + printf(">"); + fflush(stdout); +} + +static void print_error(ppm_error_e error) +{ + switch (error) { + case PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE: + break; + case PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR: + printf("I/O error\n"); + break; + case PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER: + printf("Invalid parameters\n"); + break; + case PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY: + printf("Out of memory\n"); + case PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN: + printf("Unknown error\n"); + break; + } +} + +static void print_check_result(ppm_check_result_e result) +{ + printf("res: "); + + switch (result) { + case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW: + printf("ALLOW\n"); + break; + case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY: + printf("DENY\n"); + break; + case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ASK: + printf("ASK\n"); + break; + } + + printf(">"); + fflush(stdout); +} + +gboolean main_loop_cb(GIOChannel *channel, GIOCondition cond, gpointer user_data) +{ + int ret; + char buffer[4096]; + ppm_check_result_e result; + + (void) channel; (void) cond; (void) user_data; + + ret = read(0, buffer, sizeof(buffer)); + if (ret < 0) { + printf("read from stdin failed\n"); + exit(1); + } + + buffer[ret - 1] = '\0'; + int id = rand() % 1024; + + switch (buffer[0]) { + case '0': + if (strlen(buffer) < 3) { + print_help(); + break; + } + ret = ppm_check_privilege(&buffer[2], &result); + if (ret == PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) { + print_check_result(result); + } + else { + print_error(ret); + } + break; + case '1': + if (strlen(buffer) < 3) { + print_help(); + break; + } + buffer[ret - 1] = '\0'; + print_error(ppm_popup_request(&buffer[2], popup_response_cb, (void *)id)); + break; + case 'q': + exit(0); + case 'h': + default: + print_help(); + break; + } + + return TRUE; +} + +int main(int argc, char **argv) +{ + (void) argc; (void) argv; + + srand(time(0)); + print_help(); + + GMainLoop *mainloop = g_main_loop_new(NULL, FALSE); + + GIOChannel *channel = g_io_channel_unix_new(0); + g_io_add_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, main_loop_cb, NULL); + + g_main_loop_run(mainloop); + + return 0; +}