Allow to set the user in queries sandbox/jobol/uids
authorJose Bollo <jose.bollo@iot.bzh>
Fri, 3 Jan 2020 17:36:02 +0000 (18:36 +0100)
committerJose Bollo <jose.bollo@iot.bzh>
Mon, 9 Mar 2020 09:43:49 +0000 (10:43 +0100)
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 <jose.bollo@iot.bzh>
scripts/afm-util.in
src/afm-binding.c

index 2cc714b..4594685 100755 (executable)
@@ -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)
index 7e99b20..255f3b8 100644 (file)
@@ -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, &params))
+               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, &params))
                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, &params))
                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, &params))
                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, &params)) {
+               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, &params)) {
+               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, &params)) {
+               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, &params))
+               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, &params)) {
+               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, &params))
+               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, &params))
+               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 */
 };
-