From 599150006bc04658b3ff4e54da6303d7af77531d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 16 Oct 2014 15:36:02 +0200 Subject: [PATCH] ACL: add per entry (user) stream profile selection --- src/access.c | 68 ++++++++++++++++++++++++++++++- src/access.h | 7 ++++ src/htsp_server.c | 12 ++---- src/profile.c | 35 +++++++++++++++- src/profile.h | 2 + src/webui/static/app/acleditor.js | 7 ++-- src/webui/webui.c | 8 +++- 7 files changed, 123 insertions(+), 16 deletions(-) diff --git a/src/access.c b/src/access.c index 7fc9ae9c..af6c730c 100644 --- a/src/access.c +++ b/src/access.c @@ -171,6 +171,8 @@ access_copy(access_t *src) dst->aa_username = strdup(src->aa_username); if (src->aa_representative) dst->aa_representative = strdup(src->aa_representative); + if (src->aa_profiles) + dst->aa_profiles = htsmsg_copy(src->aa_profiles); if (src->aa_dvrcfgs) dst->aa_dvrcfgs = htsmsg_copy(src->aa_dvrcfgs); if (src->aa_chtags) @@ -188,6 +190,7 @@ access_destroy(access_t *a) return; free(a->aa_username); free(a->aa_representative); + htsmsg_destroy(a->aa_profiles); htsmsg_destroy(a->aa_dvrcfgs); htsmsg_destroy(a->aa_chtags); free(a); @@ -335,6 +338,12 @@ access_update(access_t *a, access_entry_t *ae) } } + if(ae->ae_profile && ae->ae_profile->pro_name[0] != '\0') { + if (a->aa_profiles == NULL) + a->aa_profiles = htsmsg_create_list(); + htsmsg_add_str(a->aa_profiles, NULL, idnode_uuid_as_str(&ae->ae_profile->pro_id)); + } + if(ae->ae_dvr_config && ae->ae_dvr_config->dvr_config_name[0] != '\0') { if (a->aa_dvrcfgs == NULL) a->aa_dvrcfgs = htsmsg_create_list(); @@ -757,6 +766,8 @@ access_entry_destroy(access_entry_t *ae) TAILQ_REMOVE(&access_entries, ae, ae_link); idnode_unlink(&ae->ae_id); + if (ae->ae_profile) + LIST_REMOVE(ae, ae_profile_link); if (ae->ae_dvr_config) LIST_REMOVE(ae, ae_dvr_config_link); if (ae->ae_chtag) @@ -775,6 +786,22 @@ access_entry_destroy(access_entry_t *ae) free(ae); } +/* + * + */ +void +access_destroy_by_profile(profile_t *pro, int delconf) +{ + access_entry_t *ae; + + while ((ae = LIST_FIRST(&pro->pro_accesses)) != NULL) { + LIST_REMOVE(ae, ae_profile_link); + ae->ae_dvr_config = NULL; + if (delconf) + access_entry_save(ae); + } +} + /* * */ @@ -785,7 +812,7 @@ access_destroy_by_dvr_config(dvr_config_t *cfg, int delconf) while ((ae = LIST_FIRST(&cfg->dvr_accesses)) != NULL) { LIST_REMOVE(ae, ae_dvr_config_link); - ae->ae_dvr_config = NULL; + ae->ae_profile = profile_find_by_name(NULL, NULL); if (delconf) access_entry_save(ae); } @@ -1026,6 +1053,37 @@ access_entry_dvr_config_get(void *o) return &ret; } +static int +access_entry_profile_set(void *o, const void *v) +{ + access_entry_t *ae = (access_entry_t *)o; + profile_t *pro = v ? profile_find_by_uuid(v) : NULL; + if (pro == NULL && ae->ae_profile) { + LIST_REMOVE(ae, ae_profile_link); + ae->ae_profile = NULL; + return 1; + } else if (ae->ae_profile != pro) { + if (ae->ae_profile) + LIST_REMOVE(ae, ae_profile_link); + ae->ae_profile = pro; + LIST_INSERT_HEAD(&pro->pro_accesses, ae, ae_profile_link); + return 1; + } + return 0; +} + +static const void * +access_entry_profile_get(void *o) +{ + static const char *ret; + access_entry_t *ae = (access_entry_t *)o; + if (ae->ae_profile) + ret = idnode_uuid_as_str(&ae->ae_profile->pro_id); + else + ret = ""; + return &ret; +} + const idclass_t access_entry_class = { .ic_class = "access", .ic_caption = "Access", @@ -1091,6 +1149,14 @@ const idclass_t access_entry_class = { .name = "Advanced Streaming", .off = offsetof(access_entry_t, ae_adv_streaming), }, + { + .type = PT_STR, + .id = "profile", + .name = "Streaming Profile", + .set = access_entry_profile_set, + .get = access_entry_profile_get, + .list = profile_class_get_list, + }, { .type = PT_BOOL, .id = "dvr", diff --git a/src/access.h b/src/access.h index 12189a3f..e0d55c8f 100644 --- a/src/access.h +++ b/src/access.h @@ -22,6 +22,7 @@ #include "idnode.h" #include "htsmsg.h" +struct profile; struct dvr_config; struct channel_tag; @@ -57,6 +58,9 @@ typedef struct access_entry { int ae_streaming; int ae_adv_streaming; + struct profile *ae_profile; + LIST_ENTRY(access_entry) ae_profile_link; + uint32_t ae_conn_limit; int ae_dvr; @@ -83,6 +87,7 @@ typedef struct access { char *aa_username; char *aa_representative; uint32_t aa_rights; + htsmsg_t *aa_profiles; htsmsg_t *aa_dvrcfgs; uint32_t aa_chmin; uint32_t aa_chmax; @@ -185,6 +190,8 @@ access_entry_save(access_entry_t *ae); * */ void +access_destroy_by_profile(struct profile *pro, int delconf); +void access_destroy_by_dvr_config(struct dvr_config *cfg, int delconf); void access_destroy_by_channel_tag(struct channel_tag *ct, int delconf); diff --git a/src/htsp_server.c b/src/htsp_server.c index 2558da54..36a586b0 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -1783,18 +1783,14 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in) } #endif -#if ENABLE_LIBAV + profile_t *pro; const char *profile_id = htsmsg_get_str(in, "profile"); - profile_t *pro = NULL; if (profile_id) { pro = profile_find_by_uuid(profile_id); - if (pro == NULL) - pro = profile_find_by_name(profile_id, "htsp"); + if (pro) + profile_id = pro->pro_name; } - -#else - profile_t *pro = profile_find_by_name("htsp", NULL); -#endif + pro = profile_find_by_list(htsp->htsp_granted_access->aa_profiles, profile_id, "htsp"); hs->hs_work = profile_work(pro, st, &hs->hs_work_destroy); if (hs->hs_work) { diff --git a/src/profile.c b/src/profile.c index 78bcad00..89cd9768 100644 --- a/src/profile.c +++ b/src/profile.c @@ -83,6 +83,7 @@ profile_create return NULL; } LIST_INIT(&pro->pro_dvr_configs); + LIST_INIT(&pro->pro_accesses); if (idnode_insert(&pro->pro_id, uuid, pb->clazz, 0)) { if (uuid) tvherror("profile", "invalid uuid '%s'", uuid); @@ -114,6 +115,7 @@ profile_delete(profile_t *pro, int delconf) TAILQ_REMOVE(&profiles, pro, pro_link); idnode_unlink(&pro->pro_id); dvr_config_destroy_by_profile(pro, delconf); + access_destroy_by_profile(pro, delconf); if (pro->pro_free) pro->pro_free(pro); free(pro->pro_name); @@ -308,13 +310,13 @@ profile_find_by_name(const char *name, const char *alt) return profile_default; TAILQ_FOREACH(pro, &profiles, pro_link) { - if (!strcmp(pro->pro_name, name)) + if (pro->pro_enabled && !strcmp(pro->pro_name, name)) return pro; } if (alt) { TAILQ_FOREACH(pro, &profiles, pro_link) { - if (!strcmp(pro->pro_name, alt)) + if (pro->pro_enabled && !strcmp(pro->pro_name, alt)) return pro; } } @@ -322,6 +324,35 @@ profile_find_by_name(const char *name, const char *alt) return profile_default; } +/* + * + */ +profile_t * +profile_find_by_list(htsmsg_t *uuids, const char *name, const char *alt) +{ + profile_t *pro, *res = NULL; + htsmsg_field_t *f; + const char *uuid, *uuid2; + + pro = profile_find_by_name(name, alt); + uuid = idnode_uuid_as_str(&pro->pro_id); + if (uuids) { + HTSMSG_FOREACH(f, uuids) { + uuid2 = htsmsg_field_get_str(f) ?: ""; + if (strcmp(uuid, uuid2) == 0) + return res; + if (!res) { + res = profile_find_by_uuid(uuid2); + if (!res->pro_enabled) + res = NULL; + } + } + } + if (!res) + res = profile_find_by_name(NULL, NULL); + return res; +} + /* * */ diff --git a/src/profile.h b/src/profile.h index c97acf29..a0e71043 100644 --- a/src/profile.h +++ b/src/profile.h @@ -64,6 +64,7 @@ typedef struct profile { TAILQ_ENTRY(profile) pro_link; LIST_HEAD(,dvr_config) pro_dvr_configs; + LIST_HEAD(,access_entry) pro_accesses; int pro_enabled; int pro_shield; @@ -104,6 +105,7 @@ void profile_chain_close(profile_chain_t *prch); static inline profile_t *profile_find_by_uuid(const char *uuid) { return (profile_t*)idnode_find(uuid, &profile_class, NULL); } profile_t *profile_find_by_name(const char *name, const char *alt); +profile_t *profile_find_by_list(htsmsg_t *uuids, const char *name, const char *alt); htsmsg_t * profile_class_get_list(void *o); diff --git a/src/webui/static/app/acleditor.js b/src/webui/static/app/acleditor.js index dc68e136..672f5aff 100644 --- a/src/webui/static/app/acleditor.js +++ b/src/webui/static/app/acleditor.js @@ -4,9 +4,10 @@ tvheadend.acleditor = function(panel, index) { - var list = 'enabled,username,password,prefix,streaming,adv_streaming,' + - 'dvr,dvr_config,webui,admin,conn_limit,channel_min,channel_max,' + - 'channel_tag,comment'; + var list = 'enabled,username,password,prefix,' + + 'streaming,adv_streaming,profile,' + + 'dvr,dvr_config,webui,admin,conn_limit,' + + 'channel_min,channel_max,channel_tag,comment'; tvheadend.idnode_grid(panel, { url: 'api/access/entry', diff --git a/src/webui/webui.c b/src/webui/webui.c index fb98f967..295e17b4 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -719,7 +719,9 @@ http_stream_service(http_connection_t *hc, service_t *service, int weight) if(http_access_verify(hc, ACCESS_ADVANCED_STREAMING)) return HTTP_STATUS_UNAUTHORIZED; - if(!(pro = profile_find_by_name(http_arg_get(&hc->hc_req_args, "profile"), "service"))) + if(!(pro = profile_find_by_list(hc->hc_access->aa_profiles, + http_arg_get(&hc->hc_req_args, "profile"), + "service"))) return HTTP_STATUS_NOT_ALLOWED; if((tcp_id = http_stream_preop(hc)) == NULL) @@ -831,7 +833,9 @@ http_stream_channel(http_connection_t *hc, channel_t *ch, int weight) if (http_access_verify_channel(hc, ACCESS_STREAMING, ch, 1)) return HTTP_STATUS_UNAUTHORIZED; - if(!(pro = profile_find_by_name(http_arg_get(&hc->hc_req_args, "profile"), "channel"))) + if(!(pro = profile_find_by_list(hc->hc_access->aa_profiles, + http_arg_get(&hc->hc_req_args, "profile"), + "channel"))) return HTTP_STATUS_NOT_ALLOWED; if((tcp_id = http_stream_preop(hc)) == NULL)