rjgoyln commented on code in PR #64105:
URL: https://github.com/apache/airflow/pull/64105#discussion_r4062840826
##########
providers/git/src/airflow/providers/git/hooks/git.py:
##########
@@ -242,73 +247,91 @@ def _ensure_github_app_token(self) -> None:
)
self.user_name, self.auth_token, self.github_app_token_exp =
self._get_github_app_token()
+ def _strip_embedded_credentials(self) -> tuple[str | None, str | None]:
+ """Take any ``user:password@`` out of the repo url and return what it
held."""
+ if not isinstance(self.repo_url, str) or not
self.repo_url.startswith(("http://", "https://")):
+ return None, None
+ scheme, separator, rest = self.repo_url.partition("://")
+ authority, slash, path = rest.partition("/")
+ userinfo, at_sign, host = authority.rpartition("@")
+ user, _, password = userinfo.partition(":")
+ # A bare ``user@`` holds no secret, so leave those urls exactly as the
connection wrote
+ # them; anything git clones from a stripped url would lose the
username for nothing.
+ if not at_sign or not password:
+ return None, None
+ self.repo_url = f"{scheme}{separator}{host}{slash}{path}"
+ return unquote(user) or None, unquote(password)
+
+ def _extract_credential_scope(self) -> str:
+ """Return the ``<scheme>://<host>[:port]`` git matches a credential
config against."""
+ scheme, _, rest = str(self.repo_url).partition("://")
+ host = rest.partition("/")[0].rpartition("@")[2]
+ return f"{scheme}://{host}" if host else ""
+
@contextlib.contextmanager
- def _github_app_askpass_env(self) -> Generator[None]:
- if not self.auth_token:
+ def _token_credential_env(self) -> Generator[None]:
+ """Hand the token to git through a credential helper scoped to the
repository's host."""
+ # Credential helpers only serve http(s); an SSH connection that
happens to carry a
+ # password would gain nothing from one.
+ if not self.auth_token or not
str(self.repo_url).startswith(("http://", "https://")):
yield
return
- token = shlex.quote(self.auth_token)
- with tempfile.NamedTemporaryFile(mode="w", suffix=".sh", delete=True)
as askpass_script:
- askpass_script.write(
- "#!/bin/sh\n"
- 'case "$1" in\n'
- " *Username*) echo x-access-token;;\n"
- f" *Password*) echo {token};;\n"
- f" *) echo {token};;\n"
- "esac\n"
- )
- askpass_script.flush()
- os.chmod(askpass_script.name, stat.S_IRWXU)
+ scope = self._extract_credential_scope()
+ if not scope:
+ yield
+ return
+
+ with tempfile.TemporaryDirectory() as helper_dir:
+ helper_path = os.path.join(helper_dir, "credential-helper.sh")
+ # git matches the configured scope against the url it parsed, then
hands the helper
+ # structured fields on stdin. A submodule elsewhere never reaches
this helper, and no
+ # part of the decision depends on the wording of a human-readable
prompt.
+ # Written and closed before git runs: Linux refuses to exec a file
that is still
+ # open for writing, which git surfaces as "cannot exec: Text file
busy".
+ with open(helper_path, "w") as helper_script:
+ helper_script.write(
+ r"""#!/bin/sh
+cat > /dev/null
+[ "$1" = get ] || exit 0
+printf 'username=%s\npassword=%s\n' "$AIRFLOW_GIT_USER" "$AIRFLOW_GIT_TOKEN"
+"""
+ )
+ os.chmod(helper_path, stat.S_IRWXU)
- old_askpass = os.environ.get("GIT_ASKPASS")
- old_lc_all = os.environ.get("LC_ALL")
- old_terminal_prompt = os.environ.get("GIT_TERMINAL_PROMPT")
+ # Append to any GIT_CONFIG_* the deployment already exports rather
than replacing it.
+ try:
+ index = int(os.environ.get("GIT_CONFIG_COUNT", "0"))
+ except ValueError:
+ index = 0
+ # System/global config loads before env-supplied config, so a
deployment-wide
+ # `credential.helper` would otherwise answer `get` first and our
token would never
+ # be used. git also invokes every helper on `approve`, so a
`store` helper would
+ # persist it to ~/.git-credentials. Reset the scope to empty first
(git help credentials).
+ values = {
+ "GIT_CONFIG_COUNT": str(index + 2),
+ f"GIT_CONFIG_KEY_{index}": f"credential.{scope}.helper",
+ f"GIT_CONFIG_VALUE_{index}": "",
+ f"GIT_CONFIG_KEY_{index + 1}": f"credential.{scope}.helper",
+ f"GIT_CONFIG_VALUE_{index + 1}": helper_path,
Review Comment:
Fixed in b0ac2fd24e with ! + shlex.quote(helper_path), and added a
regression test for TMPDIR paths containing spaces.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]