From 14df37f3b2b5e20078f737d43bfb3921dba5c325 Mon Sep 17 00:00:00 2001 From: Tasko Olevski Date: Fri, 7 Oct 2022 11:42:57 +0200 Subject: [PATCH] fix(sidecar): properly set and unset lfs auth (#1301) --- git_services/git_services/init/cloner.py | 55 ++++++++++++++---------- git_services/git_services/init/config.py | 13 ++++-- 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/git_services/git_services/init/cloner.py b/git_services/git_services/init/cloner.py index c8d7d84c1..07f9be92b 100644 --- a/git_services/git_services/init/cloner.py +++ b/git_services/git_services/init/cloner.py @@ -1,15 +1,15 @@ -from contextlib import contextmanager +import logging import os -import requests +import re +from contextlib import contextmanager from datetime import datetime, timedelta -import logging -from time import sleep from pathlib import Path -import re -from urllib.parse import urlparse +from time import sleep +from urllib.parse import urljoin, urlparse -from git_services.init import errors +import requests from git_services.cli import GitCLI, GitCommandError +from git_services.init import errors from git_services.init.config import User @@ -58,14 +58,14 @@ def _wait_for_server(self, timeout_mins=None): sleep(5) def _initialize_repo(self): - logging.info( - f"Intitializing repo with email {self.user.email} and name {self.user.full_name}" - ) + logging.info("Intitializing repo") self.cli.git_init() # NOTE: For anonymous sessions email and name are not known for the user if self.user.email is not None: + logging.info(f"Setting email {self.user.email} in git config") self.cli.git_config("user.email", self.user.email) if self.user.full_name is not None: + logging.info(f"Setting name {self.user.full_name} in git config") self.cli.git_config("user.name", self.user.full_name) self.cli.git_config("push.default", "simple") @@ -90,19 +90,27 @@ def _setup_cloudstorage_symlink(self, mount_folder): @contextmanager def _temp_plaintext_credentials(self): + # NOTE: If "lfs." is included in urljoin it does not work properly + lfs_auth_setting = "lfs." + urljoin(f"{self.repo_url}/", "info/lfs.access") try: credential_loc = Path("/tmp/git-credentials") with open(credential_loc, "w") as f: f.write(f"https://oauth2:{self.user.oauth_token}@{self.git_host}") - yield self.cli.git_config( - "credential.helper", f"store --file={credential_loc}" - ) + # NOTE: This is required to let LFS know that it should use basic auth to pull data. + # If not set LFS will try to pull data without any auth and will then set this field + # automatically but the password and username will be required for every git + # operation. Setting this option when basic auth is used to clone with the context + # manager and then unsetting it prevents getting in trouble when the user is in the + # session by having this setting left over in the session after initialization. + self.cli.git_config(lfs_auth_setting, "basic") + yield self.cli.git_config("credential.helper", f"store --file={credential_loc}") finally: # NOTE: Temp credentials MUST be cleaned up on context manager exit logging.info("Cleaning up git credentials after cloning.") credential_loc.unlink(missing_ok=True) try: self.cli.git_config("--unset", "credential.helper") + self.cli.git_config("--unset", lfs_auth_setting) except GitCommandError as err: # INFO: The repo is fully deleted when an error occurs so when the context # manager exits then this results in an unnecessary error that masks the true @@ -189,16 +197,19 @@ def run(self, recover_autosave, session_branch, root_commit_sha, s3_mount): logging.info("The repo already exists - exiting.") return self._initialize_repo() - with self._temp_plaintext_credentials(): + if self.user.is_anonymous: self._clone(session_branch) - if recover_autosave: - autosave_branch = self._get_autosave_branch( - session_branch, root_commit_sha - ) - if autosave_branch is None: - self.cli.git_reset("--hard", root_commit_sha) - else: - self._recover_autosave(autosave_branch) + else: + with self._temp_plaintext_credentials(): + self._clone(session_branch) + if recover_autosave: + autosave_branch = self._get_autosave_branch( + session_branch, root_commit_sha + ) + if autosave_branch is None: + self.cli.git_reset("--hard", root_commit_sha) + else: + self._recover_autosave(autosave_branch) self._setup_proxy() if s3_mount: self._setup_cloudstorage_symlink(s3_mount) diff --git a/git_services/git_services/init/config.py b/git_services/git_services/init/config.py index ad94ea70f..ece139caa 100644 --- a/git_services/git_services/init/config.py +++ b/git_services/git_services/init/config.py @@ -11,14 +11,21 @@ class User: """Class for keep track of basic user info used in cloning a repo.""" username: str - oauth_token: str + oauth_token: Optional[str] = None full_name: Optional[str] = None email: Optional[str] = None def __post_init__(self): # NOTE: Sanitize user input that is used in running git shell commands with shlex - self.full_name = shlex.quote(self.full_name) - self.email = shlex.quote(self.email) + # NOTE: shlex.quote(None) == "''" + if self.full_name is not None: + self.full_name = shlex.quote(self.full_name) + if self.email is not None: + self.email = shlex.quote(self.email) + + @property + def is_anonymous(self) -> bool: + return self.oauth_token is None or self.oauth_token == "" @dataclass