]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Separate passwords from ACL
authorJaroslav Kysela <perex@perex.cz>
Mon, 1 Jun 2015 16:25:30 +0000 (18:25 +0200)
committerJaroslav Kysela <perex@perex.cz>
Mon, 1 Jun 2015 16:25:30 +0000 (18:25 +0200)
docs/html/config_access.html
src/access.c
src/access.h
src/api/api_access.c
src/config.c
src/webui/static/app/acleditor.js
src/webui/static/app/tvheadend.js

index fbe9e9dbaa451cdc738f9c52fdb93e6924702d4d..2b163bae782fd6a04ec8e67501a40bb3d4dbb14d 100644 (file)
@@ -8,7 +8,7 @@ wide open.
 <p>
 When Tvheadend verifies access is scan through all the enabled access control entries.
 The permission flags, streaming profiles, DVR config profiles, channel tags are combined
-for all matching access entries. An access entry is said to match if the username / password
+for all matching access entries. An access entry is said to match if the username
 matches and the IP source address of the requesting peer is within the prefix.
 <p>
 
@@ -47,10 +47,6 @@ The columns have the following functions:
   <dd>
   Login name to be used. If no username is required, this entry should contain a single asterisk (*).
 
-  <dt><b>Password</b>
-  <dd>
-  Login password to be used. If username is '*' (unused), the password should be the same (i.e. match any username/password combination, or no username/password required).
-
   <dt><b>Network prefix</b>
   <dd>
   IPv4 prefix for matching based on source IP address.
index 96b1eb62651cc92ba2c303bd6652f7d81ec68228..efa601c046c0242e9e966f78d92c66cc715d61ef 100644 (file)
 
 struct access_entry_queue access_entries;
 struct access_ticket_queue access_tickets;
+struct passwd_entry_queue passwd_entries;
 
 const char *superuser_username;
 const char *superuser_password;
 
 int access_noacl;
 
+static int passwd_verify(const char *username, const char *passwd);
+static int passwd_verify2(const char *username, const char *passwd,
+                          const char *username2, const char *passwd2);
+static int passwd_verify_digest(const char *username, const uint8_t *digest,
+                                const uint8_t *challenge);
+static int passwd_verify_digest2(const char *username, const uint8_t *digest,
+                                 const uint8_t *challenge,
+                                 const char *username2, const char *passwd2);
+
 /**
  *
  */
