Allow to set the user in queries
[src/app-framework-main.git] / src / afm-binding.c
1 /*
2  * Copyright (C) 2015-2020 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE         /* See feature_test_macros(7) */
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <signal.h>
24 #include <errno.h>
25
26 #include <json-c/json.h>
27
28 #define AFB_BINDING_VERSION 3
29 #include <afb/afb-binding.h>
30
31 #include "verbose.h"
32 #include "utils-systemd.h"
33 #include "afm-udb.h"
34 #include "afm-urun.h"
35 #include "wgt-info.h"
36 #include "wgtpkg-install.h"
37 #include "wgtpkg-uninstall.h"
38 #include "wrap-json.h"
39
40 /*
41  * constant strings
42  */
43 static const char _added_[]     = "added";
44 static const char _all_[]       = "all";
45 static const char _a_l_c_[]     = "application-list-changed";
46 static const char _bad_request_[] = "bad-request";
47 static const char _cannot_start_[] = "cannot-start";
48 static const char _detail_[]    = "detail";
49 static const char _forbidden_[] = "forbidden";
50 static const char _force_[]     = "force";
51 static const char _id_[]        = "id";
52 static const char _install_[]   = "install";
53 static const char _lang_[]      = "lang";
54 static const char _not_found_[] = "not-found";
55 static const char _not_running_[] = "not-running";
56 static const char _once_[]      = "once";
57 static const char _pause_[]     = "pause";
58 static const char _reload_[]    = "reload";
59 static const char _resume_[]    = "resume";
60 static const char _root_[]      = "root";
61 static const char _runid_[]     = "runid";
62 static const char _runnables_[] = "runnables";
63 static const char _runners_[]   = "runners";
64 static const char _start_[]     = "start";
65 static const char _state_[]     = "state";
66 static const char _terminate_[] = "terminate";
67 static const char _uid_[]       = "uid";
68 static const char _uninstall_[] = "uninstall";
69 static const char _update_[]    = "update";
70 static const char _wgt_[]       = "wgt";
71
72 /*
73  * the permissions
74  */
75 static const struct afb_auth
76         auth_perm_widget = {
77                 .type = afb_auth_Permission,
78                 .text = FWK_PREFIX"permission:afm:system:widget"
79         },
80         auth_perm_widget_install = {
81                 .type = afb_auth_Permission,
82                 .text = FWK_PREFIX"permission:afm:system:widget:install"
83         },
84         auth_perm_widget_uninstall = {
85                 .type = afb_auth_Permission,
86                 .text = FWK_PREFIX"permission:afm:system:widget:uninstall"
87         },
88         auth_perm_widget_detail = {
89                 .type = afb_auth_Permission,
90                 .text = FWK_PREFIX"permission:afm:system:widget:detail"
91         },
92         auth_perm_widget_start = {
93                 .type = afb_auth_Permission,
94                 .text = FWK_PREFIX"permission:afm:system:widget:start"
95         },
96         auth_perm_widget_view_all = {
97                 .type = afb_auth_Permission,
98                 .text = FWK_PREFIX"permission:afm:system:widget:view-all"
99         },
100         auth_perm_runner = {
101                 .type = afb_auth_Permission,
102                 .text = FWK_PREFIX"permission:afm:system:runner"
103         },
104         auth_perm_runner_state = {
105                 .type = afb_auth_Permission,
106                 .text = FWK_PREFIX"permission:afm:system:runner:state"
107         },
108         auth_perm_runner_kill = {
109                 .type = afb_auth_Permission,
110                 .text = FWK_PREFIX"permission:afm:system:runner:kill"
111         },
112         auth_perm_set_uid = {
113                 .type = afb_auth_Permission,
114                 .text = FWK_PREFIX"permission:afm:system:set-uid"
115         },
116
117         auth_install = {
118                 .type = afb_auth_Or,
119                 .first = &auth_perm_widget,
120                 .next = &auth_perm_widget_install
121         },
122         auth_uninstall = {
123                 .type = afb_auth_Or,
124                 .first = &auth_perm_widget,
125                 .next = &auth_perm_widget_uninstall
126         },
127         auth_detail = {
128                 .type = afb_auth_Or,
129                 .first = &auth_perm_widget,
130                 .next = &auth_perm_widget_detail
131         },
132         auth_start = {
133                 .type = afb_auth_Or,
134                 .first = &auth_perm_widget,
135                 .next = &auth_perm_widget_start
136         },
137         auth_view_all = {
138                 .type = afb_auth_Or,
139                 .first = &auth_perm_widget,
140                 .next = &auth_perm_widget_view_all
141         },
142         auth_state = {
143                 .type = afb_auth_Or,
144                 .first = &auth_perm_runner,
145                 .next = &auth_perm_runner_state
146         },
147         auth_kill = {
148                 .type = afb_auth_Or,
149                 .first = &auth_perm_runner,
150                 .next = &auth_perm_runner_kill
151         }
152 ;
153
154 /**
155  * Enumerate the possible arguments
156  * This is intended to be used as a mask of bits
157  * telling what parameter is expected, optional, 
158  * and, finally, set.
159  */
160 enum {
161         Param_Lang   = 1,
162         Param_All    = 2,
163         Param_Force  = 4,
164         Param_Reload = 8,
165         Param_Id     = 16,
166         Param_RunId  = 32,
167         Param_WGT    = 64,
168         Param_Root   = 128
169 };
170
171 /**
172  * Records the parameters of verb queries
173  */
174 struct params {
175         /** bit mask of the given param */
176         unsigned found;
177         /** value of param 'all' if set */
178         int all;
179         /** value of param 'force' if set */
180         int force;
181         /** value of param 'reload' if set */
182         int reload;
183         /** value of param 'uid' if set */
184         int uid;
185         /** value of param 'runid' if set */
186         int runid;
187         /** value of param 'lang' if set */
188         const char *lang;
189         /** value of param 'id' if set */
190         const char *id;
191         /** value of param 'wgt' if set */
192         const char *wgt;
193         /** value of param 'root' if set */
194         const char *root;
195         /** object value of parameters */
196         struct json_object *args;
197 };
198
199 /*
200  * default root
201  */
202 static const char rootdir[] = FWK_APP_DIR;
203
204 /*
205  * the internal application database
206  */
207 static struct afm_udb *afudb;
208
209 /*
210  * the event signaling that application list changed
211  */
212 static afb_event_t applist_changed_event;
213
214 /*
215  * the preallocated true json_object
216  */
217 static struct json_object *json_true;
218
219 /* enforce daemon reload */
220 static void do_reloads()
221 {
222         systemd_daemon_reload(0);
223         systemd_unit_restart_name(0, "sockets.target");
224 }
225
226 /* common bad request reply */
227 static void bad_request(afb_req_t req)
228 {
229         INFO("bad request verb %s: %s",
230                 afb_req_get_called_verb(req),
231                 json_object_to_json_string(afb_req_json(req)));
232         afb_req_fail(req, _bad_request_, NULL);
233 }
234
235 /* forbidden request reply */
236 static void forbidden_request(afb_req_t req)
237 {
238         INFO("forbidden request verb %s: %s",
239                 afb_req_get_called_verb(req),
240                 json_object_to_json_string(afb_req_json(req)));
241         afb_req_fail(req, _forbidden_, NULL);
242 }
243
244 /* common not found reply */
245 static void not_found(afb_req_t req)
246 {
247         afb_req_fail(req, _not_found_, NULL);
248 }
249
250 /* common not running reply */
251 static void not_running(afb_req_t req)
252 {
253         afb_req_fail(req, _not_running_, NULL);
254 }
255
256 /* common can't start reply */
257 static void cant_start(afb_req_t req)
258 {
259         afb_req_fail(req, _cannot_start_, NULL);
260 }
261
262 /* emulate missing function */
263 static int has_auth(afb_req_t req, const struct afb_auth *auth)
264 {
265         switch (auth->type) {
266         case afb_auth_Permission:
267                 return afb_req_has_permission(req, auth->text);
268         case afb_auth_Or:
269                 return has_auth(req, auth->first) || has_auth(req, auth->next);
270         case afb_auth_And:
271                 return has_auth(req, auth->first) && has_auth(req, auth->next);
272         case afb_auth_Not:
273                 return !has_auth(req, auth->first);
274         case afb_auth_Yes:
275                 return 1;
276         case afb_auth_No:
277         case afb_auth_Token:
278         case afb_auth_LOA:
279         default:
280                 return 0;
281         }
282 }
283
284 /*
285  * Broadcast the event "application-list-changed".
286  * This event is sent was the event "changed" is received from dbus.
287  */
288 static void application_list_changed(const char *operation, const char *data)
289 {
290         struct json_object *e = NULL;
291         wrap_json_pack(&e, "{ss ss}", "operation", operation, "data", data);
292         afb_event_broadcast(applist_changed_event, e);
293 }
294
295 /**
296  * common routine for getting parameters
297  */
298 static int get_params(afb_req_t req, unsigned mandatory, unsigned optional, struct params *params)
299 {
300         enum {
301                 no_error = 0,
302                 error_bad_request = 1,
303                 error_not_found = 2,
304                 error_not_running = 3,
305                 error_forbidden = 4
306         };
307
308         int id, error;
309         struct json_object *args, *obj;
310         unsigned found, expected;
311
312         /* init */
313         expected = optional|mandatory;
314         memset(params, 0, sizeof *params);
315         error = no_error;
316         found = 0;
317         params->uid = afb_req_get_uid(req);
318         args = afb_req_json(req);
319
320         /* args is a numeric value: a run id */
321         if (json_object_is_type(args, json_type_int)) {
322                 if (expected & Param_RunId) {
323                         params->runid = json_object_get_int(args);
324                         found |= Param_RunId;
325                 }
326         }
327         
328         /* args is a string value: either an ID or a widget path */
329         else if (json_object_is_type(args, json_type_string)) {
330                 if (expected & (Param_Id | Param_RunId)) {
331                         params->id = json_object_get_string(args);
332                         found |= Param_Id;
333                 }
334                 else if (expected & Param_WGT) {
335                         params->wgt = json_object_get_string(args);
336                         found |= Param_WGT;
337                 }
338         }
339         
340         /* args is a object value: inspect it */
341         else if (json_object_is_type(args, json_type_object)) {
342                 /* get UID */
343                 if (json_object_object_get_ex(args, _uid_, &obj)) {
344                         if (!json_object_is_type(obj, json_type_int))
345                                 error = 1;
346                         else {
347                                 id = json_object_get_int(obj);
348                                 if (id < 0)
349                                         error = error_bad_request;
350                                 else if (params->uid != id) {
351                                         if (!afb_req_has_permission(req, auth_perm_set_uid.text))
352                                                 error = error_forbidden;
353                                         else {
354                                                 params->uid = id;
355                                         }
356                                 }
357                         }
358                 }
359
360                 /* get all */
361                 if ((expected & Param_All)
362                 && json_object_object_get_ex(args, _all_, &obj)) {
363                         params->all = json_object_get_boolean(obj);
364                         if (params->all && !has_auth(req, &auth_view_all))
365                                 error = error_forbidden;
366                         else
367                                 found |= Param_All;
368                 }
369
370                 /* get force */
371                 if ((expected & Param_Force)
372                 && json_object_object_get_ex(args, _force_, &obj)) {
373                         params->force = json_object_get_boolean(obj);
374                         found |= Param_Force;
375                 }
376
377                 /* get reload */
378                 if ((expected & Param_Reload)
379                 && json_object_object_get_ex(args, _reload_, &obj)) {
380                         params->reload = json_object_get_boolean(obj);
381                         found |= Param_Reload;
382                 }
383
384                 /* get languages */
385                 if ((expected & Param_Lang)
386                 && json_object_object_get_ex(args, _lang_, &obj)) {
387                         params->lang = json_object_get_string(obj);
388                         found |= Param_Lang;
389                 }
390
391                 /* get root */
392                 if ((expected & Param_Root)
393                 && json_object_object_get_ex(args, _root_, &obj)) {
394                         params->root = json_object_get_string(obj);
395                         found |= Param_Root;
396                 }
397
398                 /* get WGT */
399                 if (expected & Param_WGT) {
400                         if (json_object_object_get_ex(args, _wgt_, &obj)) {
401                                 params->wgt = json_object_get_string(obj);
402                                 found |= Param_WGT;
403                         }
404                 }
405
406                 /* get appid */
407                 if (expected & (Param_Id | Param_RunId)) {
408                         if (json_object_object_get_ex(args, _id_, &obj)) {
409                                 params->id = json_object_get_string(obj);
410                                 found |= Param_Id;
411                         }
412                 }
413
414                 /* get runid */
415                 if (expected & Param_RunId) {
416                         if (json_object_object_get_ex(args, _runid_, &obj)) {
417                                 if (json_object_is_type(obj, json_type_int))
418                                         error = error_bad_request;
419                                 else {
420                                         params->runid = json_object_get_int(obj);
421                                         found |= Param_RunId;
422                                 }
423                         }
424                 }
425         }
426
427         /* deduce the runid from the uid on need */
428         if ((mandatory & Param_RunId) && !(found & Param_RunId) && (found & Param_Id)) {
429                 id = afm_urun_search_runid(afudb, params->id, params->uid);
430                 if (id > 0) {
431                         params->runid = id;
432                         found |= Param_RunId;
433                 }
434                 else if (errno == ESRCH)
435                         error = error_not_running;
436                 else
437                         error = error_not_found;
438         }
439
440         /* check all mandatory are here */
441         if (error != no_error || (mandatory & found) != mandatory) {
442                 switch(error) {
443                 case error_not_found:
444                         not_found(req);
445                         break;
446                 case error_not_running:
447                         not_running(req);
448                         break;
449                 case error_forbidden:
450                         forbidden_request(req);
451                         break;
452                 case error_bad_request:
453                 default:
454                         bad_request(req);
455                         break;
456                 }
457                 return 0;
458         }
459
460         params->args = args;
461         params->found = found;
462         return 1;
463 }
464
465 /*
466  * Sends the reply 'resp' to the request 'req' if 'resp' is not NULLzero.
467  * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
468  */
469 static void reply(afb_req_t req, struct json_object *resp)
470 {
471         if (resp)
472                 afb_req_reply(req, resp, NULL, NULL);
473         else
474                 afb_req_reply(req, NULL, "failed", strerror(errno));
475 }
476
477 /*
478  * Sends the reply "true" to the request 'req' if 'status' is zero.
479  * Otherwise, when 'status' is not zero replies the error string 'errstr'.
480  */
481 static void reply_status(afb_req_t req, int status)
482 {
483         reply(req, status ? NULL : json_object_get(json_true));
484 }
485
486 /*
487  * On query "runnables"
488  */
489 static void runnables(afb_req_t req)
490 {
491         struct params params;
492         struct json_object *resp;
493
494         /* scan the request */
495         if (!get_params(req, 0, Param_Lang|Param_All, &params))
496                 return;
497
498         /* get the applications */
499         resp = afm_udb_applications_public(afudb, params.all, params.uid, params.lang);
500         afb_req_success(req, resp, NULL);
501 }
502
503 /*
504  * On query "detail"
505  */
506 static void detail(afb_req_t req)
507 {
508         struct params params;
509         struct json_object *resp;
510
511         /* scan the request */
512         if (!get_params(req, Param_Id, Param_Lang, &params))
513                 return;
514
515         /* get the details */
516         resp = afm_udb_get_application_public(afudb, params.id, params.uid, params.lang);
517         if (resp)
518                 afb_req_success(req, resp, NULL);
519         else
520                 not_found(req);
521 }
522
523 /*
524  * On query "start"
525  */
526 static void start(afb_req_t req)
527 {
528         struct params params;
529         struct json_object *appli, *resp;
530         int runid;
531
532         /* scan the request */
533         if (!get_params(req, Param_Id, 0, &params))
534                 return;
535
536         /* get the application */
537         appli = afm_udb_get_application_private(afudb, params.id, params.uid);
538         if (appli == NULL) {
539                 not_found(req);
540                 return;
541         }
542
543         /* launch the application */
544         runid = afm_urun_start(appli, params.uid);
545         if (runid <= 0) {
546                 cant_start(req);
547                 return;
548         }
549
550         /* returns */
551         resp = NULL;
552         wrap_json_pack(&resp, "i", runid);
553         afb_req_success(req, resp, NULL);
554 }
555
556 /*
557  * On query "once"
558  */
559 static void once(afb_req_t req)
560 {
561         struct params params;
562         struct json_object *appli, *resp;
563         int runid;
564
565         /* scan the request */
566         if (!get_params(req, Param_Id, 0, &params))
567                 return;
568
569         /* get the application */
570         appli = afm_udb_get_application_private(afudb, params.id, params.uid);
571         if (appli == NULL) {
572                 not_found(req);
573                 return;
574         }
575
576         /* launch the application */
577         runid = afm_urun_once(appli, params.uid);
578         if (runid <= 0) {
579                 cant_start(req);
580                 return;
581         }
582
583         /* returns the state */
584         resp = afm_urun_state(afudb, runid, params.uid);
585         afb_req_success(req, resp, NULL);
586 }
587
588 /*
589  * On query "pause"
590  */
591 static void pause(afb_req_t req)
592 {
593         struct params params;
594         int status;
595
596         /* scan the request */
597         if (get_params(req, Param_RunId, 0, &params)) {
598                 status = afm_urun_pause(params.runid, params.uid);
599                 reply_status(req, status);
600         }
601 }
602
603 /*
604  * On query "resume" from 'smsg' with parameters of 'obj'.
605  */
606 static void resume(afb_req_t req)
607 {
608         struct params params;
609         int status;
610
611         /* scan the request */
612         if (get_params(req, Param_RunId, 0, &params)) {
613                 status = afm_urun_resume(params.runid, params.uid);
614                 reply_status(req, status);
615         }
616 }
617
618 /*
619  * On query "terminate"
620  */
621 static void terminate(afb_req_t req)
622 {
623         struct params params;
624         int status;
625
626         /* scan the request */
627         if (get_params(req, Param_RunId, 0, &params)) {
628                 status = afm_urun_terminate(params.runid, params.uid);
629                 reply_status(req, status);
630         }
631 }
632
633 /*
634  * On query "runners"
635  */
636 static void runners(afb_req_t req)
637 {
638         struct params params;
639         struct json_object *resp;
640
641         /* scan the request */
642         if (!get_params(req, 0, Param_All, &params))
643                 return;
644
645         resp = afm_urun_list(afudb, params.all, params.uid);
646         afb_req_success(req, resp, NULL);
647 }
648
649 /*
650  * On query "state"
651  */
652 static void state(afb_req_t req)
653 {
654         struct params params;
655         struct json_object *resp;
656
657         /* scan the request */
658         if (get_params(req, Param_RunId, 0, &params)) {
659                 resp = afm_urun_state(afudb, params.runid, params.uid);
660                 reply(req, resp);
661         }
662 }
663
664 /*
665  * On querying installation of widget(s)
666  */
667 static void install(afb_req_t req)
668 {
669         struct params params;
670         struct wgt_info *ifo;
671         struct json_object *resp;
672
673         /* scan the request */
674         if (!get_params(req, Param_WGT, Param_Root|Param_Force|Param_Reload, &params))
675                 return;
676
677         /* check if force is allowed */
678         if (params.force) {
679                 if (!has_auth(req, &auth_uninstall)) {
680                         forbidden_request(req);
681                         return;
682                  }
683         }
684
685         /* supply default values */
686         if (!(params.found & Param_Reload))
687                 params.reload = 1;
688         if (!(params.found & Param_Root))
689                 params.root = rootdir;
690
691         /* install the widget */
692         ifo = install_widget(params.wgt, params.root, params.force);
693         if (ifo == NULL)
694                 afb_req_fail_f(req, "failed", "installation failed: %m");
695         else {
696                 afm_udb_update(afudb);
697                 /* reload if needed */
698                 if (params.reload)
699                         do_reloads();
700
701                 /* build the response */
702                 wrap_json_pack(&resp, "{ss}", _added_, wgt_info_desc(ifo)->idaver);
703                 afb_req_success(req, resp, NULL);
704                 application_list_changed(_install_, wgt_info_desc(ifo)->idaver);
705
706                 /* clean-up */
707                 wgt_info_unref(ifo);
708         }
709 }
710
711 /*
712  * On querying uninstallation of widget(s)
713  */
714 static void uninstall(afb_req_t req)
715 {
716         struct params params;
717         int rc;
718
719         /* scan the request */
720         if (!get_params(req, Param_Id, Param_Root|Param_Reload, &params))
721                 return;
722         if (!(params.found & Param_Reload))
723                 params.reload = 1;
724         if (!(params.found & Param_Root))
725                 params.root = rootdir;
726
727         /* install the widget */
728         rc = uninstall_widget(params.id, params.root);
729         if (rc)
730                 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
731         else {
732                 afm_udb_update(afudb);
733                 afb_req_success(req, NULL, NULL);
734                 application_list_changed(_uninstall_, params.id);
735         }
736 }
737
738 static void onsighup(int signal)
739 {
740         afm_udb_update(afudb);
741         application_list_changed(_update_, _update_);
742 }
743
744 static int init(afb_api_t api)
745 {
746         /* create TRUE */
747         json_true = json_object_new_boolean(1);
748
749         /* init database */
750         afudb = afm_udb_create(1, 0, "afm-");
751         if (!afudb) {
752                 ERROR("afm_udb_create failed");
753                 return -1;
754         }
755
756         signal(SIGHUP, onsighup);
757
758         /* create the event */
759         applist_changed_event = afb_api_make_event(api, _a_l_c_);
760         return -!afb_event_is_valid(applist_changed_event);
761 }
762
763 static const afb_verb_t verbs[] =
764 {
765         {.verb=_runnables_, .callback=runnables, .auth=&auth_detail,    .info="Get list of runnable applications",          .session=AFB_SESSION_CHECK },
766         {.verb=_detail_   , .callback=detail,    .auth=&auth_detail,    .info="Get the details for one application",        .session=AFB_SESSION_CHECK },
767         {.verb=_start_    , .callback=start,     .auth=&auth_start,     .info="Start an application",                       .session=AFB_SESSION_CHECK },
768         {.verb=_once_     , .callback=once,      .auth=&auth_start,     .info="Start once an application",                  .session=AFB_SESSION_CHECK },
769         {.verb=_terminate_, .callback=terminate, .auth=&auth_kill,      .info="Terminate a running application",            .session=AFB_SESSION_CHECK },
770         {.verb=_pause_    , .callback=pause,     .auth=&auth_kill,      .info="Pause a running application",                .session=AFB_SESSION_CHECK },
771         {.verb=_resume_   , .callback=resume,    .auth=&auth_kill,      .info="Resume a paused application",                .session=AFB_SESSION_CHECK },
772         {.verb=_runners_  , .callback=runners,   .auth=&auth_state,     .info="Get the list of running applications",       .session=AFB_SESSION_CHECK },
773         {.verb=_state_    , .callback=state,     .auth=&auth_state,     .info="Get the state of a running application",     .session=AFB_SESSION_CHECK },
774         {.verb=_install_  , .callback=install,   .auth=&auth_install,   .info="Install an application using a widget file", .session=AFB_SESSION_CHECK },
775         {.verb=_uninstall_, .callback=uninstall, .auth=&auth_uninstall, .info="Uninstall an application",                   .session=AFB_SESSION_CHECK },
776         {.verb=NULL }
777 };
778
779 const afb_binding_t afbBindingExport = {
780         .api = "afm-main",
781         .specification = NULL,
782         .info = "Application Framework Master Service",
783         .verbs = verbs,
784         .preinit = NULL,
785         .init = init,
786         .onevent = NULL,
787         .noconcurrency = 1 /* relies on binder for serialization of requests */
788 };