cdb81647deb6096ff29d18a21b4f9e83f45b4ac9
chmalee
  Tue Mar 7 14:38:53 2023 -0800
Fix bug where hubApi was allowing ranged requests for tracks with 'tableBrowser off' setting in trackDb. Fix cartTrackDbIsAccessDenied function to recognize correctly when 'tableBrowser off' setting is present on the same host as the CGI is running. Add tests to hubApi system to check more 'tableBrowser' settings and check getting data from more than one track at once

diff --git src/hg/cgilib/cartTrackDb.c src/hg/cgilib/cartTrackDb.c
index 000d877..95c4879 100644
--- src/hg/cgilib/cartTrackDb.c
+++ src/hg/cgilib/cartTrackDb.c
@@ -206,135 +206,140 @@
 
 static char *chopAtFirstDot(char *string)
 /* Terminate string at first '.' if found.  Return string for convenience. */
 {
 char *ptr = strchr(string, '.');
 if (ptr != NULL)
     *ptr = '\0';
 return string;
 }
 
 struct accessControl
 /* Restricted permission settings for a table */
     {
     struct slName *hostList;       // List of hosts that are allowed to view this table
     boolean isNoGenome;            // True if it's OK for position range but not genome-wide query
+    boolean isFullDeny;            // True if track/table is completely inaccessible
     };
 
 void accessControlAddHost(struct accessControl *ac, char *host)
 /* Alloc an slName for host (unless NULL or empty) and add it to ac->hostList. */
 {
 if (isNotEmpty(host))
     slAddHead(&ac->hostList, slNameNew(host));
 }
 
-struct accessControl *accessControlNew(char *host, boolean isNoGenome)
+struct accessControl *accessControlNew(char *host, boolean isNoGenome, boolean isFullDeny)
 /* Alloc, init & return accessControl. */
 {
 struct accessControl *ac;
 AllocVar(ac);
 accessControlAddHost(ac, host);
 ac->isNoGenome = isNoGenome;
+ac->isFullDeny = isFullDeny;
 return ac;
 }
 
