Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ package_dir=
packages = find:
install_requires=
gitpython>3
dulwich>=0.20.46
dulwich>=0.20.49
pygit2>=1.10.0
pygtrie>=2.3.2
fsspec>=2021.7.0
Expand Down
65 changes: 4 additions & 61 deletions src/scmrepo/git/backend/dulwich/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,72 +31,15 @@
import shutil
import subprocess # nosec B404
import sys
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
from urllib.parse import ParseResult, urlparse
from typing import Any, Dict, List, Optional, Tuple, Union
from urllib.parse import urlparse

from dulwich.config import ConfigDict, StackedConfig
from dulwich.config import StackedConfig
from dulwich.credentials import urlmatch_credential_sections

SectionLike = Union[bytes, str, Tuple[Union[bytes, str], ...]]


def match_urls(url: ParseResult, url_prefix: ParseResult) -> bool:
base_match = (
url.scheme == url_prefix.scheme
and url.hostname == url_prefix.hostname
and url.port == url_prefix.port
)
user_match = url.username == url_prefix.username if url_prefix.username else True
path_match = url.path.rstrip("/").startswith(url_prefix.path.rstrip())
return base_match and user_match and path_match


def match_partial_url(valid_url: ParseResult, partial_url: str) -> bool:
"""matches a parsed url with a partial url (no scheme/netloc)"""
if "://" not in partial_url:
parsed = urlparse("scheme://" + partial_url)
else:
parsed = urlparse(partial_url)
if valid_url.scheme != parsed.scheme:
return False

if any(
(
(parsed.hostname and valid_url.hostname != parsed.hostname),
(parsed.username and valid_url.username != parsed.username),
(parsed.port and valid_url.port != parsed.port),
(parsed.path and parsed.path.rstrip("/") != valid_url.path.rstrip("/")),
),
):
return False

return True


def urlmatch_credential_sections(
config: ConfigDict, url: Optional[str]
) -> Iterator[SectionLike]:
"""Returns credential sections from the config which match the given URL"""
encoding = config.encoding or sys.getdefaultencoding()
parsed_url = urlparse(url or "")
for config_section in config.sections():
if config_section[0] != b"credential":
continue

if len(config_section) < 2:
yield config_section
continue

config_url = config_section[1].decode(encoding)
parsed_config_url = urlparse(config_url)
if parsed_config_url.scheme and parsed_config_url.netloc:
is_match = match_urls(parsed_url, parsed_config_url)
else:
is_match = match_partial_url(parsed_url, config_url)

if is_match:
yield config_section


class CredentialNotFoundError(Exception):
"""Error occurred while retrieving credentials/no credentials available."""

Expand Down