185dbcc2ba84d6eb1301163b926ebed3177cd379
angie
  Thu May 19 04:42:20 2016 -0700
Several revisions to login cookie-checking after helpful code review by Max:

Use /dev/urandom instead of srand(clock1000()), duh.

Instead of forming cookie strings in both wikiLink.c and hgLogin.c,
form them all in wikiLink.c so they're consistent.  The wikiLink
routines now return (possibly empty) slName lists of cookie strings
to be set.

The login system uses new cookie names that default to a concatentation
of central.cookie (which needs to have one name per central database,
like hguid for RR hgcentral and hguid.genome-test for hgcentraltest)
and either optional new config params login.tokenCookie and
login.userNameCookie or central.cookie concatenated with hgLoginToken and
hgLoginUserName (because login uses the central db, so it's different for
hgwdev vs RR).  If those cookies are not set but the wiki cookies are set,
then we accept the wiki cookie values and send out the new cookies,
removing the wiki cookies the first time that happens.

The login system no longer depends on any wiki.* hg.conf settings.

refs #17336, #17327

diff --git src/hg/hgLogin/hgLogin.c src/hg/hgLogin/hgLogin.c
index ab7cf57..2c59e55 100644
--- src/hg/hgLogin/hgLogin.c
+++ src/hg/hgLogin/hgLogin.c
@@ -30,50 +30,30 @@
 char *incorrectUsernameOrPassword="The username or password you entered is incorrect.";
 char *incorrectUsername="The username you entered is incorrect.";
 /* The excludeVars are not saved to the cart. */
 char *excludeVars[] = { "submit", "Submit", "debug", "fixMembers", "update", 
      "hgLogin_password", "hgLogin_password2", "hgLogin_newPassword1",
      "hgLogin_newPassword2", NULL };
 struct cart *cart;	/* This holds cgi and other variables between clicks. */
 char *database;		/* Name of genome database - hg15, mm3, or the like. */
 struct hash *oldCart;	/* Old cart hash. */
 char *errMsg;           /* Error message to show user when form data rejected */
 char brwName[64];
 char brwAddr[256];
 char signature[256];
 char returnAddr[256];
 /* ---- Global helper functions ---- */
-char *cookieNameForUserName()
-/* Return the cookie name used for logged in user name like 'wikidb_mw1_UserName' */
-{
-if isEmpty(cfgOption(CFG_COOKIIENAME_USERNAME))
-    return cloneString("NULL_cookieNameUserName");
-else
-    return cloneString(cfgOption(CFG_COOKIIENAME_USERNAME));
-}
-
-char *cookieNameForUserID()
-/* Return the cookie name used for logged in user ID like 'wikidb_mw1_UserID' */
-{
-if isEmpty(cfgOption(CFG_COOKIIENAME_USERID))
-    return cloneString("NULL_cookieNameUserID");
-else
-    return cloneString(cfgOption(CFG_COOKIIENAME_USERID));
-}
-
-
-
 char *browserName()
 /* Return the browser name like 'UCSC Genome Browser' */
 {
 if isEmpty(cfgOption(CFG_LOGIN_BROWSER_NAME))
     return cloneString("NULL_browserName");
 else
     return cloneString(cfgOption(CFG_LOGIN_BROWSER_NAME));
 }
 
 char *browserAddr()
 /* Return the browser address like 'http://genome.ucsc.edu' */
 {
 if isEmpty(cfgOption(CFG_LOGIN_BROWSER_ADDR))
     return cloneString("NULL_browserAddr");
 else
@@ -314,31 +294,31 @@
 do 
     {
     if (*c == '.') 
         {
         if (c == domain || *(c - 1) == '.') return 0;
         count++;
         }
     if (*c <= ' ' || *c >= 127) return 0;
     if (strchr(rfc822_specials, *c)) return 0;
     } while (*++c);
 
 return (count >= 1);
 }
 
 char *getReturnToURL()
-/* get URL passed in with returnto URL */
+/* get URL from cart var returnto; if empty, get URL to hgSession on login host.  */
 {
 char *returnURL = cartUsualString(cart, "returnto", "");
 char *hgLoginHost = wikiLinkHost();
 char *cgiDir = cgiScriptDirUrl();
 char returnTo[2048];
 if (!returnURL || sameString(returnURL,""))
    safef(returnTo, sizeof(returnTo),
         "http%s://%s%shgSession?hgS_doMainPage=1",
         cgiAppendSForHttps(), hgLoginHost, cgiDir);
 else
    safecpy(returnTo, sizeof(returnTo), returnURL);
 return cloneString(returnTo);
 }
 
 void returnToURL(int delay)
@@ -1173,86 +1153,46 @@
 
 boolean usingNewPassword(struct sqlConnection *conn, char *userName, char *password)
 /* The user is using  requested new password */
 {
 char query[256];
 sqlSafef(query,sizeof(query), "SELECT passwordChangeRequired FROM gbMembers WHERE userName='%s'", userName);
 char *change = sqlQuickString(conn, query);
 sqlSafef(query,sizeof(query), "SELECT newPassword FROM gbMembers WHERE userName='%s'", userName);
 char *newPassword = sqlQuickString(conn, query);
 if (change && sameString(change, "Y") && checkPwd(password, newPassword))
     return TRUE;
 else
     return FALSE;
 }
 