-static void acHashAddOneTable(struct hash *acHash, char *table, char *host, boolean isNoGenome)
+static void acHashAddOneTable(struct hash *acHash, char *table, char *host, boolean isNoGenome, boolean isFullDeny)
 /* If table is already in acHash, update its accessControl fields; otherwise store a
  * new accessControl for table. */
 {
 struct hashEl *hel = hashLookup(acHash, table);
 if (hel == NULL)
     {
-    struct accessControl *ac = accessControlNew(host, isNoGenome);
+    struct accessControl *ac = accessControlNew(host, isNoGenome, isFullDeny);
     hashAdd(acHash, table, ac);
     }
 else
     {
     struct accessControl *ac = hel->val;
     ac->isNoGenome = isNoGenome;
+    ac->isFullDeny = isFullDeny;
     accessControlAddHost(ac, host);
     }
 }
 
 static struct hash *accessControlInit(char *db)
 /* Return a hash associating restricted table/track names in the given db/conn
  * with virtual hosts -- hash is empty if there is no tableAccessControl table and no
  * accessControlTrackRefList (see getFullTrackList). */
 {
 struct hash *acHash = hashNew(0);
 if (! trackHubDatabase(db))
     {
     struct sqlConnection *conn = hAllocConn(db);
     if (sqlTableExists(conn, "tableAccessControl"))
         {
         struct sqlResult *sr = NULL;
         char **row = NULL;
         acHash = newHash(0);
 	char query[1024];
 	sqlSafef(query, sizeof query, "select name,host from tableAccessControl");
         sr = sqlGetResult(conn, query);
         while ((row = sqlNextRow(sr)) != NULL)
-            acHashAddOneTable(acHash, row[0], chopAtFirstDot(row[1]), FALSE);
+            acHashAddOneTable(acHash, row[0], chopAtFirstDot(row[1]), FALSE, FALSE);
         sqlFreeResult(&sr);
         }
     hFreeConn(&conn);
     }
 struct slRef *tdbRef;
 
 // init accessControlTrackRefList
 if (!accessControlTrackRefList)
     {
     boolean oldAC = useAC;
     useAC = TRUE;
     (void)getFullTrackList(NULL, db, NULL);
     useAC = oldAC;
     }
 for (tdbRef = accessControlTrackRefList; tdbRef != NULL; tdbRef = tdbRef->next)
     {
     struct trackDb *tdb = tdbRef->val;
     char *tbOff = cloneString(trackDbSetting(tdb, "tableBrowser"));
     if (isEmpty(tbOff))
         errAbort("accessControlInit bug: tdb for %s does not have tableBrowser setting",
                  tdb->track);
     // First word is "off" or "noGenome" or "tbNoGenome":
     char *type = nextWord(&tbOff);
 
     boolean isNoGenome = sameString(type, "noGenome");
+    boolean isFullDeny = sameString(type, "off");
     if (!isNoGenome)
         isNoGenome = sameString(type, "off") || sameString(type, "tbNoGenome"); // like 'noGenome' but only in the table browser, not the API 
         // since the API does not use this function
 
     // Add track table to acHash:
-    acHashAddOneTable(acHash, tdb->table, NULL, isNoGenome);
+    acHashAddOneTable(acHash, tdb->table, NULL, isNoGenome, isFullDeny);
     // Remaining words are additional table names to add:
     char *tbl;
     while ((tbl = nextWord(&tbOff)) != NULL)
-        acHashAddOneTable(acHash, tbl, NULL, isNoGenome);
+        acHashAddOneTable(acHash, tbl, NULL, isNoGenome, isFullDeny);
     // add any subtracks that don't have their own setting overriding us
     struct trackDb *subTdb;
     for (subTdb = tdb->subtracks; subTdb != NULL; subTdb = subTdb->next)
         {
         char *subTdbOff = cloneString(trackDbSetting(tdb, "tableBrowser"));
         if (!subTdbOff)
-            acHashAddOneTable(acHash, subTdb->table, NULL, isNoGenome);
+            acHashAddOneTable(acHash, subTdb->table, NULL, isNoGenome, isFullDeny);
         else
             {
             char *subTdbType = nextWord(&subTdbOff);
             boolean subTdbIsNoGenome = sameString(subTdbType, "noGenome");
+            boolean subTdbIsDenied = sameString(subTdbType, "off");
             if (!subTdbIsNoGenome)
                 subTdbIsNoGenome = sameString(subTdbType, "off") || sameString(subTdbType, "tbNoGenome");
-            acHashAddOneTable(acHash, subTdb->table, NULL, subTdbIsNoGenome);
+            acHashAddOneTable(acHash, subTdb->table, NULL, subTdbIsNoGenome, subTdbIsDenied);
             }
         }
     }
 return acHash;
 }
 
 static struct hash *getCachedAcHash(char *db)
 /* Returns a hash that maps table names to accessControl, creating it if necessary. */
 {
 static struct hash *dbToAcHash = NULL;
 if (dbToAcHash == NULL)
     dbToAcHash = hashNew(0);
 struct hash *acHash = hashFindVal(dbToAcHash, db);
 if (acHash == NULL)
     {
@@ -359,30 +364,32 @@
 return currentHost;
 }
 
 boolean cartTrackDbIsAccessDenied(char *db, char *table)
 /* Return TRUE if useAccessControl=TRUE was passed to cartTrackDbInit and
  * if access to table is denied (at least on this host) by 'tableBrowser off'
  * or by the tableAccessControl table. */
 {
 if (!useAC)
     return FALSE;
 
 struct hash *acHash = getCachedAcHash(db);
 struct accessControl *ac = hashFindVal(acHash, table);
 if (ac == NULL)
     return FALSE;
+if (ac->isFullDeny)
+    return TRUE;
 if (ac->isNoGenome && ac->hostList == NULL)
     return FALSE;
 char *currentHost = getCachedCurrentHost();
 if (currentHost == NULL)
     warn("accessControl: unable to determine current host");
 return (! slNameInList(ac->hostList, currentHost));
 }
 
 boolean cartTrackDbIsNoGenome(char *db, char *table)
 /* Return TRUE if range queries, but not genome-queries, are permitted for this table. */
 {
 struct hash *acHash = getCachedAcHash(db);
 struct accessControl *ac = hashFindVal(acHash, table);
 return (ac != NULL && ac->isNoGenome);
 }