| #!/usr/bin/python |
| """ |
| Background daemon to refresh OAuth access tokens. |
| Tokens are written to ~/.git-credential-cache/cookie |
| Git config variable http.cookiefile is updated. |
| |
| Runs only on Google Compute Engine. |
| """ |
| |
| import atexit |
| import contextlib |
| import cookielib |
| import json |
| import os |
| import subprocess |
| import sys |
| import time |
| import urllib2 |
| |
| REFRESH = 25 # seconds remaining when starting refresh |
| RETRY_INTERVAL = 5 # seconds between retrying a failed refresh |
| |
| A = 'http://metadata/0.1/meta-data/service-accounts/default/acquire' |
| S = 'https://www.googleapis.com/auth/gerritcodereview' |
| COOKIE_JAR = None |
| |
| def configure_git(): |
| global COOKIE_JAR |
| |
| dir = os.path.join(os.environ['HOME'], '.git-credential-cache') |
| COOKIE_JAR = os.path.join(dir, 'cookie') |
| |
| if os.path.exists(dir): |
| os.chmod(dir, 0700) |
| else: |
| os.mkdir(dir, 0700) |
| subprocess.call([ |
| 'git', 'config', '--global', |
| 'http.cookiefile', COOKIE_JAR |
| ]) |
| |
| def acquire_token(retry): |
| while True: |
| try: |
| with contextlib.closing(urllib2.urlopen(A + '?scopes=' + S)) as token: |
| return json.load(token) |
| except urllib2.URLError: |
| if not retry: |
| raise |
| time.sleep(RETRY_INTERVAL) |
| |
| def update_cookie(retry): |
| token = acquire_token(retry) |
| expires = token['expiresAt'] |
| |
| tmp_jar = COOKIE_JAR + '.lock' |
| cj = cookielib.MozillaCookieJar(tmp_jar) |
| cj.set_cookie(cookielib.Cookie( |
| version = 0, |
| name = 'o', |
| value = token['accessToken'], |
| port = None, |
| port_specified = False, |
| domain = '.googlesource.com', |
| domain_specified = True, |
| domain_initial_dot = True, |
| path = '/', |
| path_specified = True, |
| secure = True, |
| expires = expires, |
| discard = False, |
| comment = None, |
| comment_url = None, |
| rest = None)) |
| cj.save() |
| os.rename(tmp_jar, COOKIE_JAR) |
| return expires |
| |
| def cleanup(): |
| if COOKIE_JAR: |
| for p in [COOKIE_JAR, COOKIE_JAR + '.lock']: |
| if os.path.exists(p): |
| os.remove(p) |
| |
| def refresh_loop(expires): |
| atexit.register(cleanup) |
| expires = expires - REFRESH |
| while True: |
| now = time.time() |
| expires = max(expires, now + RETRY_INTERVAL) |
| while now < expires: |
| time.sleep(expires - now) |
| now = time.time() |
| expires = update_cookie(retry=True) - REFRESH |
| |
| def main(): |
| configure_git() |
| expires = update_cookie(retry=False) |
| |
| if os.fork() > 0: |
| sys.exit(0) |
| |
| os.chdir('/') |
| os.setsid() |
| os.umask(0) |
| |
| pid = os.fork() |
| if pid > 0: |
| print '%s PID %d' % (sys.argv[0], pid) |
| sys.exit(0) |
| |
| refresh_loop(expires) |
| |
| if __name__ == '__main__': |
| main() |