-char *getCookieDomainName()
-/* Return domain name to be used by the cookies or NULL. Allocd here.   */
-/* Return central.domain if returnToURL is also in the same domain.     */
-/* else return the domain in returnTo URL generated by remote hgSession.*/
-{
-char *centralDomain=cloneString(cfgOption(CFG_CENTRAL_DOMAIN));
-char *returnURL = getReturnToURL();
-char returnToDomain[256];
-
-/* parse the URL */
-struct netParsedUrl rtpu;
-netParseUrl(returnURL, &rtpu);
-safecpy(returnToDomain, sizeof(returnToDomain), rtpu.host);
-if (endsWith(returnToDomain,centralDomain))
-    return centralDomain;
-else
-    return cloneString(returnToDomain);
-}
-
-char *getCookieDomainString()
-/* Get a string that will look something like " domain=.ucsc.edu;" if getCookieDomainName
- * returns something good,  otherwise just " " */
-{
-char buf[256];
-char *domain = getCookieDomainName();
-if (domain != NULL && strchr(domain, '.') != NULL)
-    safef(buf, sizeof(buf), " domain=%s;", domain);
-else
-    safef(buf, sizeof(buf), " ");
-freeMem(domain);
-return cloneString(buf);
-}
-
-void displayLoginSuccess(char *userName, int userID)
+void displayLoginSuccess(char *userName)
 /* display login success msg, and set cookie */
 {
 hPrintf("<h2>%s</h2>", brwName);
 hPrintf(
     "<p align=\"left\">"
     "</p>"
     "<span style='color:red;'></span>"
     "\n");
 /* Set cookies */
-char *domainString=getCookieDomainString();
-
-char *userNameCookie=cookieNameForUserName();
-char *userIDCookie=cookieNameForUserID();
 hPrintf("<script language=\"JavaScript\">"
-    " document.write(\"Login successful, setting cookies now...\");"
-    "</script>\n"
-    "<script language=\"JavaScript\">"
-    "document.cookie = \"%s=%s;%s expires=Thu, 30-Dec-2037 23:59:59 GMT; path=/;\";"
-    "\n"
-    "document.cookie = \"%s=%d;%s expires=Thu, 30-Dec-2037 23:59:59 GMT; path=/;\";"
-    " </script>"
-    "\n", userNameCookie, userName, domainString, userIDCookie, userID, domainString);
+        " document.write(\"Login successful, setting cookies now...\");");
+struct slName *newCookies = loginLoginUser(userName), *sl;
+for (sl = newCookies;  sl != NULL;  sl = sl->next)
+    hPrintf(" document.cookie = '%s';", sl->name);
+hPrintf(" </script>\n");
 cartRemove(cart,"hgLogin_userName");
 returnToURL(150);
 }
 
 void displayLogin(struct sqlConnection *conn)
 /* display and process login info */
 {
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *userName = cartUsualString(cart, "hgLogin_userName", "");
 if (sameString(userName,""))
     {
     freez(&errMsg);
     errMsg = cloneString("User name cannot be blank.");
@@ -1281,65 +1221,61 @@
 struct gbMembers *m = gbMembersLoad(row);
 sqlFreeResult(&sr);
 
 /* Check user name exist and account activated */
 if (!sameString(m->accountActivated,"Y"))
     {              
     freez(&errMsg);
     errMsg = cloneString("Account is not activated.");
     displayLoginPage(conn);
     return;
     }
 if (checkPwd(password,m->password))
     {
     hPrintf("<h2>Login successful for user %s.\n</h2>\n", userName);
     clearNewPasswordFields(conn, userName);
-    uint authToken = loginSystemLoginUser(userName);
-    displayLoginSuccess(userName, authToken);
+    displayLoginSuccess(userName);
     return;
     } 
 else if (usingNewPassword(conn, userName, password))
     {
     cartSetString(cart, "hgLogin_changeRequired", "YES");
     changePasswordPage(conn);
     } 
 else
     {
     errMsg = cloneString(incorrectUsernameOrPassword);
     displayLoginPage(conn);
     return;
     }
 gbMembersFree(&m);
 }
 
 void  displayLogoutSuccess()
 /* display logout success msg, and reset cookie */
 {
 hPrintf("<h2>%s Sign Out</h2>", brwName);
 hPrintf(
     "<p align=\"left\">"
     "</p>"
     "<span style='color:red;'></span>"
     "\n");
-char *domainString=getCookieDomainString();
-char *userNameCookie=cookieNameForUserName();
-char *userIDCookie=cookieNameForUserID();
-hPrintf("<script language=\"JavaScript\">"
-    "document.cookie = \"%s=;%s expires=Thu, 1-Jan-1970 0:0:0 GMT; path=/;\";"
-    "\n"
-    "document.cookie = \"%s=;%s expires=Thu, 1-Jan-1970 0:0:0 GMT; path=/;\";"
-    "</script>\n", userNameCookie, domainString, userIDCookie, domainString);
+hPrintf("<script language=\"JavaScript\">");
+struct slName *newCookies = loginLogoutUser(), *sl;
+for (sl = newCookies;  sl != NULL;  sl = sl->next)
+    hPrintf(" document.cookie = '%s';", sl->name);
+hPrintf("</script>\n");
 /* return to "returnto" URL */
 returnToURL(150);
 }
 
 void doMiddle(struct cart *theCart)
 /* Write the middle parts of the HTML page.
  * This routine sets up some globals and then
  * dispatches to the appropriate page-maker. */
 {
 struct sqlConnection *conn = hConnectCentral();
 cart = theCart;
 safecpy(brwName,sizeof(brwName), browserName());
 safecpy(brwAddr,sizeof(brwAddr), browserAddr());
 safecpy(signature,sizeof(signature), mailSignature());
 safecpy(returnAddr,sizeof(returnAddr), mailReturnAddr());