diff options
author | Egor Tensin <Egor.Tensin@gmail.com> | 2021-03-29 08:54:40 +0300 |
---|---|---|
committer | Egor Tensin <Egor.Tensin@gmail.com> | 2021-03-29 15:16:00 +0300 |
commit | 48be16c88df07cf806cf178c524314605cabbf45 (patch) | |
tree | 21709929a2b4242f96f5916aadcc1b07c463abf8 | |
parent | cgitize.repo: more refactoring (diff) | |
download | cgitize-48be16c88df07cf806cf178c524314605cabbf45.tar.gz cgitize-48be16c88df07cf806cf178c524314605cabbf45.zip |
don't expose access tokens in the clone URL
-rw-r--r-- | cgitize/cgit.py | 52 | ||||
-rw-r--r-- | cgitize/main.py | 1 | ||||
-rw-r--r-- | cgitize/repo.py | 41 |
3 files changed, 70 insertions, 24 deletions
diff --git a/cgitize/cgit.py b/cgitize/cgit.py index 6e287e9..177a5f0 100644 --- a/cgitize/cgit.py +++ b/cgitize/cgit.py @@ -3,11 +3,13 @@ # For details, see https://github.com/egor-tensin/cgitize. # Distributed under the MIT License. +from contextlib import contextmanager from enum import Enum import logging import os import os.path import shutil +import stat import cgitize.utils as utils @@ -16,12 +18,34 @@ GIT_ENV = os.environ.copy() GIT_ENV['GIT_SSH_COMMAND'] = 'ssh -oBatchMode=yes -oLogLevel=QUIET -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null' -def run(*args, **kwargs): - return utils.run(*args, env=GIT_ENV, **kwargs) - - -def check_output(*args, **kwargs): - return utils.check_output(*args, env=GIT_ENV, **kwargs) +def git(*args, **kwargs): + return utils.run('git', *args, env=GIT_ENV, **kwargs) + + +def git_stdout(*args, **kwargs): + return utils.check_output('git', *args, env=GIT_ENV, **kwargs) + + +@contextmanager +def setup_git_auth(repo): + if not repo.url_auth: + yield + return + config_path = os.path.expanduser('~/.gitconfig') + exists = os.path.exists(config_path) + if exists: + old_permissions = stat.S_IMODE(os.stat(config_path).st_mode) + new_permissions = stat.S_IRUSR | stat.S_IWUSR # 0x600 + os.chmod(config_path, new_permissions) + git('config', '--global', f'url.{repo.clone_url_with_auth}.insteadOf', repo.clone_url) + try: + yield + finally: + if exists: + git('config', '--global', '--remove-section', f'url.{repo.clone_url_with_auth}.insteadOf') + os.chmod(config_path, old_permissions) + else: + os.unlink(config_path) class CGit: @@ -101,10 +125,10 @@ class Output: if not os.path.isdir(repo_dir): return RepoVerdict.SHOULD_MIRROR with utils.chdir(repo_dir): - if not run('git', 'rev-parse', '--is-inside-work-tree', discard_output=True): + if not git('rev-parse', '--is-inside-work-tree', discard_output=True): logging.warning('Not a repository, so going to mirror: %s', repo_dir) return RepoVerdict.SHOULD_MIRROR - success, output = check_output('git', 'config', '--get', 'remote.origin.url') + success, output = git_stdout('config', '--get', 'remote.origin.url') if not success: # Every repository managed by this script should have the # 'origin' remote. If it doesn't, it's trash. @@ -126,16 +150,18 @@ class Output: except Exception as e: logging.exception(e) return False - return run('git', 'clone', '--mirror', repo.clone_url, repo_dir) + with setup_git_auth(repo): + return git('clone', '--mirror', repo.clone_url, repo_dir) def update(self, repo): logging.info("Updating repository '%s'", repo.repo_id) repo_dir = self.get_repo_dir(repo) with utils.chdir(repo_dir): - if not run('git', 'remote', 'update', '--prune'): - return False - if run('git', 'rev-parse', '--verify', '--quiet', 'origin/master', discard_output=True): - if not run('git', 'reset', '--soft', 'origin/master'): + with setup_git_auth(repo): + if not git('remote', 'update', '--prune'): + return False + if git('rev-parse', '--verify', '--quiet', 'origin/master', discard_output=True): + if not git('reset', '--soft', 'origin/master'): return False return True diff --git a/cgitize/main.py b/cgitize/main.py index 006f235..39f370e 100644 --- a/cgitize/main.py +++ b/cgitize/main.py @@ -12,7 +12,6 @@ import os.path import sys from cgitize.cgit import CGit, Output -from cgitize.repo import Bitbucket, GitHub, Repo import cgitize.utils as utils diff --git a/cgitize/repo.py b/cgitize/repo.py index 3a2cf1e..8ecbcf5 100644 --- a/cgitize/repo.py +++ b/cgitize/repo.py @@ -5,6 +5,7 @@ import abc import os.path +from urllib.parse import urlsplit, urlunsplit class Repo: @@ -59,6 +60,10 @@ class Repo: def homepage(self): return self._homepage + @property + def url_auth(self): + return False + class HostedRepo(Repo, abc.ABC): def __init__(self, repo_id, owner=None, desc=None, homepage=None, @@ -102,6 +107,18 @@ class HostedRepo(Repo, abc.ABC): return self.clone_url_ssh return self.clone_url_https + @property + def clone_url_with_auth(self): + if self._via_ssh: + return self.clone_url_ssh + auth = self.url_auth + clone_url = self.clone_url_https + if not auth: + return clone_url + clone_url = urlsplit(clone_url) + clone_url = clone_url._replace(netloc=f'{auth}@{clone_url.netloc}') + return urlunsplit(clone_url) + class GitHub(HostedRepo): def __init__(self, *args, **kwargs): @@ -123,16 +140,18 @@ class GitHub(HostedRepo): return f'https://github.com/{self.user}/{self.repo_name}' @property + def url_auth(self): + if self._access_token is None: + return '' + return f'{self._access_token}' + + @property def clone_url_ssh(self): return f'ssh://git@github.com/{self.user}/{self.repo_name}.git' @property def clone_url_https(self): - if self._access_token is None: - auth = '' - else: - auth = f'{self._access_token}@' - return f'https://{auth}github.com/{self.user}/{self.repo_name}.git' + return f'https://github.com/{self.user}/{self.repo_name}.git' class Bitbucket(HostedRepo): @@ -155,13 +174,15 @@ class Bitbucket(HostedRepo): return f'https://bitbucket.org/{self.user}/{self.repo_name.lower()}' @property + def url_auth(self): + if self._app_password is None: + return '' + return f'{self.user}:{self._app_password}' + + @property def clone_url_ssh(self): return f'ssh://git@bitbucket.org/{self.user}/{self.repo_name}.git' @property def clone_url_https(self): - if self._app_password is None: - auth = '' - else: - auth = f'{self.user}:{self._app_password}@' - return f'https://{auth}bitbucket.org/{self.user}/{self.repo_name}.git' + return f'https://bitbucket.org/{self.user}/{self.repo_name}.git' |