14ecff0730596f8be147b255afc8acb99e9095e2 max Fri Sep 18 09:06:06 2026 -0700 hgHubConnect: an api key made on one geo mirror now works on all of them, behind syncHubApiKeys #Preview2 week - bugs introduced now will need a build patch to fix #Preview2 week - bugs introduced now will need a build patch to fix Api keys live in the hgcentral of whichever mirror issued them, so a key made on genome.ucsc.edu was rejected on genome-euro and genome-asia, and the botDelay error told the user to go and make a second one. hgHubConnect now tells the other nodes about a key as soon as it generates or revokes one: cjGenerateApiKey and cjRevokeApiKey call syncApiKeyToOtherNodes(), which posts a hgHubSyncApiKey cartJson request to each peer, and cjSyncApiKey() on the far side writes it into that mirror's own table. The peers come from hgcentral.gbNode via the new geoMirrorNotifyOtherNodes(), and the request is signed with login.cookieSalt, which all of a site's mirrors already share - it is what makes the login cookie verifiable on each of them. So neither a list of mirror addresses nor a new shared secret needs provisioning. The notify is best effort: a peer that is down is warn()ed about and skipped, never failing the local generate or revoke, which has already committed by then. All of it is off unless hg.conf says syncHubApiKeys=on. With the gate off the sender returns at once, the receiver refuses the request outright rather than merely being unreachable, and botDelay keeps printing the old server-specific wording, which off is still the truth. Registered in hgConfCatalog.py as a gate, to be flipped once this is released. refs #38323 diff --git src/hg/inc/hubSpaceKeys.h src/hg/inc/hubSpaceKeys.h index ff2a37d46c4..c09fd75e091 100644 --- src/hg/inc/hubSpaceKeys.h +++ src/hg/inc/hubSpaceKeys.h @@ -1,81 +1,93 @@ /* hubSpaceKeys.h was originally generated by the autoSql program, which also * generated hubSpaceKeys.c and hubSpaceKeys.sql. This header links the database and * the RAM representation of objects. */ #ifndef HUBSPACEKEYS_H #define HUBSPACEKEYS_H #define HUBSPACEKEYS_NUM_COLS 2 extern char *hubSpaceKeysCommaSepFieldNames; struct hubSpaceKeys /* Api keys for userNames to upload to hubSpace using hubtools */ { struct hubSpaceKeys *next; /* Next in singly linked list. */ char *userName; /* userName in gbMembers */ char *apiKey; /* random key */ }; void hubSpaceKeysStaticLoad(char **row, struct hubSpaceKeys *ret); /* Load a row from hubSpaceKeys table into ret. The contents of ret will * be replaced at the next call to this function. */ struct hubSpaceKeys *hubSpaceKeysLoad(char **row); /* Load a hubSpaceKeys from row fetched with select * from hubSpaceKeys * from database. Dispose of this with hubSpaceKeysFree(). */ struct hubSpaceKeys *hubSpaceKeysLoadAll(char *fileName); /* Load all hubSpaceKeys from whitespace-separated file. * Dispose of this with hubSpaceKeysFreeList(). */ struct hubSpaceKeys *hubSpaceKeysLoadAllByChar(char *fileName, char chopper); /* Load all hubSpaceKeys from chopper separated file. * Dispose of this with hubSpaceKeysFreeList(). */ #define hubSpaceKeysLoadAllByTab(a) hubSpaceKeysLoadAllByChar(a, '\t'); /* Load all hubSpaceKeys from tab separated file. * Dispose of this with hubSpaceKeysFreeList(). */ struct hubSpaceKeys *hubSpaceKeysCommaIn(char **pS, struct hubSpaceKeys *ret); /* Create a hubSpaceKeys out of a comma separated string. * This will fill in ret if non-null, otherwise will * return a new hubSpaceKeys */ void hubSpaceKeysFree(struct hubSpaceKeys **pEl); /* Free a single dynamically allocated hubSpaceKeys such as created * with hubSpaceKeysLoad(). */ void hubSpaceKeysFreeList(struct hubSpaceKeys **pList); /* Free a list of dynamically allocated hubSpaceKeys's */ void hubSpaceKeysOutput(struct hubSpaceKeys *el, FILE *f, char sep, char lastSep); /* Print out hubSpaceKeys. Separate fields with sep. Follow last field with lastSep. */ #define hubSpaceKeysTabOut(el,f) hubSpaceKeysOutput(el,f,'\t','\n'); /* Print out hubSpaceKeys as a line in a tab-separated file. */ #define hubSpaceKeysCommaOut(el,f) hubSpaceKeysOutput(el,f,',',','); /* Print out hubSpaceKeys as a comma separated list including final comma. */ /* -------------------------------- End autoSql Generated Code -------------------------------- */ #define AUTH_TABLE_DEFAULT "apiKeys" char *hubSpaceGetApiKey(char *userName); /* Grab the already created api key if it exists */ char *hubSpaceUserNameForApiKey(struct sqlConnection *conn, char *apiKey); /* Return userName associated with apiKey else NULL. If conn is NULL, will create a connection and free it. */ void hubSpaceRevokeApiKey(char *userName); /* Remove any api keys for userName. errAborts if userName is NULL. * Run in an errCatch to handle errors. */ char *hubSpaceGenerateApiKey(char *userName); /* Make a random (but not crypto-secure) api key for userName, for use of hubtools to upload * to hubspace or for bypassing cloudflare. errAborts if userName is NULL. * Run in an errCatch to handle errors. */ +void hubSpaceSetApiKey(char *userName, char *apiKey); +/* Set userName's api key to apiKey, replacing any existing key -- unlike hubSpaceGenerateApiKey, + * this does not make up a new key. Used to adopt a key that a peer geo mirror generated, so + * that a key works the same on every UCSC mirror. errAborts if userName or apiKey is NULL. */ + +char *hubSpaceApiKeySyncSig(char *userName, char *apiKey); +/* Return a signature over userName and apiKey (empty string for a revoke), made with the + * login.cookieSalt shared secret that is already required to be identical across all of a + * site's geo mirrors (it is what makes the login cookie itself verifiable on every mirror). + * A peer mirror recomputes this to check that a hubSpaceSetApiKey/revoke request genuinely + * came from another UCSC mirror acting for this user, not from an outside caller. */ + #endif /* HUBSPACEKEYS_H */