3c9694bc8ed2b3d835de4985b9ee7f0eebf1bfe2 angie Wed May 25 13:50:18 2016 -0700 If remote hgLogin is configured by specifying some other server in hg.conf's wiki.host, then just accept the remote host cookies with no validation. fixes #17397 diff --git src/hg/lib/wikiLink.c src/hg/lib/wikiLink.c index 087566e..c05e473 100644 --- src/hg/lib/wikiLink.c +++ src/hg/lib/wikiLink.c @@ -6,30 +6,32 @@ #include "common.h" #include "hash.h" #include "htmshell.h" #include "autoUpgrade.h" #include "cartDb.h" #include "cheapcgi.h" #include "hgConfig.h" #include "hui.h" #include "web.h" #include "wikiLink.h" // Flag to indicate that loginValidateCookies has been called: static boolean alreadyAuthenticated = FALSE; // Set by loginValidateCookies, used by wikiLinkUserName static boolean authenticated = FALSE; +// User name from remote login cookies +static char *remoteUserName = NULL; // If we need to change some cookies, store cookie strings here in case loginValidateCookies // is called multiple times (e.g. validate before cookie-writing, then later write cookies) static struct slName *cookieStrings = NULL; char *loginSystemName() /* Return the wiki host specified in hg.conf, or NULL. Allocd here. */ { return cloneString(cfgOption(CFG_LOGIN_SYSTEM_NAME)); } boolean loginSystemEnabled() /* Return TRUE if login.systemName parameter is defined in hg.conf . */ { #ifdef USE_SSL return (cfgOption(CFG_LOGIN_SYSTEM_NAME) != NULL); @@ -152,53 +154,53 @@ if ((row = sqlNextRow(sr)) != NULL) { if (sameString(row[0], userName)) { struct slName *validKeyList = slNameListFromString(row[1], ','); isValid = slNameInListUseCase(validKeyList, key); } } sqlFreeResult(&sr); return isValid; } static void deleteKey(struct sqlConnection *conn, uint idx, char *key) /* Remove key from idx row's comma-separated keyList. */ { -char query[512]; +char query[2048]; sqlSafef(query, sizeof(query), "select keyList from gbMembers where idx = %u", idx); char buf[1024]; char *keyListStr = sqlQuickQuery(conn, query, buf, sizeof(buf)); if (isNotEmpty(keyListStr)) { struct slName *keyList = slNameListFromString(keyListStr, ','); struct slName *keyToDelete = slNameFind(keyList, key); if (keyToDelete) { slRemoveEl(&keyList, keyToDelete); char *newListStr = slNameListToString(keyList, ','); sqlSafef(query, sizeof(query), "update gbMembers set keyList='%s' where idx = %u", newListStr, idx); sqlUpdate(conn, query); } } } static void insertKey(struct sqlConnection *conn, uint idx, char *key) /* Add a new entry to gbMembers.keyList for idx. */ { -char query[512]; +char query[2048]; sqlSafef(query, sizeof(query), "select keyList from gbMembers where idx = %u", idx); char buf[1024]; char *keyListStr = sqlQuickQuery(conn, query, buf, sizeof(buf)); if (isNotEmpty(keyListStr)) sqlSafef(query, sizeof(query), "update gbMembers set keyList='%s,%s' where idx = %u", key, keyListStr, idx); else sqlSafef(query, sizeof(query), "update gbMembers set keyList='%s' where idx = %u", key, idx); sqlUpdate(conn, query); } char *getCookieDomainString() /* Get a string that will look something like " domain=.ucsc.edu;" if central.domain * is defined, otherwise just "". Don't free result. */ { @@ -271,47 +273,116 @@ { key = makeNewKey(); insertKey(conn, idx, key); } hDisconnectCentral(&conn); slAddHead(&cookieStrings, loginIdKeyCookieString(idx, key)); slAddHead(&cookieStrings, loginUserNameCookieString(userName)); return cookieStrings; } struct slName *loginLogoutUser() /* Return cookie strings to set (deleting the login cookies). */ { alreadyAuthenticated = TRUE; authenticated = FALSE; +cookieStrings = NULL; char *key = NULL; uint idx = getCookieIdKey(&key, NULL); struct sqlConnection *conn = hConnectCentral(); if (haveKeyList(conn) && key) deleteKey(conn, idx, key); hDisconnectCentral(&conn); slAddHead(&cookieStrings, loginIdKeyCookieString(0, NULL)); slAddHead(&cookieStrings, loginUserNameCookieString(NULL)); +// BEGIN TODO: remove in July 2016 +if (wikiLinkEnabled()) + { + slAddHead(&cookieStrings, newCookieString(wikiLinkLoggedInCookie(), NULL)); + slAddHead(&cookieStrings, newCookieString(wikiLinkUserNameCookie(), NULL)); + } +// END TODO: remove in July 2016 return cookieStrings; } +static char *getRemoteCookiePrefix(char *wikiHost) +/* Try to guess what cookie prefix wikiHost will use, to tide us over for release 333. + * It's better to set hg.conf's login.tokenCookie and login.userNameCookie than to rely on this. */ +{ +if (sameString("genome.ucsc.edu", wikiHost)) + return "hguid"; +if (startsWith("hgwbeta.", wikiHost)) + return "hguid.hgwbeta"; +if (startsWith("genome-test.", wikiHost) || startsWith("hgwdev.", wikiHost)) + return "hguid.genome-test"; +return NULL; +} + +static char *getRemoteCookieVal(char *cfgCookieName, char *remoteCookiePrefix, char *suffix) +/* Return the value of the remote login cookie. If the cookie name is not specified in hg.conf, + * make a guess to tide us over for release 333. Do not free result. */ +{ +char *cookie = cfgOption(cfgCookieName); +if (isNotEmpty(cookie)) + return findCookieData(cookie); +if (remoteCookiePrefix) + { + char defaultCookie[512]; + safef(defaultCookie, sizeof(defaultCookie), "%s.%s", remoteCookiePrefix, suffix); + return findCookieData(defaultCookie); + } +return NULL; +} + +static void remoteHostBypass() +/* If a remote host was used for login, redirecting back to this server, then this server's + * central database does not have the correct login token -- so we can't validate cookies. + * Fall back on the old method of just accepting whatever cookies we get. Unfortunately + * we'll have to take a guess at what those cookies are, although hg.conf can override. */ +{ +char *wikiHost = cfgOption(CFG_WIKI_HOST); +if (isEmpty(wikiHost) || sameString(wikiHost, "HTTPHOST") || sameString(wikiHost, hHttpHost())) + return; +alreadyAuthenticated = TRUE; +cookieStrings = NULL; +char *cookiePrefix = getRemoteCookiePrefix(wikiHost); +char *userName = getRemoteCookieVal(CFG_LOGIN_USER_NAME_COOKIE, cookiePrefix, "hgLoginUserName"); +char *idKey = getRemoteCookieVal(CFG_LOGIN_IDKEY_COOKIE, cookiePrefix, "hgLoginToken"); +authenticated = (isNotEmpty(userName) && isNotEmpty(idKey)); +remoteUserName = userName; +// BEGIN TODO: remove in July 2016 +if (! authenticated && wikiLinkEnabled()) + { + // Accept old wiki cookies for now + char *wikiUserName = findCookieData(wikiLinkUserNameCookie()); + char *wikiLoggedIn = findCookieData(wikiLinkLoggedInCookie()); + if (isNotEmpty(wikiLoggedIn) && isNotEmpty(wikiUserName)) + { + authenticated = TRUE; + remoteUserName = wikiUserName; + } + } +// END TODO: remove in July 2016 +} + struct slName *loginValidateCookies() /* Return possibly empty list of cookie strings for the caller to set. * If login cookies are obsolete but (formerly) valid, the results sets updated cookies. * If login cookies are present but invalid, the result deletes/expires the cookies. * Otherwise returns NULL (no change to cookies). */ { +remoteHostBypass(); if (alreadyAuthenticated) return cookieStrings; alreadyAuthenticated = TRUE; authenticated = FALSE; char *userName = findCookieData(loginUserNameCookie()); // BEGIN TODO: remove in July 2016 // If we're using values from old wiki cookies, replace the cookies. boolean replaceOldCookies = FALSE; if (isEmpty(userName) && wikiLinkEnabled()) { userName = findCookieData(wikiLinkUserNameCookie()); if (isNotEmpty(userName)) replaceOldCookies = TRUE; } @@ -387,30 +458,32 @@ wikiHost = hHttpHost(); return cloneString(wikiHost); } char *wikiLinkUserName() /* Return the user name specified in cookies from the browser, or NULL if * the user doesn't appear to be logged in. */ { if (loginSystemEnabled()) { if (! alreadyAuthenticated) errAbort("wikiLinkUserName: loginValidateCookies must be called first."); char *userName = findCookieData(loginUserNameCookie()); if (isEmpty(userName) && wikiLinkEnabled()) // TODO: remove in July 2016 userName = findCookieData(wikiLinkUserNameCookie()); // TODO: remove in July 2016 + if (isEmpty(userName) && isNotEmpty(remoteUserName)) + userName = remoteUserName; if (authenticated) return cloneString(userName); } else if (wikiLinkEnabled()) { char *wikiUserName = findCookieData(wikiLinkUserNameCookie()); char *wikiLoggedIn = findCookieData(wikiLinkLoggedInCookie()); if (isNotEmpty(wikiLoggedIn) && isNotEmpty(wikiUserName)) return cloneString(wikiUserName); } else errAbort("wikiLinkUserName called when wiki is not enabled (specified " "in hg.conf)."); return NULL; }