Skip to content

Commit

Permalink
Add enum CredentialType
Browse files Browse the repository at this point in the history
  • Loading branch information
jorio committed Nov 22, 2023
1 parent 983fef5 commit 6f76afe
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 30 deletions.
4 changes: 2 additions & 2 deletions docs/recipes/git-clone-ssh.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ Example for cloning a git repository over ssh.
class MyRemoteCallbacks(pygit2.RemoteCallbacks):
def credentials(self, url, username_from_url, allowed_types):
if allowed_types & pygit2.credentials.GIT_CREDENTIAL_USERNAME:
if allowed_types & pygit2.CredentialType.USERNAME:
return pygit2.Username("git")
elif allowed_types & pygit2.credentials.GIT_CREDENTIAL_SSH_KEY:
elif allowed_types & pygit2.CredentialType.SSH_KEY:
return pygit2.Keypair("git", "id_rsa.pub", "id_rsa", "")
else:
return None
Expand Down
10 changes: 10 additions & 0 deletions pygit2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
CheckoutNotifyFlags,
CheckoutStrategy,
ConfigLevel,
CredentialType,
DescribeStrategy,
DeltaFlags,
DeltaStatus,
Expand Down Expand Up @@ -162,6 +163,15 @@
GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED = StashApplyProgress.CHECKOUT_MODIFIED
GIT_STASH_APPLY_PROGRESS_DONE = StashApplyProgress.DONE

# GIT_CREDENTIAL_* values for legacy code
GIT_CREDENTIAL_USERPASS_PLAINTEXT = CredentialType.USERPASS_PLAINTEXT
GIT_CREDENTIAL_SSH_KEY = CredentialType.SSH_KEY
GIT_CREDENTIAL_SSH_CUSTOM = CredentialType.SSH_CUSTOM
GIT_CREDENTIAL_DEFAULT = CredentialType.DEFAULT
GIT_CREDENTIAL_SSH_INTERACTIVE = CredentialType.SSH_INTERACTIVE
GIT_CREDENTIAL_USERNAME = CredentialType.USERNAME
GIT_CREDENTIAL_SSH_MEMORY = CredentialType.SSH_MEMORY

# libgit version tuple
LIBGIT2_VER = (LIBGIT2_VER_MAJOR, LIBGIT2_VER_MINOR, LIBGIT2_VER_REVISION)

Expand Down
22 changes: 13 additions & 9 deletions pygit2/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@
# Standard Library
from contextlib import contextmanager
from functools import wraps
from typing import Optional
from typing import Optional, Union

# pygit2
from ._pygit2 import Oid, DiffFile
from .enums import CheckoutNotifyFlags, CheckoutStrategy, StashApplyProgress
from .enums import CheckoutNotifyFlags, CheckoutStrategy, CredentialType, StashApplyProgress
from .errors import check_error, Passthrough
from .ffi import ffi, C
from .utils import maybe_string, to_bytes, ptr_to_bytes, StrArray
Expand Down Expand Up @@ -131,7 +131,7 @@ def sideband_progress(self, string):
Progress output from the remote.
"""

def credentials(self, url, username_from_url, allowed_types):
def credentials(self, url: str, username_from_url: Union[str, None], allowed_types: CredentialType):
"""
Credentials callback. If the remote server requires authentication,
this function will be called and its return value used for
Expand All @@ -148,8 +148,9 @@ def credentials(self, url, username_from_url, allowed_types):
username_from_url : str or None
Username extracted from the url, if any.
allowed_types : int
Credential types supported by the remote.
allowed_types : CredentialType
A combination of CredentialType bitflags representing the
credential types supported by the remote.
"""
raise Passthrough

Expand Down Expand Up @@ -477,6 +478,9 @@ def _credentials_cb(cred_out, url, username, allowed, data):
if not credentials:
return 0

# convert int flags to enum before forwarding to user code
allowed = CredentialType(allowed)

ccred = get_credentials(credentials, url, username, allowed)
cred_out[0] = ccred[0]
return 0
Expand Down Expand Up @@ -575,12 +579,12 @@ def get_credentials(fn, url, username, allowed):
raise TypeError("invalid credential type")

ccred = ffi.new('git_credential **')
if cred_type == C.GIT_CREDENTIAL_USERPASS_PLAINTEXT:
if cred_type == CredentialType.USERPASS_PLAINTEXT:
name, passwd = credential_tuple
err = C.git_credential_userpass_plaintext_new(ccred, to_bytes(name),
to_bytes(passwd))

elif cred_type == C.GIT_CREDENTIAL_SSH_KEY:
elif cred_type == CredentialType.SSH_KEY:
name, pubkey, privkey, passphrase = credential_tuple
name = to_bytes(name)
if pubkey is None and privkey is None:
Expand All @@ -590,11 +594,11 @@ def get_credentials(fn, url, username, allowed):
to_bytes(privkey),
to_bytes(passphrase))

elif cred_type == C.GIT_CREDENTIAL_USERNAME:
elif cred_type == CredentialType.USERNAME:
name, = credential_tuple
err = C.git_credential_username_new(ccred, to_bytes(name))

elif cred_type == C.GIT_CREDENTIAL_SSH_MEMORY:
elif cred_type == CredentialType.SSH_MEMORY:
name, pubkey, privkey, passphrase = credential_tuple
if pubkey is None and privkey is None:
raise TypeError("SSH keys from memory are empty")
Expand Down
25 changes: 9 additions & 16 deletions pygit2/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,7 @@

from .ffi import C


GIT_CREDENTIAL_USERPASS_PLAINTEXT = C.GIT_CREDENTIAL_USERPASS_PLAINTEXT
GIT_CREDENTIAL_SSH_KEY = C.GIT_CREDENTIAL_SSH_KEY
GIT_CREDENTIAL_SSH_CUSTOM = C.GIT_CREDENTIAL_SSH_CUSTOM
GIT_CREDENTIAL_DEFAULT = C.GIT_CREDENTIAL_DEFAULT
GIT_CREDENTIAL_SSH_INTERACTIVE = C.GIT_CREDENTIAL_SSH_INTERACTIVE
GIT_CREDENTIAL_USERNAME = C.GIT_CREDENTIAL_USERNAME
GIT_CREDENTIAL_SSH_MEMORY = C.GIT_CREDENTIAL_SSH_MEMORY
from .enums import CredentialType


class Username:
Expand All @@ -46,8 +39,8 @@ def __init__(self, username):
self._username = username

@property
def credential_type(self):
return GIT_CREDENTIAL_USERNAME
def credential_type(self) -> CredentialType:
return CredentialType.USERNAME

@property
def credential_tuple(self):
Expand All @@ -69,8 +62,8 @@ def __init__(self, username, password):
self._password = password

@property
def credential_type(self):
return GIT_CREDENTIAL_USERPASS_PLAINTEXT
def credential_type(self) -> CredentialType:
return CredentialType.USERPASS_PLAINTEXT

@property
def credential_tuple(self):
Expand Down Expand Up @@ -110,8 +103,8 @@ def __init__(self, username, pubkey, privkey, passphrase):
self._passphrase = passphrase

@property
def credential_type(self):
return GIT_CREDENTIAL_SSH_KEY
def credential_type(self) -> CredentialType:
return CredentialType.SSH_KEY

@property
def credential_tuple(self):
Expand All @@ -128,5 +121,5 @@ def __init__(self, username):

class KeypairFromMemory(Keypair):
@property
def credential_type(self):
return GIT_CREDENTIAL_SSH_MEMORY
def credential_type(self) -> CredentialType:
return CredentialType.SSH_MEMORY
37 changes: 37 additions & 0 deletions pygit2/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,43 @@ class ConfigLevel(IntEnum):
specific config file available that actually is loaded)"""


