session: introduce api to register session create and destroy callbacks
[project/rpcd.git] / session.c
1 /*
2 * rpcd - UBUS RPC server
3 *
4 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
5 * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <libubox/avl-cmp.h>
21 #include <libubox/utils.h>
22 #include <libubus.h>
23 #include <fnmatch.h>
24
25 #include "session.h"
26
27 static struct avl_tree sessions;
28 static struct blob_buf buf;
29
30 static LIST_HEAD(create_callbacks);
31 static LIST_HEAD(destroy_callbacks);
32
33 static const struct blobmsg_policy new_policy = {
34 .name = "timeout", .type = BLOBMSG_TYPE_INT32
35 };
36
37 static const struct blobmsg_policy sid_policy = {
38 .name = "sid", .type = BLOBMSG_TYPE_STRING
39 };
40
41 enum {
42 RPC_SS_SID,
43 RPC_SS_VALUES,
44 __RPC_SS_MAX,
45 };
46 static const struct blobmsg_policy set_policy[__RPC_SS_MAX] = {
47 [RPC_SS_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
48 [RPC_SS_VALUES] = { .name = "values", .type = BLOBMSG_TYPE_TABLE },
49 };
50
51 enum {
52 RPC_SG_SID,
53 RPC_SG_KEYS,
54 __RPC_SG_MAX,
55 };
56 static const struct blobmsg_policy get_policy[__RPC_SG_MAX] = {
57 [RPC_SG_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
58 [RPC_SG_KEYS] = { .name = "keys", .type = BLOBMSG_TYPE_ARRAY },
59 };
60
61 enum {
62 RPC_SA_SID,
63 RPC_SA_SCOPE,
64 RPC_SA_OBJECTS,
65 __RPC_SA_MAX,
66 };
67 static const struct blobmsg_policy acl_policy[__RPC_SA_MAX] = {
68 [RPC_SA_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
69 [RPC_SA_SCOPE] = { .name = "scope", .type = BLOBMSG_TYPE_STRING },
70 [RPC_SA_OBJECTS] = { .name = "objects", .type = BLOBMSG_TYPE_ARRAY },
71 };
72
73 enum {
74 RPC_SP_SID,
75 RPC_SP_SCOPE,
76 RPC_SP_OBJECT,
77 RPC_SP_FUNCTION,
78 __RPC_SP_MAX,
79 };
80 static const struct blobmsg_policy perm_policy[__RPC_SP_MAX] = {
81 [RPC_SP_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
82 [RPC_SP_SCOPE] = { .name = "scope", .type = BLOBMSG_TYPE_STRING },
83 [RPC_SP_OBJECT] = { .name = "object", .type = BLOBMSG_TYPE_STRING },
84 [RPC_SP_FUNCTION] = { .name = "function", .type = BLOBMSG_TYPE_STRING },
85 };
86
87 /*
88 * Keys in the AVL tree contain all pattern characters up to the first wildcard.
89 * To look up entries, start with the last entry that has a key less than or
90 * equal to the method name, then work backwards as long as the AVL key still
91 * matches its counterpart in the object name
92 */
93 #define uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func) \
94 for (_acl = avl_find_le_element(_avl, _obj, _acl, avl); \
95 _acl; \
96 _acl = avl_is_first(_avl, &(_acl)->avl) ? NULL : \
97 avl_prev_element((_acl), avl))
98
99 #define uh_foreach_matching_acl(_acl, _avl, _obj, _func) \
100 uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func) \
101 if (!strncmp((_acl)->object, _obj, (_acl)->sort_len) && \
102 !fnmatch((_acl)->object, (_obj), FNM_NOESCAPE) && \
103 !fnmatch((_acl)->function, (_func), FNM_NOESCAPE))
104
105 static void
106 rpc_random(char *dest)
107 {
108 unsigned char buf[16] = { 0 };
109 FILE *f;
110 int i;
111
112 f = fopen("/dev/urandom", "r");
113 if (!f)
114 return;
115
116 fread(buf, 1, sizeof(buf), f);
117 fclose(f);
118
119 for (i = 0; i < sizeof(buf); i++)
120 sprintf(dest + (i<<1), "%02x", buf[i]);
121 }
122
123 static void
124 rpc_session_dump_data(struct rpc_session *ses, struct blob_buf *b)
125 {
126 struct rpc_session_data *d;
127
128 avl_for_each_element(&ses->data, d, avl) {
129 blobmsg_add_field(b, blobmsg_type(d->attr), blobmsg_name(d->attr),
130 blobmsg_data(d->attr), blobmsg_data_len(d->attr));
131 }
132 }
133
134 static void
135 rpc_session_dump_acls(struct rpc_session *ses, struct blob_buf *b)
136 {
137 struct rpc_session_acl *acl;
138 struct rpc_session_acl_scope *acl_scope;
139 const char *lastobj = NULL;
140 const char *lastscope = NULL;
141 void *c = NULL, *d = NULL;
142
143 avl_for_each_element(&ses->acls, acl_scope, avl) {
144 if (!lastscope || strcmp(acl_scope->avl.key, lastscope))
145 {
146 if (c) blobmsg_close_table(b, c);
147 c = blobmsg_open_table(b, acl_scope->avl.key);
148 lastobj = NULL;
149 }
150
151 d = NULL;
152
153 avl_for_each_element(&acl_scope->acls, acl, avl) {
154 if (!lastobj || strcmp(acl->object, lastobj))
155 {
156 if (d) blobmsg_close_array(b, d);
157 d = blobmsg_open_array(b, acl->object);
158 }
159
160 blobmsg_add_string(b, NULL, acl->function);
161 lastobj = acl->object;
162 }
163
164 if (d) blobmsg_close_array(b, d);
165 }
166
167 if (c) blobmsg_close_table(b, c);
168 }
169
170 static void
171 rpc_session_dump(struct rpc_session *ses,
172 struct ubus_context *ctx,
173 struct ubus_request_data *req)
174 {
175 void *c;
176
177 blob_buf_init(&buf, 0);
178
179 blobmsg_add_string(&buf, "sid", ses->id);
180 blobmsg_add_u32(&buf, "timeout", ses->timeout);
181 blobmsg_add_u32(&buf, "expires", uloop_timeout_remaining(&ses->t) / 1000);
182
183 c = blobmsg_open_table(&buf, "acls");
184 rpc_session_dump_acls(ses, &buf);
185 blobmsg_close_table(&buf, c);
186
187 c = blobmsg_open_table(&buf, "data");
188 rpc_session_dump_data(ses, &buf);
189 blobmsg_close_table(&buf, c);
190
191 ubus_send_reply(ctx, req, buf.head);
192 }
193
194 static void
195 rpc_touch_session(struct rpc_session *ses)
196 {
197 uloop_timeout_set(&ses->t, ses->timeout * 1000);
198 }
199
200 static void
201 rpc_session_destroy(struct rpc_session *ses)
202 {
203 struct rpc_session_acl *acl, *nacl;
204 struct rpc_session_acl_scope *acl_scope, *nacl_scope;
205 struct rpc_session_data *data, *ndata;
206 struct rpc_session_cb *cb;
207
208 list_for_each_entry(cb, &destroy_callbacks, list)
209 cb->cb(ses, cb->priv);
210
211 uloop_timeout_cancel(&ses->t);
212
213 avl_for_each_element_safe(&ses->acls, acl_scope, avl, nacl_scope) {
214 avl_remove_all_elements(&acl_scope->acls, acl, avl, nacl)
215 free(acl);
216
217 avl_delete(&ses->acls, &acl_scope->avl);
218 free(acl_scope);
219 }
220
221 avl_remove_all_elements(&ses->data, data, avl, ndata)
222 free(data);
223
224 avl_delete(&sessions, &ses->avl);
225 free(ses);
226 }
227
228 static void rpc_session_timeout(struct uloop_timeout *t)
229 {
230 struct rpc_session *ses;
231
232 ses = container_of(t, struct rpc_session, t);
233 rpc_session_destroy(ses);
234 }
235
236 static struct rpc_session *
237 rpc_session_create(int timeout)
238 {
239 struct rpc_session *ses;
240 struct rpc_session_cb *cb;
241
242 ses = calloc(1, sizeof(*ses));
243 if (!ses)
244 return NULL;
245
246 ses->timeout = timeout;
247 ses->avl.key = ses->id;
248 rpc_random(ses->id);
249
250 avl_insert(&sessions, &ses->avl);
251 avl_init(&ses->acls, avl_strcmp, true, NULL);
252 avl_init(&ses->data, avl_strcmp, false, NULL);
253
254 ses->t.cb = rpc_session_timeout;
255 rpc_touch_session(ses);
256
257 list_for_each_entry(cb, &create_callbacks, list)
258 cb->cb(ses, cb->priv);
259
260 return ses;
261 }
262
263 static struct rpc_session *
264 rpc_session_get(const char *id)
265 {
266 struct rpc_session *ses;
267
268 ses = avl_find_element(&sessions, id, ses, avl);
269 if (!ses)
270 return NULL;
271
272 rpc_touch_session(ses);
273 return ses;
274 }
275
276 static int
277 rpc_handle_create(struct ubus_context *ctx, struct ubus_object *obj,
278 struct ubus_request_data *req, const char *method,
279 struct blob_attr *msg)
280 {
281 struct rpc_session *ses;
282 struct blob_attr *tb;
283 int timeout = RPC_DEFAULT_SESSION_TIMEOUT;
284
285 blobmsg_parse(&new_policy, 1, &tb, blob_data(msg), blob_len(msg));
286 if (tb)
287 timeout = blobmsg_get_u32(tb);
288
289 ses = rpc_session_create(timeout);
290 if (ses)
291 rpc_session_dump(ses, ctx, req);
292
293 return 0;
294 }
295
296 static int
297 rpc_handle_list(struct ubus_context *ctx, struct ubus_object *obj,
298 struct ubus_request_data *req, const char *method,
299 struct blob_attr *msg)
300 {
301 struct rpc_session *ses;
302 struct blob_attr *tb;
303
304 blobmsg_parse(&sid_policy, 1, &tb, blob_data(msg), blob_len(msg));
305
306 if (!tb) {
307 avl_for_each_element(&sessions, ses, avl)
308 rpc_session_dump(ses, ctx, req);
309 return 0;
310 }
311
312 ses = rpc_session_get(blobmsg_data(tb));
313 if (!ses)
314 return UBUS_STATUS_NOT_FOUND;
315
316 rpc_session_dump(ses, ctx, req);
317
318 return 0;
319 }
320
321 static int
322 uh_id_len(const char *str)
323 {
324 return strcspn(str, "*?[");
325 }
326
327 static int
328 rpc_session_grant(struct rpc_session *ses, struct ubus_context *ctx,
329 const char *scope, const char *object, const char *function)
330 {
331 struct rpc_session_acl *acl;
332 struct rpc_session_acl_scope *acl_scope;
333 char *new_scope, *new_obj, *new_func, *new_id;
334 int id_len;
335
336 if (!object || !function)
337 return UBUS_STATUS_INVALID_ARGUMENT;
338
339 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
340
341 if (acl_scope) {
342 uh_foreach_matching_acl_prefix(acl, &acl_scope->acls, object, function) {
343 if (!strcmp(acl->object, object) &&
344 !strcmp(acl->function, function))
345 return 0;
346 }
347 }
348
349 if (!acl_scope) {
350 acl_scope = calloc_a(sizeof(*acl_scope),
351 &new_scope, strlen(scope) + 1);
352
353 if (!acl_scope)
354 return UBUS_STATUS_UNKNOWN_ERROR;
355
356 acl_scope->avl.key = strcpy(new_scope, scope);
357 avl_init(&acl_scope->acls, avl_strcmp, true, NULL);
358 avl_insert(&ses->acls, &acl_scope->avl);
359 }
360
361 id_len = uh_id_len(object);
362 acl = calloc_a(sizeof(*acl),
363 &new_obj, strlen(object) + 1,
364 &new_func, strlen(function) + 1,
365 &new_id, id_len + 1);
366
367 if (!acl)
368 return UBUS_STATUS_UNKNOWN_ERROR;
369
370 acl->object = strcpy(new_obj, object);
371 acl->function = strcpy(new_func, function);
372 acl->avl.key = strncpy(new_id, object, id_len);
373 avl_insert(&acl_scope->acls, &acl->avl);
374
375 return 0;
376 }
377
378 static int
379 rpc_session_revoke(struct rpc_session *ses, struct ubus_context *ctx,
380 const char *scope, const char *object, const char *function)
381 {
382 struct rpc_session_acl *acl, *next;
383 struct rpc_session_acl_scope *acl_scope;
384 int id_len;
385 char *id;
386
387 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
388
389 if (!acl_scope)
390 return 0;
391
392 if (!object && !function) {
393 avl_remove_all_elements(&acl_scope->acls, acl, avl, next)
394 free(acl);
395 avl_delete(&ses->acls, &acl_scope->avl);
396 free(acl_scope);
397 return 0;
398 }
399
400 id_len = uh_id_len(object);
401 id = alloca(id_len + 1);
402 strncpy(id, object, id_len);
403 id[id_len] = 0;
404
405 acl = avl_find_element(&acl_scope->acls, id, acl, avl);
406 while (acl) {
407 if (!avl_is_last(&acl_scope->acls, &acl->avl))
408 next = avl_next_element(acl, avl);
409 else
410 next = NULL;
411
412 if (strcmp(id, acl->avl.key) != 0)
413 break;
414
415 if (!strcmp(acl->object, object) &&
416 !strcmp(acl->function, function)) {
417 avl_delete(&acl_scope->acls, &acl->avl);
418 free(acl);
419 }
420 acl = next;
421 }
422
423 if (avl_is_empty(&acl_scope->acls)) {
424 avl_delete(&ses->acls, &acl_scope->avl);
425 free(acl_scope);
426 }
427
428 return 0;
429 }
430
431
432 static int
433 rpc_handle_acl(struct ubus_context *ctx, struct ubus_object *obj,
434 struct ubus_request_data *req, const char *method,
435 struct blob_attr *msg)
436 {
437 struct rpc_session *ses;
438 struct blob_attr *tb[__RPC_SA_MAX];
439 struct blob_attr *attr, *sattr;
440 const char *object, *function;
441 const char *scope = "ubus";
442 int rem1, rem2;
443
444 int (*cb)(struct rpc_session *ses, struct ubus_context *ctx,
445 const char *scope, const char *object, const char *function);
446
447 blobmsg_parse(acl_policy, __RPC_SA_MAX, tb, blob_data(msg), blob_len(msg));
448
449 if (!tb[RPC_SA_SID])
450 return UBUS_STATUS_INVALID_ARGUMENT;
451
452 ses = rpc_session_get(blobmsg_data(tb[RPC_SA_SID]));
453 if (!ses)
454 return UBUS_STATUS_NOT_FOUND;
455
456 if (tb[RPC_SA_SCOPE])
457 scope = blobmsg_data(tb[RPC_SA_SCOPE]);
458
459 if (!strcmp(method, "grant"))
460 cb = rpc_session_grant;
461 else
462 cb = rpc_session_revoke;
463
464 if (!tb[RPC_SA_OBJECTS])
465 return cb(ses, ctx, scope, NULL, NULL);
466
467 blobmsg_for_each_attr(attr, tb[RPC_SA_OBJECTS], rem1) {
468 if (blob_id(attr) != BLOBMSG_TYPE_ARRAY)
469 continue;
470
471 object = NULL;
472 function = NULL;
473
474 blobmsg_for_each_attr(sattr, attr, rem2) {
475 if (blob_id(sattr) != BLOBMSG_TYPE_STRING)
476 continue;
477
478 if (!object)
479 object = blobmsg_data(sattr);
480 else if (!function)
481 function = blobmsg_data(sattr);
482 else
483 break;
484 }
485
486 if (object && function)
487 cb(ses, ctx, scope, object, function);
488 }
489
490 return 0;
491 }
492
493 static bool
494 rpc_session_acl_allowed(struct rpc_session *ses, const char *scope,
495 const char *obj, const char *fun)
496 {
497 struct rpc_session_acl *acl;
498 struct rpc_session_acl_scope *acl_scope;
499
500 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
501
502 if (acl_scope) {
503 uh_foreach_matching_acl(acl, &acl_scope->acls, obj, fun)
504 return true;
505 }
506
507 return false;
508 }
509
510 static int
511 rpc_handle_access(struct ubus_context *ctx, struct ubus_object *obj,
512 struct ubus_request_data *req, const char *method,
513 struct blob_attr *msg)
514 {
515 struct rpc_session *ses;
516 struct blob_attr *tb[__RPC_SP_MAX];
517 const char *scope = "ubus";
518 bool allow;
519
520 blobmsg_parse(perm_policy, __RPC_SP_MAX, tb, blob_data(msg), blob_len(msg));
521
522 if (!tb[RPC_SP_SID] || !tb[RPC_SP_OBJECT] || !tb[RPC_SP_FUNCTION])
523 return UBUS_STATUS_INVALID_ARGUMENT;
524
525 ses = rpc_session_get(blobmsg_data(tb[RPC_SP_SID]));
526 if (!ses)
527 return UBUS_STATUS_NOT_FOUND;
528
529 if (tb[RPC_SP_SCOPE])
530 scope = blobmsg_data(tb[RPC_SP_SCOPE]);
531
532 allow = rpc_session_acl_allowed(ses, scope,
533 blobmsg_data(tb[RPC_SP_OBJECT]),
534 blobmsg_data(tb[RPC_SP_FUNCTION]));
535
536 blob_buf_init(&buf, 0);
537 blobmsg_add_u8(&buf, "access", allow);
538 ubus_send_reply(ctx, req, buf.head);
539
540 return 0;
541 }
542
543 static int
544 rpc_handle_set(struct ubus_context *ctx, struct ubus_object *obj,
545 struct ubus_request_data *req, const char *method,
546 struct blob_attr *msg)
547 {
548 struct rpc_session *ses;
549 struct rpc_session_data *data;
550 struct blob_attr *tb[__RPC_SA_MAX];
551 struct blob_attr *attr;
552 int rem;
553
554 blobmsg_parse(set_policy, __RPC_SS_MAX, tb, blob_data(msg), blob_len(msg));
555
556 if (!tb[RPC_SS_SID] || !tb[RPC_SS_VALUES])
557 return UBUS_STATUS_INVALID_ARGUMENT;
558
559 ses = rpc_session_get(blobmsg_data(tb[RPC_SS_SID]));
560 if (!ses)
561 return UBUS_STATUS_NOT_FOUND;
562
563 blobmsg_for_each_attr(attr, tb[RPC_SS_VALUES], rem) {
564 if (!blobmsg_name(attr)[0])
565 continue;
566
567 data = avl_find_element(&ses->data, blobmsg_name(attr), data, avl);
568 if (data) {
569 avl_delete(&ses->data, &data->avl);
570 free(data);
571 }
572
573 data = calloc(1, sizeof(*data) + blob_pad_len(attr));
574 if (!data)
575 break;
576
577 memcpy(data->attr, attr, blob_pad_len(attr));
578 data->avl.key = blobmsg_name(data->attr);
579 avl_insert(&ses->data, &data->avl);
580 }
581
582 return 0;
583 }
584
585 static int
586 rpc_handle_get(struct ubus_context *ctx, struct ubus_object *obj,
587 struct ubus_request_data *req, const char *method,
588 struct blob_attr *msg)
589 {
590 struct rpc_session *ses;
591 struct rpc_session_data *data;
592 struct blob_attr *tb[__RPC_SA_MAX];
593 struct blob_attr *attr;
594 void *c;
595 int rem;
596
597 blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg));
598
599 if (!tb[RPC_SG_SID])
600 return UBUS_STATUS_INVALID_ARGUMENT;
601
602 ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID]));
603 if (!ses)
604 return UBUS_STATUS_NOT_FOUND;
605
606 blob_buf_init(&buf, 0);
607 c = blobmsg_open_table(&buf, "values");
608
609 if (tb[RPC_SG_KEYS])
610 blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) {
611 if (blob_id(attr) != BLOBMSG_TYPE_STRING)
612 continue;
613
614 data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
615 if (!data)
616 continue;
617
618 blobmsg_add_field(&buf, blobmsg_type(data->attr),
619 blobmsg_name(data->attr),
620 blobmsg_data(data->attr),
621 blobmsg_data_len(data->attr));
622 }
623 else
624 rpc_session_dump_data(ses, &buf);
625
626 blobmsg_close_table(&buf, c);
627 ubus_send_reply(ctx, req, buf.head);
628
629 return 0;
630 }
631
632 static int
633 rpc_handle_unset(struct ubus_context *ctx, struct ubus_object *obj,
634 struct ubus_request_data *req, const char *method,
635 struct blob_attr *msg)
636 {
637 struct rpc_session *ses;
638 struct rpc_session_data *data, *ndata;
639 struct blob_attr *tb[__RPC_SA_MAX];
640 struct blob_attr *attr;
641 int rem;
642
643 blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg));
644
645 if (!tb[RPC_SG_SID])
646 return UBUS_STATUS_INVALID_ARGUMENT;
647
648 ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID]));
649 if (!ses)
650 return UBUS_STATUS_NOT_FOUND;
651
652 if (!tb[RPC_SG_KEYS]) {
653 avl_remove_all_elements(&ses->data, data, avl, ndata)
654 free(data);
655 return 0;
656 }
657
658 blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) {
659 if (blob_id(attr) != BLOBMSG_TYPE_STRING)
660 continue;
661
662 data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
663 if (!data)
664 continue;
665
666 avl_delete(&ses->data, &data->avl);
667 free(data);
668 }
669
670 return 0;
671 }
672
673 static int
674 rpc_handle_destroy(struct ubus_context *ctx, struct ubus_object *obj,
675 struct ubus_request_data *req, const char *method,
676 struct blob_attr *msg)
677 {
678 struct rpc_session *ses;
679 struct blob_attr *tb;
680
681 blobmsg_parse(&sid_policy, 1, &tb, blob_data(msg), blob_len(msg));
682
683 if (!tb)
684 return UBUS_STATUS_INVALID_ARGUMENT;
685
686 ses = rpc_session_get(blobmsg_data(tb));
687 if (!ses)
688 return UBUS_STATUS_NOT_FOUND;
689
690 rpc_session_destroy(ses);
691
692 return 0;
693 }
694
695 int rpc_session_api_init(struct ubus_context *ctx)
696 {
697 static const struct ubus_method session_methods[] = {
698 UBUS_METHOD("create", rpc_handle_create, &new_policy),
699 UBUS_METHOD("list", rpc_handle_list, &sid_policy),
700 UBUS_METHOD("grant", rpc_handle_acl, acl_policy),
701 UBUS_METHOD("revoke", rpc_handle_acl, acl_policy),
702 UBUS_METHOD("access", rpc_handle_access, perm_policy),
703 UBUS_METHOD("set", rpc_handle_set, set_policy),
704 UBUS_METHOD("get", rpc_handle_get, get_policy),
705 UBUS_METHOD("unset", rpc_handle_unset, get_policy),
706 UBUS_METHOD("destroy", rpc_handle_destroy, &sid_policy),
707 };
708
709 static struct ubus_object_type session_type =
710 UBUS_OBJECT_TYPE("luci-rpc-session", session_methods);
711
712 static struct ubus_object obj = {
713 .name = "session",
714 .type = &session_type,
715 .methods = session_methods,
716 .n_methods = ARRAY_SIZE(session_methods),
717 };
718
719 avl_init(&sessions, avl_strcmp, false, NULL);
720
721 return ubus_add_object(ctx, &obj);
722 }
723
724 bool rpc_session_access(const char *sid, const char *scope,
725 const char *object, const char *function)
726 {
727 struct rpc_session *ses = rpc_session_get(sid);
728
729 if (!ses)
730 return false;
731
732 return rpc_session_acl_allowed(ses, scope, object, function);
733 }
734
735 void rpc_session_create_cb(struct rpc_session_cb *cb)
736 {
737 if (cb && cb->cb)
738 list_add(&cb->list, &create_callbacks);
739 }
740
741 void rpc_session_destroy_cb(struct rpc_session_cb *cb)
742 {
743 if (cb && cb->cb)
744 list_add(&cb->list, &destroy_callbacks);
745 }