| # Copyright 2019 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """API for SSO utility functions.""" |
| |
| import re |
| import urlparse |
| |
| from recipe_engine import recipe_api |
| |
| |
| class SSOApi(recipe_api.RecipeApi): |
| """API for SSO utility functions.""" |
| |
| def __init__(self, props, *args, **kwargs): |
| super(SSOApi, self).__init__(*args, **kwargs) |
| self._hosts_to_configure_insteadof = props.hosts_to_configure_insteadof |
| self._initialized = False |
| self._configured = set() |
| |
| def initialize(self): |
| if self._initialized: |
| return |
| self._initialized = True |
| if self._hosts_to_configure_insteadof: |
| with self.m.step.nest("sso"): |
| for host in self._hosts_to_configure_insteadof: |
| url = "sso://{}".format(host) |
| self.configure_insteadof(url) |
| |
| def configure_insteadof(self, sso_url): |
| """Configure git to automatically rewrite an sso:// URL to https://. |
| |
| If always calling git directly, transforming the URL ahead of time using |
| sso_to_https() is sufficient, but if Android Repo Tool manifests or |
| .gitmodules files reference sso:// URLs there's no good way to call |
| sso_to_https() on those paths ahead of time. |
| |
| This would appear to affect future jobs on the same host because of the |
| "--global" flag, but the wrapper of git modifies files in |
| INFRA_GIT_WRAPPER_HOME instead, and that is cleaned up after each job. |
| |
| Args: |
| host (str): An "sso://..." URL or a hostname. |
| """ |
| if not sso_url.startswith("sso://"): |
| sso_url = "sso://" + sso_url |
| host = urlparse.urlparse(sso_url).netloc |
| if not re.search(r"^[-a-z][-a-z.]+[-a-z]$", host): |
| raise self.m.step.StepFailure('bad hostname "{}"'.format(host)) |
| # The argument might be a full path to a repository but it must be |
| # trimmed to only the host for duplicate checking. |
| sso = "sso://" + host |
| if sso in self._configured: |
| return |
| |
| host, path = self._sso_to_https(sso) |
| # Using foo.googlesource.com/a/bar instead of foo.googlesource.com/bar |
| # The "/a/" forces authentication, which causes this request to use the |
| # correct quota bucket. |
| if not path.startswith("/a/"): |
| path = "/a" + path |
| https = host + path |
| self.m.git( |
| "git insteadof {}".format(sso), |
| "config", |
| "--global", |
| "--add", |
| "url.{}.insteadof".format(https), |
| sso, |
| ) |
| self._configured.add(sso) |
| |
| def _sso_to_https(self, remote): |
| url = urlparse.urlparse(remote) |
| host = url.netloc |
| # Sometimes URLs use '.git.corp.google.com' instead of |
| # '.googlesource.com', but '.git.corp.google.com' won't work with LUCI. |
| host = host.replace(".git.corp.google.com", ".googlesource.com") |
| # sso:// URLs do not usually contain '.googlesource.com' but can. |
| if not host.endswith(".googlesource.com"): |
| host += ".googlesource.com" |
| |
| sso_host = "sso://{}".format(url.netloc) |
| https_host = "https://{}".format(host) |
| |
| return (https_host, url.path) |
| |
| def sso_to_https(self, remote): |
| """Transform a remote URL of SSO scheme to its associated https version. |
| |
| If not SSO, do nothing. |
| |
| Args: |
| remote (str): SSO or https remote URL. |
| |
| Returns: |
| str: input as SSO URL. |
| """ |
| remote = remote.replace(".git.corp.google.com", ".googlesource.com") |
| if not remote.startswith("sso://") and ".git.corp.google.com" not in remote: |
| return remote |
| |
| host, path = self._sso_to_https(remote) |
| # Note that url.path contains a leading '/'. |
| return host + path |