Remote: support credentials via CFFI

This commit is contained in:
Carlos Martín Nieto 2014-04-12 17:13:15 +02:00
parent c76c3f0195
commit 4ef3be18cc
2 changed files with 65 additions and 1 deletions

@ -60,6 +60,13 @@ typedef enum {
GIT_DIRECTION_PUSH = 1
} git_direction;
typedef enum {
GIT_CREDTYPE_USERPASS_PLAINTEXT = ...,
GIT_CREDTYPE_SSH_KEY = ...,
GIT_CREDTYPE_SSH_CUSTOM = ...,
GIT_CREDTYPE_DEFAULT = ...,
} git_credtype_t;
typedef struct git_remote_callbacks {
unsigned int version;
int (*progress)(const char *str, int len, void *data);
@ -125,3 +132,14 @@ int git_refspec_dst_matches(const git_refspec *refspec, const char *refname);
int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name);
int git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, const char *name);
int git_cred_userpass_plaintext_new(
git_cred **out,
const char *username,
const char *password);
int git_cred_ssh_key_new(
git_cred **out,
const char *username,
const char *publickey,
const char *privatekey,
const char *passphrase);

@ -57,11 +57,13 @@ class Remote(object):
self._repo = repo
self._remote = ptr
self._stored_exception = None
# Build the callback structure
callbacks = ffi.new('git_remote_callbacks *')
callbacks.version = 1
callbacks.transfer_progress = self._transfer_progress_cb
callbacks.credentials = self._credentials_cb
# We need to make sure that this handle stays alive
self._self_handle = ffi.new_handle(self)
callbacks.payload = self._self_handle
@ -103,8 +105,9 @@ class Remote(object):
check_error(err)
def fetch(self):
self._stored_exception = None
err = C.git_remote_fetch(self._remote)
if err == C.GIT_EUSER:
if self._stored_exception:
raise self._stored_exception
check_error(err)
@ -205,3 +208,46 @@ class Remote(object):
return C.GIT_EUSER
return 0
@ffi.callback('int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data)')
def _credentials_cb(cred_out, url, username, allowed, data):
self = ffi.from_handle(data)
if not hasattr(self, 'credentials'):
return 0
try:
url_str = maybe_string(url)
username_str = maybe_string(username)
creds = self.credentials(url_str, username_str, allowed)
if not hasattr(creds, 'credential_type') or not hasattr(creds, 'credential_tuple'):
raise TypeError("credential does not implement interface")
cred_type = creds.credential_type
if not (allowed & cred_type):
raise TypeError("invalid credential type")
ccred = ffi.new('git_cred **')
if cred_type == C.GIT_CREDTYPE_USERPASS_PLAINTEXT:
name, passwd = creds.credential_tuple
err = C.git_cred_userpass_plaintext_new(ccred, to_str(name), to_str(passwd))
elif cred_type == C.GIT_CREDTYPE_SSH_KEY:
name, pubkey, privkey, passphrase = creds.credential_tuple
err = C.git_cred_ssh_key_new(ccred, to_str(name),to_str(pubkey),
to_str(privkey), to_str(passphrase))
else:
raise TypeError("unsupported credential type")
check_error(err)
cred_out[0] = ccred[0]
except Exception, e:
self._stored_exception = e
return C.GIT_EUSER
return 0