class CredentialType(IntFlag):
"""
Supported credential types. This represents the various types of
authentication methods supported by the library.
"""

USERPASS_PLAINTEXT = C.GIT_CREDENTIAL_USERPASS_PLAINTEXT
"A vanilla user/password request"

SSH_KEY = C.GIT_CREDENTIAL_SSH_KEY
"An SSH key-based authentication request"

SSH_CUSTOM = C.GIT_CREDENTIAL_SSH_CUSTOM
"An SSH key-based authentication request, with a custom signature"

DEFAULT = C.GIT_CREDENTIAL_DEFAULT
"An NTLM/Negotiate-based authentication request."

SSH_INTERACTIVE = C.GIT_CREDENTIAL_SSH_INTERACTIVE
"An SSH interactive authentication request."

USERNAME = C.GIT_CREDENTIAL_USERNAME
"""
Username-only authentication request.
Used as a pre-authentication step if the underlying transport (eg. SSH,
with no username in its URL) does not know which username to use.
"""

SSH_MEMORY = C.GIT_CREDENTIAL_SSH_MEMORY
"""
An SSH key-based authentication request.
Allows credentials to be read from memory instead of files.
Note that because of differences in crypto backend support, it might
not be functional.
"""


class DeltaFlags(IntFlag):
"""
Flags for the delta object and the file objects on each side.
Expand Down
6 changes: 3 additions & 3 deletions test/test_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import pytest

import pygit2
from pygit2 import Username, UserPass, Keypair, KeypairFromAgent, KeypairFromMemory
from pygit2 import CredentialType, Username, UserPass, Keypair, KeypairFromAgent, KeypairFromMemory
from . import utils


Expand Down Expand Up @@ -127,7 +127,7 @@ def test_keypair_from_memory(tmp_path, pygit2_empty_key):
def test_callback(testrepo):
class MyCallbacks(pygit2.RemoteCallbacks):
def credentials(testrepo, url, username, allowed):
assert allowed & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT
assert allowed & CredentialType.USERPASS_PLAINTEXT
raise Exception("I don't know the password")

url = "https://github.com/github/github"
Expand All @@ -138,7 +138,7 @@ def credentials(testrepo, url, username, allowed):
def test_bad_cred_type(testrepo):
class MyCallbacks(pygit2.RemoteCallbacks):
def credentials(testrepo, url, username, allowed):
assert allowed & pygit2.GIT_CREDENTIAL_USERPASS_PLAINTEXT
assert allowed & CredentialType.USERPASS_PLAINTEXT
return Keypair("git", "foo.pub", "foo", "sekkrit")

url = "https://github.com/github/github"
Expand Down

0 comments on commit 6f76afe

Please sign in to comment.