@@ -306,17 +316,18 @@ access_verify(const char *username, const char *password,
 {
   uint32_t bits = 0;
   access_entry_t *ae;
-  int match = 0;
+  int match = 0, nouser = username == NULL || username[0] == '\0';
 
   if (access_noacl)
     return 0;
 
-  if(username != NULL && superuser_username != NULL && 
-     password != NULL && superuser_password != NULL && 
-     !strcmp(username, superuser_username) &&
-     !strcmp(password, superuser_password))
+  if (!passwd_verify2(username, password,
+                      superuser_username, superuser_password))
     return 0;
 
+  if (passwd_verify(username, password))
+    username = NULL;
+
   TAILQ_FOREACH(ae, &access_entries, ae_link) {
 
     if(!ae->ae_enabled)
@@ -324,12 +335,8 @@ access_verify(const char *username, const char *password,
 
     if(ae->ae_username[0] != '*') {
       /* acl entry requires username to match */
-      if(username == NULL || password == NULL)
+      if(username == NULL || strcmp(username, ae->ae_username))
        continue; /* Didn't get one */
-
-      if(strcmp(ae->ae_username, username) ||
-        strcmp(ae->ae_password, password))
-       continue; /* username/password mismatch */
     }
 
     if(!netmask_verify(ae, src))
@@ -342,10 +349,8 @@ access_verify(const char *username, const char *password,
   }
 
   /* Username was not matched - no access */
-  if (!match) {
-    if (username && *username != '\0')
-      bits = 0;
-  }
+  if (!match && !nouser)
+    bits = 0;
 
   return (mask & ACCESS_OR) ?
          ((mask & bits) ? 0 : -1) :
@@ -485,11 +490,13 @@ access_get(const char *username, const char *password, struct sockaddr *src)
 {
   access_t *a = calloc(1, sizeof(*a));
   access_entry_t *ae;
+  int nouser = username == NULL || username[0] == '\0';
 
-  if (username && username[0]) {
+  if (!passwd_verify(username, password)) {
     a->aa_username = strdup(username);
     a->aa_representative = strdup(username);
   } else {
+    username = NULL;
     a->aa_representative = malloc(50);
     tcp_get_str_from_ip((struct sockaddr*)src, a->aa_representative, 50);
   }
@@ -499,10 +506,8 @@ access_get(const char *username, const char *password, struct sockaddr *src)
     return a;
   }
 
-  if(username != NULL && superuser_username != NULL &&
-     password != NULL && superuser_password != NULL &&
-     !strcmp(username, superuser_username) &&
-     !strcmp(password, superuser_password)) {
+  if(!passwd_verify2(username, password,
+                     superuser_username, superuser_password)) {
     a->aa_rights = ACCESS_FULL;
     return a;
   }
@@ -514,12 +519,8 @@ access_get(const char *username, const char *password, struct sockaddr *src)
 
     if(ae->ae_username[0] != '*') {
       /* acl entry requires username to match */
-      if(username == NULL || password == NULL)
+      if(username == NULL || strcmp(username, ae->ae_username))
        continue; /* Didn't get one */
-
-      if(strcmp(ae->ae_username, username) ||
-        strcmp(ae->ae_password, password))
-       continue; /* username/password mismatch */
     }
 
     if(!netmask_verify(ae, src))
@@ -535,7 +536,7 @@ access_get(const char *username, const char *password, struct sockaddr *src)
   if (!a->aa_match) {
     free(a->aa_username);
     a->aa_username = NULL;
-    if (username && *username != '\0')
+    if (!nouser)
       a->aa_rights = 0;
   }
 
@@ -553,15 +554,15 @@ access_get_hashed(const char *username, const uint8_t digest[20],
 {
   access_t *a = calloc(1, sizeof(*a));
   access_entry_t *ae;
-  SHA_CTX shactx;
-  uint8_t d[20];
+  int nouser = username == NULL || username[0] == '\0';
 
-  if (username && username[0]) {
+  if (!passwd_verify_digest(username, digest, challenge)) {
     a->aa_username = strdup(username);
     a->aa_representative = strdup(username);
   } else {
     a->aa_representative = malloc(50);
     tcp_get_str_from_ip((struct sockaddr*)src, a->aa_representative, 50);
+    username = NULL;
   }
 
   if(access_noacl) {
@@ -569,18 +570,10 @@ access_get_hashed(const char *username, const uint8_t digest[20],
     return a;
   }
 
-  if(username && superuser_username != NULL && superuser_password != NULL) {
-
-    SHA1_Init(&shactx);
-    SHA1_Update(&shactx, (const uint8_t *)superuser_password,
-               strlen(superuser_password));
-    SHA1_Update(&shactx, challenge, 32);
-    SHA1_Final(d, &shactx);
-
-    if(!strcmp(superuser_username, username) && !memcmp(d, digest, 20)) {
-      a->aa_rights = ACCESS_FULL;
-      return a;
-    }
+  if(!passwd_verify_digest2(username, digest, challenge,
+                            superuser_username, superuser_password)) {
+    a->aa_rights = ACCESS_FULL;
+    return a;
   }
 
   TAILQ_FOREACH(ae, &access_entries, ae_link) {
@@ -596,15 +589,6 @@ access_get_hashed(const char *username, const uint8_t digest[20],
       if (!username)
         continue;
 
-      SHA1_Init(&shactx);
-      SHA1_Update(&shactx, (const uint8_t *)ae->ae_password,
-                  strlen(ae->ae_password));
-      SHA1_Update(&shactx, challenge, 32);
-      SHA1_Final(d, &shactx);
-
-      if(strcmp(ae->ae_username, username) || memcmp(d, digest, 20))
-        continue;
-
       a->aa_match = 1;
     }
 
@@ -615,7 +599,7 @@ access_get_hashed(const char *username, const uint8_t digest[20],
   if (!a->aa_match) {
     free(a->aa_username);
     a->aa_username = NULL;
-    if (username && *username != '\0')
+    if (!nouser)
       a->aa_rights = 0;
   }
 
@@ -874,13 +858,11 @@ access_entry_update_rights(access_entry_t *ae)
  */
 
 static void access_entry_reindex(void);
-static int access_entry_class_password_set(void *o, const void *v);
 
 access_entry_t *
 access_entry_create(const char *uuid, htsmsg_t *conf)
 {
   access_entry_t *ae, *ae2;
-  const char *s;
 
   lock_assert(&global_lock);
 
@@ -902,9 +884,6 @@ access_entry_create(const char *uuid, htsmsg_t *conf)
     ae->ae_all_dvr        = 1;
     ae->ae_failed_dvr     = 1;
     idnode_load(&ae->ae_id, conf);
-    /* note password has PO_NOSAVE, thus it must be set manually */
-    if ((s = htsmsg_get_str(conf, "password")) != NULL)
-      access_entry_class_password_set(ae, s);
     access_entry_update_rights(ae);
     TAILQ_FOREACH(ae2, &access_entries, ae_link)
       if (ae->ae_index < ae2->ae_index)
@@ -922,8 +901,6 @@ access_entry_create(const char *uuid, htsmsg_t *conf)
     ae->ae_username = strdup("*");
   if (ae->ae_comment == NULL)
     ae->ae_comment = strdup("New entry");
-  if (ae->ae_password == NULL)
-    access_entry_class_password_set(ae, "*");
   if (TAILQ_FIRST(&ae->ae_ipmasks) == NULL)
     access_set_prefix_default(ae);
 
@@ -955,8 +932,6 @@ access_entry_destroy(access_entry_t *ae)
   }
 
   free(ae->ae_username);
-  free(ae->ae_password);
-  free(ae->ae_password2);
   free(ae->ae_comment);
   free(ae);
 }
@@ -1125,47 +1100,6 @@ access_entry_class_prefix_get(void *o)
   return &ret;
 }
 
-static int
-access_entry_class_password_set(void *o, const void *v)
-{
-  access_entry_t *ae = (access_entry_t *)o;
-  char buf[256], result[300];
-
-  if (strcmp(v ?: "", ae->ae_password ?: "")) {
-    snprintf(buf, sizeof(buf), "TVHeadend-Hide-%s", (const char *)v ?: "");
-    base64_encode(result, sizeof(result), (uint8_t *)buf, strlen(buf));
-    free(ae->ae_password2);
-    ae->ae_password2 = strdup(result);
-    free(ae->ae_password);
-    ae->ae_password = strdup((const char *)v ?: "");
-    return 1;
-  }
-  return 0;
-}
-
-static int
-access_entry_class_password2_set(void *o, const void *v)
-{
-  access_entry_t *ae = (access_entry_t *)o;
-  char result[300];
-  int l;
-
-  if (strcmp(v ?: "", ae->ae_password2 ?: "")) {
-    if (v && ((const char *)v)[0] != '\0') {
-      l = base64_decode((uint8_t *)result, v, sizeof(result)-1);
-      if (l < 0)
-        l = 0;
-      result[l] = '\0';
-      free(ae->ae_password);
-      ae->ae_password = strdup(result + 15);
-      free(ae->ae_password2);
-      ae->ae_password2 = strdup((const char *)v);
-      return 1;
-    }
-  }
-  return 0;
-}
-
 static int
 access_entry_chtag_set(void *o, const void *v)
 {
@@ -1289,22 +1223,6 @@ const idclass_t access_entry_class = {
       .name     = "Username",
       .off      = offsetof(access_entry_t, ae_username),
     },
-    {
-      .type     = PT_STR,
-      .id       = "password",
-      .name     = "Password",
-      .off      = offsetof(access_entry_t, ae_password),
-      .opts     = PO_PASSWORD | PO_NOSAVE,
-      .set      = access_entry_class_password_set,
-    },
-    {
-      .type     = PT_STR,
-      .id       = "password2",
-      .name     = "Password2",
-      .off      = offsetof(access_entry_t, ae_password2),
-      .opts     = PO_PASSWORD | PO_HIDDEN | PO_ADVANCED | PO_WRONCE,
-      .set      = access_entry_class_password2_set,
-    },
     {
       .type     = PT_STR,
       .id       = "prefix",
@@ -1427,6 +1345,244 @@ const idclass_t access_entry_class = {
   }
 };
 
+/*
+ * Password table
+ */
+
+static int passwd_entry_class_password_set(void *o, const void *v);
+
+static int
+passwd_verify_digest2(const char *username, const uint8_t *digest,
+                      const uint8_t *challenge,
+                      const char *username2, const char *passwd2)
+{
+  SHA_CTX shactx;
+  uint8_t d[20];
+
+  if (username == NULL || username[0] == '\0' ||
+      username2 == NULL || username2[0] == '\0' ||
+      passwd2 == NULL || passwd2[0] == '\0')
+    return -1;
+
+  if (strcmp(username, username2))
+    return -1;
+
+  SHA1_Init(&shactx);
+  SHA1_Update(&shactx, (const uint8_t *)passwd2, strlen(passwd2));
+  SHA1_Update(&shactx, challenge, 32);
+  SHA1_Final(d, &shactx);
+
+  return memcmp(d, digest, 20) ? -1 : 0;
+}
+
+static int
+passwd_verify_digest(const char *username, const uint8_t *digest,
+                     const uint8_t *challenge)
+{
+  passwd_entry_t *pw;
+
+  TAILQ_FOREACH(pw, &passwd_entries, pw_link)
+    if (pw->pw_enabled &&
+        !passwd_verify_digest2(username, digest, challenge,
+                               pw->pw_username, pw->pw_password))
+      return 0;
+  return -1;
+}
+
+static int
+passwd_verify2(const char *username, const char *passwd,
+               const char *username2, const char *passwd2)
+{
+  if (username == NULL || username[0] == '\0' ||
+      username2 == NULL || username2[0] == '\0' ||
+      passwd == NULL || passwd2 == NULL)
+    return -1;
+
+  if (strcmp(username, username2))
+    return -1;
+
+  return strcmp(passwd, passwd2) ? -1 : 0;
+}
+
+static int
+passwd_verify(const char *username, const char *passwd)
+{
+  passwd_entry_t *pw;
+
+  TAILQ_FOREACH(pw, &passwd_entries, pw_link)
+    if (pw->pw_enabled &&
+        !passwd_verify2(username, passwd,
+                        pw->pw_username, pw->pw_password))
+      return 0;
+  return -1;
+}
+
+passwd_entry_t *
+passwd_entry_create(const char *uuid, htsmsg_t *conf)
+{
+  passwd_entry_t *pw;
+  const char *s;
+
+  lock_assert(&global_lock);
+
+  pw = calloc(1, sizeof(passwd_entry_t));
+
+  if (idnode_insert(&pw->pw_id, uuid, &passwd_entry_class, 0)) {
+    if (uuid)
+      tvherror("access", "invalid uuid '%s'", uuid);
+    free(pw);
+    return NULL;
+  }
+
+  if (conf) {
+    pw->pw_enabled = 1;
+    idnode_load(&pw->pw_id, conf);
+    /* note password has PO_NOSAVE, thus it must be set manually */
+    if ((s = htsmsg_get_str(conf, "password")) != NULL)
+      passwd_entry_class_password_set(pw, s);
+  }
+
+  TAILQ_INSERT_TAIL(&passwd_entries, pw, pw_link);
+
+  return pw;
+}
+
+static void
+passwd_entry_destroy(passwd_entry_t *pw)
+{
+  if (pw == NULL)
+    return;
+  TAILQ_REMOVE(&passwd_entries, pw, pw_link);
+  idnode_unlink(&pw->pw_id);
+  free(pw->pw_username);
+  free(pw->pw_password);
+  free(pw->pw_password2);
+  free(pw->pw_comment);
+  free(pw);
+}
+
+void
+passwd_entry_save(passwd_entry_t *pw)
+{
+  htsmsg_t *c = htsmsg_create_map();
+  idnode_save(&pw->pw_id, c);
+  hts_settings_save(c, "passwd/%s", idnode_uuid_as_str(&pw->pw_id));
+  htsmsg_destroy(c);
+}
+
+static void
+passwd_entry_class_save(idnode_t *self)
+{
+  passwd_entry_save((passwd_entry_t *)self);
+}
+
+static void
+passwd_entry_class_delete(idnode_t *self)
+{
+  passwd_entry_t *pw = (passwd_entry_t *)self;
+
+  hts_settings_remove("passwd/%s", idnode_uuid_as_str(&pw->pw_id));
+  passwd_entry_destroy(pw);
+}
+
+static const char *
+passwd_entry_class_get_title (idnode_t *self)
+{
+  passwd_entry_t *pw = (passwd_entry_t *)self;
+
+  if (pw->pw_comment && pw->pw_comment[0] != '\0')
+    return pw->pw_comment;
+  return pw->pw_username ?: "";
+}
+
+static int
+passwd_entry_class_password_set(void *o, const void *v)
+{
+  passwd_entry_t *pw = (passwd_entry_t *)o;
+  char buf[256], result[300];
+
+  if (strcmp(v ?: "", pw->pw_password ?: "")) {
+    snprintf(buf, sizeof(buf), "TVHeadend-Hide-%s", (const char *)v ?: "");
+    base64_encode(result, sizeof(result), (uint8_t *)buf, strlen(buf));
+    free(pw->pw_password2);
+    pw->pw_password2 = strdup(result);
+    free(pw->pw_password);
+    pw->pw_password = strdup((const char *)v ?: "");
+    return 1;
+  }
+  return 0;
+}
+
+static int
+passwd_entry_class_password2_set(void *o, const void *v)
+{
+  passwd_entry_t *pw = (passwd_entry_t *)o;
+  char result[300];
+  int l;
+
+  if (strcmp(v ?: "", pw->pw_password2 ?: "")) {
+    if (v && ((const char *)v)[0] != '\0') {
+      l = base64_decode((uint8_t *)result, v, sizeof(result)-1);
+      if (l < 0)
+        l = 0;
+      result[l] = '\0';
+      free(pw->pw_password);
+      pw->pw_password = strdup(result + 15);
+      free(pw->pw_password2);
+      pw->pw_password2 = strdup((const char *)v);
+      return 1;
+    }
+  }
+  return 0;
+}
+
+const idclass_t passwd_entry_class = {
+  .ic_class      = "passwd",
+  .ic_caption    = "Passwords",
+  .ic_event      = "passwd",
+  .ic_perm_def   = ACCESS_ADMIN,
+  .ic_save       = passwd_entry_class_save,
+  .ic_get_title  = passwd_entry_class_get_title,
+  .ic_delete     = passwd_entry_class_delete,
+  .ic_properties = (const property_t[]){
+    {
+      .type     = PT_BOOL,
+      .id       = "enabled",
+      .name     = "Enabled",
+      .off      = offsetof(passwd_entry_t, pw_enabled),
+    },
+    {
+      .type     = PT_STR,
+      .id       = "username",
+      .name     = "Username",
+      .off      = offsetof(passwd_entry_t, pw_username),
+    },
+    {
+      .type     = PT_STR,
+      .id       = "password",
+      .name     = "Password",
+      .off      = offsetof(passwd_entry_t, pw_password),
+      .opts     = PO_PASSWORD | PO_NOSAVE,
+      .set      = passwd_entry_class_password_set,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "password2",
+      .name     = "Password2",
+      .off      = offsetof(passwd_entry_t, pw_password2),
+      .opts     = PO_PASSWORD | PO_HIDDEN | PO_ADVANCED | PO_WRONCE,
+      .set      = passwd_entry_class_password2_set,
+    },
+    {
+      .type     = PT_STR,
+      .id       = "comment",
+      .name     = "Comment",
+      .off      = offsetof(passwd_entry_t, pw_comment),
+    },
+    {}
+  }
+};
+
 /**
  *
  */
@@ -1453,8 +1609,18 @@ access_init(int createdefault, int noacl)
 
   TAILQ_INIT(&access_entries);
   TAILQ_INIT(&access_tickets);
+  TAILQ_INIT(&passwd_entries);
+
+  /* Load passwd entries */
+  if ((c = hts_settings_load("passwd")) != NULL) {
+    HTSMSG_FOREACH(f, c) {
+      if (!(m = htsmsg_field_get_map(f))) continue;
+      (void)passwd_entry_create(f->hmf_name, m);
+    }
+    htsmsg_destroy(c);
+  }
 
-  /* Load */
+  /* Load ACL entries */
   if ((c = hts_settings_load("accesscontrol")) != NULL) {
     HTSMSG_FOREACH(f, c) {
       if (!(m = htsmsg_field_get_map(f))) continue;
@@ -1513,12 +1679,15 @@ access_done(void)
 {
   access_entry_t *ae;
   access_ticket_t *at;
+  passwd_entry_t *pw;
 
   pthread_mutex_lock(&global_lock);
   while ((ae = TAILQ_FIRST(&access_entries)) != NULL)
     access_entry_destroy(ae);
   while ((at = TAILQ_FIRST(&access_tickets)) != NULL)
     access_ticket_destroy(at);
+  while ((pw = TAILQ_FIRST(&passwd_entries)) != NULL)
+    passwd_entry_destroy(pw);
   free((void *)superuser_username);
   superuser_username = NULL;
   free((void *)superuser_password);
index 42321db57567db82d0093eedc7dfc5856b301999..c1e7f0b328a3cd9f1ac91d0b65873716ec472927 100644 (file)
@@ -26,6 +26,26 @@ struct profile;
 struct dvr_config;
 struct channel_tag;
 
+TAILQ_HEAD(passwd_entry_queue, passwd_entry);
+
+extern struct passwd_entry_queue passwd_entries;
+
+typedef struct passwd_entry {
+  idnode_t pw_id;
+
+  TAILQ_ENTRY(passwd_entry) pw_link;
+
+  char *pw_username;
+  char *pw_password;
+  char *pw_password2;
+
+  int   pw_enabled;
+
+  char *pw_comment;
+} passwd_entry_t;
+
+extern const idclass_t passwd_entry_class;
+
 typedef struct access_ipmask {
   TAILQ_ENTRY(access_ipmask) ai_link;
 
@@ -48,8 +68,6 @@ typedef struct access_entry {
 
   TAILQ_ENTRY(access_entry) ae_link;
   char *ae_username;
-  char *ae_password;
-  char *ae_password2;
   char *ae_comment;
 
   int ae_index;
@@ -220,6 +238,14 @@ access_destroy_by_dvr_config(struct dvr_config *cfg, int delconf);
 void
 access_destroy_by_channel_tag(struct channel_tag *ct, int delconf);
 
+/**
+ *
+ */
+passwd_entry_t *
+passwd_entry_create(const char *uuid, htsmsg_t *conf);
+void
+passwd_entry_save(passwd_entry_t *pw);
+
 /**
  *
  */
index 63f98398fb12f3c8a561fbc19e8aa8a7d7d25e00..cc5456973b0ed6bafe418020973ca2effa702d02 100644 (file)
 #include "access.h"
 #include "api.h"
 
+/*
+ *
+ */
+
+static void
+api_passwd_entry_grid
+  ( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
+{
+  passwd_entry_t *pw;
+
+  TAILQ_FOREACH(pw, &passwd_entries, pw_link)
+    idnode_set_add(ins, (idnode_t*)pw, &conf->filter);
+}
+
+static int
+api_passwd_entry_create
+  ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+  htsmsg_t *conf;
+  passwd_entry_t *pw;
+
+  if (!(conf  = htsmsg_get_map(args, "conf")))
+    return EINVAL;
+
+  pthread_mutex_lock(&global_lock);
+  if ((pw = passwd_entry_create(NULL, conf)) != NULL)
+    passwd_entry_save(pw);
+  pthread_mutex_unlock(&global_lock);
+
+  return 0;
+}
+
+/*
+ *
+ */
+
 static void
 api_access_entry_grid
   ( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args )
@@ -52,6 +88,10 @@ api_access_entry_create
 void api_access_init ( void )
 {
   static api_hook_t ah[] = {
+    { "passwd/entry/class",  ACCESS_ADMIN, api_idnode_class, (void*)&passwd_entry_class },
+    { "passwd/entry/grid",   ACCESS_ADMIN, api_idnode_grid,  api_passwd_entry_grid },
+    { "passwd/entry/create", ACCESS_ADMIN, api_passwd_entry_create, NULL },
+
     { "access/entry/class",  ACCESS_ADMIN, api_idnode_class, (void*)&access_entry_class },
     { "access/entry/grid",   ACCESS_ADMIN, api_idnode_grid,  api_access_entry_grid },
     { "access/entry/create", ACCESS_ADMIN, api_access_entry_create, NULL },
index 84156b833bcdb088dc9b0ff7e9dd1985b5b11c01..0de63576afbe854d53016a58dd925ce502290c61 100644 (file)
@@ -1210,6 +1210,32 @@ config_migrate_v18 ( void )
   }
 }
 
+static void
+config_migrate_v19 ( void )
+{
+  htsmsg_t *c, *e, *m;
+  htsmsg_field_t *f;
+  const char *username, *passwd;
+  tvh_uuid_t u;
+
+  if ((c = hts_settings_load("accesscontrol")) != NULL) {
+    HTSMSG_FOREACH(f, c) {
+      if (!(e = htsmsg_field_get_map(f))) continue;
+      if ((username = htsmsg_get_str(e, "username")) == NULL)
+        continue;
+      if ((passwd = htsmsg_get_str(e, "password2")) == NULL)
+        continue;
+      m = htsmsg_create_map();
+      htsmsg_add_str(m, "username", username);
+      htsmsg_add_str(m, "password2", passwd);
+      uuid_init_hex(&u, NULL);
+      hts_settings_save(m, "passwd/%s", u.hex);
+      htsmsg_delete_field(e, "password2");
+      hts_settings_save(e, "accesscontrol/%s", f->hmf_name);
+    }
+  }
+}
+
 /*
  * Perform backup
  */
@@ -1324,6 +1350,7 @@ static const config_migrate_t config_migrate_table[] = {
   config_migrate_v16,
   config_migrate_v17,
   config_migrate_v18,
+  config_migrate_v19,
 };
 
 /*
index 0ec941d8053e3a0d4116335046c3ec37d19c9f76..e287e3f4a76310d7ce56dc3282720be08be9e707 100644 (file)
@@ -61,3 +61,43 @@ tvheadend.acleditor = function(panel, index)
         }
     });
 };
+
+/*
+ * Password Control
+ */
+
+tvheadend.passwdeditor = function(panel, index)
+{
+    var list = 'enabled,username,password,comment';
+
+    tvheadend.idnode_grid(panel, {
+        url: 'api/passwd/entry',
+        titleS: 'Password',
+        titleP: 'Passwords',
+        iconCls: 'key',
+        columns: {
+            enabled:        { width: 120 },
+            username:       { width: 250 },
+            password:       { width: 250 }
+        },
+        tabIndex: index,
+        edit: {
+            params: {
+                list: list
+            }
+        },
+        add: {
+            url: 'api/passwd/entry',
+            params: {
+                list: list
+            },
+            create: { }
+        },
+        del: true,
+        move: true,
+        list: list,
+        help: function() {
+            new tvheadend.help('Password Control Entries', 'config_passwd.html');
+        }
+    });
+};
index 9efe8d08b74d71b935dd5c1647d6667b351041a8..0f2c6dca8348cf3dfac8c0938222bc91cdfa8547 100644 (file)
@@ -345,6 +345,7 @@ function accessUpdate(o) {
         tvheadend.miscconf(cp);
 
         tvheadend.acleditor(cp);
+        tvheadend.passwdeditor(cp);
 
         /* DVB inputs, networks, muxes, services */
         var dvbin = new Ext.TabPanel({