예제 #1
0
    def _ParseInput(self):
        """Parse the fields from stdin.

    Returns:
      {str: str}, The parsed parameters given on stdin.
    """
        info = {}
        for line in sys.stdin:
            if _BLANK_LINE_RE.match(line):
                continue
            match = _KEYVAL_RE.match(line)
            if not match:
                raise auth_exceptions.GitCredentialHelperError(
                    'Invalid input line format: [{format}].'.format(
                        format=line.rstrip('\n')))
            key, val = match.groups()
            info[key] = val.strip()

        if 'protocol' not in info:
            raise auth_exceptions.GitCredentialHelperError(
                'Required key "protocol" missing.')

        if 'host' not in info:
            raise auth_exceptions.GitCredentialHelperError(
                'Required key "host" missing.')

        if info.get('protocol') != 'https':
            raise auth_exceptions.GitCredentialHelperError(
                'Invalid protocol [{p}].  "https" expected.'.format(
                    p=info.get('protocol')))
        return info
 def ValidateHost(host):
     if args.ignore_unknown:
         return
     if host in credentialed_domains:
         return
     for suffix in credentialed_domains_suffix:
         if host.endswith(suffix):
             return
     raise auth_exceptions.GitCredentialHelperError(
         'Unknown host [{host}].'.format(host=host))
예제 #3
0
    def Run(self, args):
        """Run the helper command."""

        if args.method not in GitHelper.METHODS:
            if args.ignore_unknown:
                return
            raise auth_exceptions.GitCredentialHelperError(
                'Unexpected method [{meth}]. One of [{methods}] expected.'.
                format(meth=args.method, methods=', '.join(GitHelper.METHODS)))

        info = self._ParseInput()
        credentialed_domains = [
            'source.developers.google.com',
            GitHelper.GOOGLESOURCE,  # Requires a different username value.
        ]
        credentialed_domains_suffix = [
            '.' + GitHelper.GOOGLESOURCE,
        ]
        extra = properties.VALUES.core.credentialed_hosted_repo_domains.Get()
        if extra:
            credentialed_domains.extend(extra.split(','))
        host = info.get('host')

        def _ValidateHost(host):
            if host in credentialed_domains:
                return True
            for suffix in credentialed_domains_suffix:
                if host.endswith(suffix):
                    return True
            return False

        if not _ValidateHost(host):
            if not args.ignore_unknown:
                raise auth_exceptions.GitCredentialHelperError(
                    'Unknown host [{host}].'.format(host=host))
            return

        if args.method == GitHelper.GET:
            account = properties.VALUES.core.account.Get()
            try:
                cred = c_store.Load(account)
                c_store.Refresh(cred)
            except c_store.Error as e:
                sys.stderr.write(
                    textwrap.dedent("""\
            ERROR: {error}
            Run 'gcloud auth login' to log in.
            """.format(error=str(e))))
                return

            self._CheckNetrc()

            # For googlesource.com, any username beginning with "git-" is accepted
            # and the identity of the user is extracted from the token server-side.
            if (host == GitHelper.GOOGLESOURCE
                    or host.endswith('.' + GitHelper.GOOGLESOURCE)):
                sent_account = 'git-account'
            else:
                sent_account = account

            sys.stdout.write(
                textwrap.dedent("""\
          username={username}
          password={password}
          """).format(username=sent_account, password=cred.access_token))
        elif args.method == GitHelper.STORE:
            # On OSX, there is an additional credential helper that gets called before
            # ours does.  When we return a token, it gets cached there.  Git continues
            # to get it from there first until it expires.  That command then fails,
            # and the token is deleted, but it does not retry the operation.  The next
            # command gets a new token from us and it starts working again, for an
            # hour.  This erases our credential from the other cache whenever 'store'
            # is called on us.  Because they are called first, the token will already
            # be stored there, and so we can successfully erase it to prevent caching.
            if (platforms.OperatingSystem.Current() ==
                    platforms.OperatingSystem.MACOSX):
                log.debug('Clearing OSX credential cache.')
                try:
                    input_string = 'protocol={protocol}\nhost={host}\n\n'.format(
                        protocol=info.get('protocol'), host=info.get('host'))
                    log.debug('Calling erase with input:\n%s', input_string)
                    p = subprocess.Popen(
                        ['git-credential-osxkeychain', 'erase'],
                        stdin=subprocess.PIPE,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE)
                    (out, err) = p.communicate(input_string)
                    if p.returncode:
                        log.debug(
                            'Failed to clear OSX keychain:\nstdout: {%s}\nstderr: {%s}',
                            out, err)
                # pylint:disable=broad-except, This can fail and should only be done as
                # best effort.
                except Exception as e:
                    log.debug('Failed to clear OSX keychain', exc_info=True)