From 275f7f5b9d2b148ae61da39c87f81c75974ab31f Mon Sep 17 00:00:00 2001 From: Jose Bollo Date: Fri, 3 Jan 2020 18:36:02 +0100 Subject: Allow to set the user in queries Instead of enforcing administrator to log as user, allow him to explicitely tell the uid of the application to launch or browse. This change also includes a refactor of how parameters are scanned. Bug-AGL: SPEC-3085 Change-Id: Id2c4e6ccfc0a59f3ec694ec45afc1f35496273e6 Signed-off-by: Jose Bollo --- scripts/afm-util.in | 44 +++-- src/afm-binding.c | 498 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 349 insertions(+), 193 deletions(-) diff --git a/scripts/afm-util.in b/scripts/afm-util.in index 2cc714b..4594685 100755 --- a/scripts/afm-util.in +++ b/scripts/afm-util.in @@ -1,10 +1,29 @@ #!/bin/bash send() { - afb-client-demo -H -d unix:@afm_platform_rundir@/apis/ws/afm-main "$1" "$2" | - awk '$1=="ON-REPLY" && $3!="success"{$1="ERROR:";$2="";print > "/dev/stderr";exit 1;}NR>1' + local verb="$1" + afb-client-demo -H -d unix:@afm_platform_rundir@/apis/ws/afm-main "$verb" "$2" | + awk '$1=="ON-REPLY" && $3!="success"{$1="ERROR:";$2="";print > "/dev/stderr";exit 1;}NR>1' } +all=false +force=false +uid="$UID" +help=false + +set -- $(getopt -l all,force,help,uid: -s afhu: -n afm-util -- "$@") +while : +do + case "$1" in + -a|--all) all=true; shift;; + -f|--force) force=true; shift;; + -h|--help) help=true; shift;; + -u|--uid) uid="$2"; shift 2;; + --) shift; break;; + *) help=true; break;; + esac +done + getall() { case "$1" in -a|--all) echo -n '{"all":true}';; @@ -15,14 +34,14 @@ getall() { case "$1" in list|runnables) - send runnables $(getall $2) + send runnables "{\"all\":$all,\"uid\":$uid}" ;; add|install) f=$(realpath $2) r=true if [[ "$(basename $0)" = "afm-install" ]]; then r=false; fi - send install '{"wgt":"'"$f"'","force":true,"reload":'"$r"'}' + send install "{\"wgt\":\"$f\",\"force\":$force,\"reload\":$r}" ;; remove|uninstall) @@ -32,36 +51,31 @@ case "$1" in info|detail) i=$2 - send detail "\"$i\"" + send detail "{\"id\":$i,\"uid\":$uid}" ;; ps|runners) - send runners $(getall $2) + send runners "{\"all\":$all,\"uid\":$uid}" ;; run|start) i=$2 - send start "\"$i\"" - ;; - - run-remote|start-remote) - i=$2 - send start '{"id":"'"$i"'","mode":"remote"}' + send start "{\"id\":$i,\"uid\":$uid}" ;; once) i=$2 - send once "\"$i\"" + send once "{\"id\":$i,\"uid\":$uid}" ;; terminate|kill) i=$2 - send terminate "$i" + send terminate "{\"runid\":$i,\"uid\":$uid}" ;; state|status) i=$2 - send state "$i" + send state "{\"runid\":$i,\"uid\":$uid}" ;; -h|--help|help) diff --git a/src/afm-binding.c b/src/afm-binding.c index 7e99b20..255f3b8 100644 --- a/src/afm-binding.c +++ b/src/afm-binding.c @@ -46,6 +46,8 @@ static const char _a_l_c_[] = "application-list-changed"; static const char _bad_request_[] = "bad-request"; static const char _cannot_start_[] = "cannot-start"; static const char _detail_[] = "detail"; +static const char _forbidden_[] = "forbidden"; +static const char _force_[] = "force"; static const char _id_[] = "id"; static const char _install_[] = "install"; static const char _lang_[] = "lang"; @@ -53,15 +55,19 @@ static const char _not_found_[] = "not-found"; static const char _not_running_[] = "not-running"; static const char _once_[] = "once"; static const char _pause_[] = "pause"; +static const char _reload_[] = "reload"; static const char _resume_[] = "resume"; +static const char _root_[] = "root"; static const char _runid_[] = "runid"; static const char _runnables_[] = "runnables"; static const char _runners_[] = "runners"; static const char _start_[] = "start"; static const char _state_[] = "state"; static const char _terminate_[] = "terminate"; +static const char _uid_[] = "uid"; static const char _uninstall_[] = "uninstall"; static const char _update_[] = "update"; +static const char _wgt_[] = "wgt"; /* * the permissions @@ -79,10 +85,6 @@ static const struct afb_auth .type = afb_auth_Permission, .text = FWK_PREFIX"permission:afm:system:widget:uninstall" }, - auth_perm_widget_preinstall = { - .type = afb_auth_Permission, - .text = FWK_PREFIX"permission:afm:system:widget:preinstall" - }, auth_perm_widget_detail = { .type = afb_auth_Permission, .text = FWK_PREFIX"permission:afm:system:widget:detail" @@ -107,6 +109,10 @@ static const struct afb_auth .type = afb_auth_Permission, .text = FWK_PREFIX"permission:afm:system:runner:kill" }, + auth_perm_set_uid = { + .type = afb_auth_Permission, + .text = FWK_PREFIX"permission:afm:system:set-uid" + }, auth_install = { .type = afb_auth_Or, @@ -118,11 +124,6 @@ static const struct afb_auth .first = &auth_perm_widget, .next = &auth_perm_widget_uninstall }, - auth_preinstall = { - .type = afb_auth_Or, - .first = &auth_perm_widget, - .next = &auth_perm_widget_preinstall - }, auth_detail = { .type = afb_auth_Or, .first = &auth_perm_widget, @@ -150,10 +151,55 @@ static const struct afb_auth } ; +/** + * Enumerate the possible arguments + * This is intended to be used as a mask of bits + * telling what parameter is expected, optional, + * and, finally, set. + */ +enum { + Param_Lang = 1, + Param_All = 2, + Param_Force = 4, + Param_Reload = 8, + Param_Id = 16, + Param_RunId = 32, + Param_WGT = 64, + Param_Root = 128 +}; + +/** + * Records the parameters of verb queries + */ +struct params { + /** bit mask of the given param */ + unsigned found; + /** value of param 'all' if set */ + int all; + /** value of param 'force' if set */ + int force; + /** value of param 'reload' if set */ + int reload; + /** value of param 'uid' if set */ + int uid; + /** value of param 'runid' if set */ + int runid; + /** value of param 'lang' if set */ + const char *lang; + /** value of param 'id' if set */ + const char *id; + /** value of param 'wgt' if set */ + const char *wgt; + /** value of param 'root' if set */ + const char *root; + /** object value of parameters */ + struct json_object *args; +}; + /* * default root */ -static const char *rootdir = FWK_APP_DIR; +static const char rootdir[] = FWK_APP_DIR; /* * the internal application database @@ -186,6 +232,15 @@ static void bad_request(afb_req_t req) afb_req_fail(req, _bad_request_, NULL); } +/* forbidden request reply */ +static void forbidden_request(afb_req_t req) +{ + INFO("forbidden request verb %s: %s", + afb_req_get_called_verb(req), + json_object_to_json_string(afb_req_json(req))); + afb_req_fail(req, _forbidden_, NULL); +} + /* common not found reply */ static void not_found(afb_req_t req) { @@ -204,6 +259,28 @@ static void cant_start(afb_req_t req) afb_req_fail(req, _cannot_start_, NULL); } +/* emulate missing function */ +static int has_auth(afb_req_t req, const struct afb_auth *auth) +{ + switch (auth->type) { + case afb_auth_Permission: + return afb_req_has_permission(req, auth->text); + case afb_auth_Or: + return has_auth(req, auth->first) || has_auth(req, auth->next); + case afb_auth_And: + return has_auth(req, auth->first) && has_auth(req, auth->next); + case afb_auth_Not: + return !has_auth(req, auth->first); + case afb_auth_Yes: + return 1; + case afb_auth_No: + case afb_auth_Token: + case afb_auth_LOA: + default: + return 0; + } +} + /* * Broadcast the event "application-list-changed". * This event is sent was the event "changed" is received from dbus. @@ -215,103 +292,173 @@ static void application_list_changed(const char *operation, const char *data) afb_event_broadcast(applist_changed_event, e); } -/* - * Retrieve the required language from 'req'. +/** + * common routine for getting parameters */ -static const char *get_lang(afb_req_t req) +static int get_params(afb_req_t req, unsigned mandatory, unsigned optional, struct params *params) { - const char *lang; - - /* get the optional language */ - lang = afb_req_value(req, _lang_); - - /* TODO use the req to get the lang of the session (if any) */ - - return lang; -} - -/* - * Retrieve whether all is required from 'req'. - */ -static int get_all(afb_req_t req) -{ - struct json_object *val; - - /* get the optional language */ - return json_object_object_get_ex(afb_req_json(req), _all_, &val) - && json_object_get_boolean(val); -} - -/* - * retrieves the 'appid' in parameters received with the - * request 'req'. - * - * Returns 1 in case of success. - * Otherwise, if the 'appid' can't be retrieved, an error stating - * the bad request is replied for 'req' and 0 is returned. - */ -static int onappid(afb_req_t req, const char **appid) -{ - struct json_object *json; - - /* get the paramaters of the request */ - json = afb_req_json(req); - - /* get the appid if any */ - if (!wrap_json_unpack(json, "s", appid) - || !wrap_json_unpack(json, "{ss}", _id_, appid)) { - /* found */ - INFO("method %s called for %s", afb_req_get_called_verb(req), *appid); - return 1; + enum { + no_error = 0, + error_bad_request = 1, + error_not_found = 2, + error_not_running = 3, + error_forbidden = 4 + }; + + int id, error; + struct json_object *args, *obj; + unsigned found, expected; + + /* init */ + expected = optional|mandatory; + memset(params, 0, sizeof *params); + error = no_error; + found = 0; + params->uid = afb_req_get_uid(req); + args = afb_req_json(req); + + /* args is a numeric value: a run id */ + if (json_object_is_type(args, json_type_int)) { + if (expected & Param_RunId) { + params->runid = json_object_get_int(args); + found |= Param_RunId; + } } - - /* nothing appropriate */ - bad_request(req); - return 0; -} - -/* - * retrieves the 'runid' in parameters received with the - * request 'req'. - * - * Returns 1 in case of success. - * Otherwise, if the 'runid' can't be retrieved, an error stating - * the bad request is replied for 'req' and 0 is returned. - */ -static int onrunid(afb_req_t req, int *runid) -{ - struct json_object *json; - const char *appid; - - /* get the paramaters of the request */ - json = afb_req_json(req); - - /* get the runid if any */ - if (!wrap_json_unpack(json, "i", runid) - || !wrap_json_unpack(json, "{si}", _runid_, runid)) { - INFO("method %s called for %d", afb_req_get_called_verb(req), *runid); - return 1; + + /* args is a string value: either an ID or a widget path */ + else if (json_object_is_type(args, json_type_string)) { + if (expected & (Param_Id | Param_RunId)) { + params->id = json_object_get_string(args); + found |= Param_Id; + } + else if (expected & Param_WGT) { + params->wgt = json_object_get_string(args); + found |= Param_WGT; + } + } + + /* args is a object value: inspect it */ + else if (json_object_is_type(args, json_type_object)) { + /* get UID */ + if (json_object_object_get_ex(args, _uid_, &obj)) { + if (!json_object_is_type(obj, json_type_int)) + error = 1; + else { + id = json_object_get_int(obj); + if (id < 0) + error = error_bad_request; + else if (params->uid != id) { + if (!afb_req_has_permission(req, auth_perm_set_uid.text)) + error = error_forbidden; + else { + params->uid = id; + } + } + } + } + + /* get all */ + if ((expected & Param_All) + && json_object_object_get_ex(args, _all_, &obj)) { + params->all = json_object_get_boolean(obj); + if (params->all && !has_auth(req, &auth_view_all)) + error = error_forbidden; + else + found |= Param_All; + } + + /* get force */ + if ((expected & Param_Force) + && json_object_object_get_ex(args, _force_, &obj)) { + params->force = json_object_get_boolean(obj); + found |= Param_Force; + } + + /* get reload */ + if ((expected & Param_Reload) + && json_object_object_get_ex(args, _reload_, &obj)) { + params->reload = json_object_get_boolean(obj); + found |= Param_Reload; + } + + /* get languages */ + if ((expected & Param_Lang) + && json_object_object_get_ex(args, _lang_, &obj)) { + params->lang = json_object_get_string(obj); + found |= Param_Lang; + } + + /* get root */ + if ((expected & Param_Root) + && json_object_object_get_ex(args, _root_, &obj)) { + params->root = json_object_get_string(obj); + found |= Param_Root; + } + + /* get WGT */ + if (expected & Param_WGT) { + if (json_object_object_get_ex(args, _wgt_, &obj)) { + params->wgt = json_object_get_string(obj); + found |= Param_WGT; + } + } + + /* get appid */ + if (expected & (Param_Id | Param_RunId)) { + if (json_object_object_get_ex(args, _id_, &obj)) { + params->id = json_object_get_string(obj); + found |= Param_Id; + } + } + + /* get runid */ + if (expected & Param_RunId) { + if (json_object_object_get_ex(args, _runid_, &obj)) { + if (json_object_is_type(obj, json_type_int)) + error = error_bad_request; + else { + params->runid = json_object_get_int(obj); + found |= Param_RunId; + } + } + } } - /* get the appid if any */ - if (!onappid(req, &appid)) - return 0; - - /* search the runid of the appid */ - *runid = afm_urun_search_runid(afudb, appid, afb_req_get_uid(req)); - if (*runid < 0) { - /* nothing appropriate */ - INFO("method %s can't get runid for %s: %m", afb_req_get_called_verb(req), - appid); - if (errno == ESRCH) - not_running(req); + /* deduce the runid from the uid on need */ + if ((mandatory & Param_RunId) && !(found & Param_RunId) && (found & Param_Id)) { + id = afm_urun_search_runid(afudb, params->id, params->uid); + if (id > 0) { + params->runid = id; + found |= Param_RunId; + } + else if (errno == ESRCH) + error = error_not_running; else + error = error_not_found; + } + + /* check all mandatory are here */ + if (error != no_error || (mandatory & found) != mandatory) { + switch(error) { + case error_not_found: not_found(req); + break; + case error_not_running: + not_running(req); + break; + case error_forbidden: + forbidden_request(req); + break; + case error_bad_request: + default: + bad_request(req); + break; + } return 0; } - /* found */ - INFO("method %s called for %s -> %d", afb_req_get_called_verb(req), appid, *runid); + params->args = args; + params->found = found; return 1; } @@ -341,18 +488,15 @@ static void reply_status(afb_req_t req, int status) */ static void runnables(afb_req_t req) { - int all; - const char *lang; + struct params params; struct json_object *resp; - /* get the language */ - lang = get_lang(req); - - /* get the all */ - all = get_all(req); + /* scan the request */ + if (!get_params(req, 0, Param_Lang|Param_All, ¶ms)) + return; - /* get the details */ - resp = afm_udb_applications_public(afudb, all, afb_req_get_uid(req), lang); + /* get the applications */ + resp = afm_udb_applications_public(afudb, params.all, params.uid, params.lang); afb_req_success(req, resp, NULL); } @@ -361,19 +505,15 @@ static void runnables(afb_req_t req) */ static void detail(afb_req_t req) { - const char *lang; - const char *appid; + struct params params; struct json_object *resp; /* scan the request */ - if (!onappid(req, &appid)) + if (!get_params(req, Param_Id, Param_Lang, ¶ms)) return; - /* get the language */ - lang = get_lang(req); - - /* wants details for appid */ - resp = afm_udb_get_application_public(afudb, appid, afb_req_get_uid(req), lang); + /* get the details */ + resp = afm_udb_get_application_public(afudb, params.id, params.uid, params.lang); if (resp) afb_req_success(req, resp, NULL); else @@ -385,23 +525,23 @@ static void detail(afb_req_t req) */ static void start(afb_req_t req) { - const char *appid; + struct params params; struct json_object *appli, *resp; int runid; /* scan the request */ - if (!onappid(req, &appid)) + if (!get_params(req, Param_Id, 0, ¶ms)) return; /* get the application */ - appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req)); + appli = afm_udb_get_application_private(afudb, params.id, params.uid); if (appli == NULL) { not_found(req); return; } /* launch the application */ - runid = afm_urun_start(appli, afb_req_get_uid(req)); + runid = afm_urun_start(appli, params.uid); if (runid <= 0) { cant_start(req); return; @@ -409,11 +549,7 @@ static void start(afb_req_t req) /* returns */ resp = NULL; -#if 0 - wrap_json_pack(&resp, "{si}", _runid_, runid); -#else wrap_json_pack(&resp, "i", runid); -#endif afb_req_success(req, resp, NULL); } @@ -422,30 +558,30 @@ static void start(afb_req_t req) */ static void once(afb_req_t req) { - const char *appid; + struct params params; struct json_object *appli, *resp; int runid; /* scan the request */ - if (!onappid(req, &appid)) + if (!get_params(req, Param_Id, 0, ¶ms)) return; /* get the application */ - appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req)); + appli = afm_udb_get_application_private(afudb, params.id, params.uid); if (appli == NULL) { not_found(req); return; } /* launch the application */ - runid = afm_urun_once(appli, afb_req_get_uid(req)); + runid = afm_urun_once(appli, params.uid); if (runid <= 0) { cant_start(req); return; } /* returns the state */ - resp = afm_urun_state(afudb, runid, afb_req_get_uid(req)); + resp = afm_urun_state(afudb, runid, params.uid); afb_req_success(req, resp, NULL); } @@ -454,9 +590,12 @@ static void once(afb_req_t req) */ static void pause(afb_req_t req) { - int runid, status; - if (onrunid(req, &runid)) { - status = afm_urun_pause(runid, afb_req_get_uid(req)); + struct params params; + int status; + + /* scan the request */ + if (get_params(req, Param_RunId, 0, ¶ms)) { + status = afm_urun_pause(params.runid, params.uid); reply_status(req, status); } } @@ -466,9 +605,12 @@ static void pause(afb_req_t req) */ static void resume(afb_req_t req) { - int runid, status; - if (onrunid(req, &runid)) { - status = afm_urun_resume(runid, afb_req_get_uid(req)); + struct params params; + int status; + + /* scan the request */ + if (get_params(req, Param_RunId, 0, ¶ms)) { + status = afm_urun_resume(params.runid, params.uid); reply_status(req, status); } } @@ -478,9 +620,12 @@ static void resume(afb_req_t req) */ static void terminate(afb_req_t req) { - int runid, status; - if (onrunid(req, &runid)) { - status = afm_urun_terminate(runid, afb_req_get_uid(req)); + struct params params; + int status; + + /* scan the request */ + if (get_params(req, Param_RunId, 0, ¶ms)) { + status = afm_urun_terminate(params.runid, params.uid); reply_status(req, status); } } @@ -490,10 +635,14 @@ static void terminate(afb_req_t req) */ static void runners(afb_req_t req) { - int all; + struct params params; struct json_object *resp; - all = get_all(req); - resp = afm_urun_list(afudb, all, afb_req_get_uid(req)); + + /* scan the request */ + if (!get_params(req, 0, Param_All, ¶ms)) + return; + + resp = afm_urun_list(afudb, params.all, params.uid); afb_req_success(req, resp, NULL); } @@ -502,10 +651,12 @@ static void runners(afb_req_t req) */ static void state(afb_req_t req) { - int runid; + struct params params; struct json_object *resp; - if (onrunid(req, &runid)) { - resp = afm_urun_state(afudb, runid, afb_req_get_uid(req)); + + /* scan the request */ + if (get_params(req, Param_RunId, 0, ¶ms)) { + resp = afm_urun_state(afudb, params.runid, params.uid); reply(req, resp); } } @@ -515,38 +666,36 @@ static void state(afb_req_t req) */ static void install(afb_req_t req) { - const char *wgtfile; - const char *root; - int force; - int reload; + struct params params; struct wgt_info *ifo; - struct json_object *json; struct json_object *resp; - /* default settings */ - root = rootdir; - force = 0; - reload = 1; - /* scan the request */ - json = afb_req_json(req); - if (wrap_json_unpack(json, "s", &wgtfile) - && wrap_json_unpack(json, "{ss s?s s?b s?b}", - "wgt", &wgtfile, - "root", &root, - "force", &force, - "reload", &reload)) { - return bad_request(req); + if (!get_params(req, Param_WGT, Param_Root|Param_Force|Param_Reload, ¶ms)) + return; + + /* check if force is allowed */ + if (params.force) { + if (!has_auth(req, &auth_uninstall)) { + forbidden_request(req); + return; + } } + /* supply default values */ + if (!(params.found & Param_Reload)) + params.reload = 1; + if (!(params.found & Param_Root)) + params.root = rootdir; + /* install the widget */ - ifo = install_widget(wgtfile, root, force); + ifo = install_widget(params.wgt, params.root, params.force); if (ifo == NULL) afb_req_fail_f(req, "failed", "installation failed: %m"); else { afm_udb_update(afudb); /* reload if needed */ - if (reload) + if (params.reload) do_reloads(); /* build the response */ @@ -564,31 +713,25 @@ static void install(afb_req_t req) */ static void uninstall(afb_req_t req) { - const char *idaver; - const char *root; - struct json_object *json; + struct params params; int rc; - /* default settings */ - root = rootdir; - /* scan the request */ - json = afb_req_json(req); - if (wrap_json_unpack(json, "s", &idaver) - && wrap_json_unpack(json, "{ss s?s}", - _id_, &idaver, - "root", &root)) { - return bad_request(req); - } + if (!get_params(req, Param_Id, Param_Root|Param_Reload, ¶ms)) + return; + if (!(params.found & Param_Reload)) + params.reload = 1; + if (!(params.found & Param_Root)) + params.root = rootdir; /* install the widget */ - rc = uninstall_widget(idaver, root); + rc = uninstall_widget(params.id, params.root); if (rc) afb_req_fail_f(req, "failed", "uninstallation failed: %m"); else { afm_udb_update(afudb); afb_req_success(req, NULL, NULL); - application_list_changed(_uninstall_, idaver); + application_list_changed(_uninstall_, params.id); } } @@ -643,4 +786,3 @@ const afb_binding_t afbBindingExport = { .onevent = NULL, .noconcurrency = 1 /* relies on binder for serialization of requests */ }; - -- cgit 1.2.3-korg