Skip to content

Commit b3ce1b5

Browse files
committed
Add credentials support to clone_repository()
1 parent 75f9f88 commit b3ce1b5

File tree

5 files changed

+95
-69
lines changed

5 files changed

+95
-69
lines changed

pygit2/__init__.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def init_repository(path, bare=False):
5151

5252
def clone_repository(
5353
url, path, bare=False, ignore_cert_errors=False,
54-
remote_name="origin", checkout_branch=None):
54+
remote_name="origin", checkout_branch=None, credentials=None):
5555
"""Clones a new Git repository from *url* in the given *path*.
5656
5757
Returns a Repository class pointing to the newly cloned repository.
@@ -67,10 +67,15 @@ def clone_repository(
6767
:param str checkout_branch: Branch to checkout after the
6868
clone. The default is to use the remote's default branch.
6969
70+
:param callable credentials: authentication to use if the remote
71+
requires it
72+
73+
:rtype: Repository
74+
7075
"""
7176

7277
_pygit2.clone_repository(
73-
url, path, bare, ignore_cert_errors, remote_name, checkout_branch)
78+
url, path, bare, ignore_cert_errors, remote_name, checkout_branch, credentials)
7479
return Repository(path)
7580

7681
settings = Settings()

src/pygit2.c

+14-2
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ init_repository(PyObject *self, PyObject *args) {
118118
Py_RETURN_NONE;
119119
};
120120

121+
static int
122+
credentials_cb(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *data)
123+
{
124+
PyObject *credentials = (PyObject *) data;
125+
126+
return callable_to_credentials(out, url, username_from_url, allowed_types, credentials);
127+
}
128+
121129
PyDoc_STRVAR(clone_repository__doc__,
122130
"clone_repository(url, path, bare, remote_name, checkout_branch)\n"
123131
"\n"
@@ -146,18 +154,22 @@ clone_repository(PyObject *self, PyObject *args) {
146154
const char *path;
147155
unsigned int bare, ignore_cert_errors;
148156
const char *remote_name, *checkout_branch;
157+
PyObject *credentials;
149158
int err;
150159
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
151160

152-
if (!PyArg_ParseTuple(args, "zzIIzz",
153-
&url, &path, &bare, &ignore_cert_errors, &remote_name, &checkout_branch))
161+
if (!PyArg_ParseTuple(args, "zzIIzzO",
162+
&url, &path, &bare, &ignore_cert_errors, &remote_name, &checkout_branch, &credentials))
154163
return NULL;
155164

156165
opts.bare = bare;
157166
opts.ignore_cert_errors = ignore_cert_errors;
158167
opts.remote_name = remote_name;
159168
opts.checkout_branch = checkout_branch;
160169

170+
opts.remote_callbacks.credentials = credentials_cb;
171+
opts.remote_callbacks.payload = credentials;
172+
161173
err = git_clone(&repo, url, path, &opts);
162174
if (err < 0)
163175
return Error_set(err);

src/remote.c

+1-65
Original file line numberDiff line numberDiff line change
@@ -158,76 +158,12 @@ progress_cb(const char *str, int len, void *data)
158158
return 0;
159159
}
160160

161-
static int
162-
py_cred_to_git_cred(git_cred **out, PyObject *py_cred, unsigned int allowed)
163-
{
164-
Cred *base_cred;
165-
int err;
166-
167-
if (!PyObject_TypeCheck(py_cred, &CredUsernamePasswordType) &&
168-
!PyObject_TypeCheck(py_cred, &CredSshKeyType)) {
169-
PyErr_SetString(PyExc_TypeError, "unkown credential type");
170-
return -1;
171-
}
172-
173-
base_cred = (Cred *) py_cred;
174-
175-
/* Sanity check, make sure we're given credentials we can use */
176-
if (!(allowed & base_cred->credtype)) {
177-
PyErr_SetString(PyExc_TypeError, "invalid credential type");
178-
return -1;
179-
}
180-
181-
switch (base_cred->credtype) {
182-
case GIT_CREDTYPE_USERPASS_PLAINTEXT:
183-
{
184-
CredUsernamePassword *cred = (CredUsernamePassword *) base_cred;
185-
err = git_cred_userpass_plaintext_new(out, cred->username, cred->password);
186-
break;
187-
}
188-
case GIT_CREDTYPE_SSH_KEY:
189-
{
190-
CredSshKey *cred = (CredSshKey *) base_cred;
191-
err = git_cred_ssh_key_new(out, cred->username, cred->pubkey, cred->privkey, cred->passphrase);
192-
break;
193-
}
194-
default:
195-
PyErr_SetString(PyExc_TypeError, "unsupported credential type");
196-
err = -1;
197-
break;
198-
}
199-
200-
return err;
201-
}
202-
203161
static int
204162
credentials_cb(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *data)
205163
{
206164
Remote *remote = (Remote *) data;
207-
PyObject *arglist, *py_cred;
208-
int err;
209-
210-
if (remote->credentials == NULL)
211-
return 0;
212-
213-
if (!PyCallable_Check(remote->credentials)) {
214-
PyErr_SetString(PyExc_TypeError, "credentials callback is not callable");
215-
return -1;
216-
}
217-
218-
arglist = Py_BuildValue("(szI)", url, username_from_url, allowed_types);
219-
py_cred = PyObject_CallObject(remote->credentials, arglist);
220-
Py_DECREF(arglist);
221-
222-
if (!py_cred)
223-
return -1;
224-
225-
err = py_cred_to_git_cred(out, py_cred, allowed_types);
226-
227-
228-
Py_DECREF(py_cred);
229165

230-
return err;
166+
return callable_to_credentials(out, url, username_from_url, allowed_types, remote->credentials);
231167
}
232168

233169
static int

src/utils.c

+71
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "utils.h"
3232

3333
extern PyTypeObject ReferenceType;
34+
extern PyTypeObject CredUsernamePasswordType;
35+
extern PyTypeObject CredSshKeyType;
3436

3537
/**
3638
* py_str_to_c_str() returns a newly allocated C string holding the string
@@ -153,3 +155,72 @@ get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist)
153155

154156
return -1;
155157
}
158+
159+
static int
160+
py_cred_to_git_cred(git_cred **out, PyObject *py_cred, unsigned int allowed)
161+
{
162+
Cred *base_cred;
163+
int err;
164+
165+
if (!PyObject_TypeCheck(py_cred, &CredUsernamePasswordType) &&
166+
!PyObject_TypeCheck(py_cred, &CredSshKeyType)) {
167+
PyErr_SetString(PyExc_TypeError, "unkown credential type");
168+
return -1;
169+
}
170+
171+
base_cred = (Cred *) py_cred;
172+
173+
/* Sanity check, make sure we're given credentials we can use */
174+
if (!(allowed & base_cred->credtype)) {
175+
PyErr_SetString(PyExc_TypeError, "invalid credential type");
176+
return -1;
177+
}
178+
179+
switch (base_cred->credtype) {
180+
case GIT_CREDTYPE_USERPASS_PLAINTEXT:
181+
{
182+
CredUsernamePassword *cred = (CredUsernamePassword *) base_cred;
183+
err = git_cred_userpass_plaintext_new(out, cred->username, cred->password);
184+
break;
185+
}
186+
case GIT_CREDTYPE_SSH_KEY:
187+
{
188+
CredSshKey *cred = (CredSshKey *) base_cred;
189+
err = git_cred_ssh_key_new(out, cred->username, cred->pubkey, cred->privkey, cred->passphrase);
190+
break;
191+
}
192+
default:
193+
PyErr_SetString(PyExc_TypeError, "unsupported credential type");
194+
err = -1;
195+
break;
196+
}
197+
198+
return err;
199+
}
200+
201+
int
202+
callable_to_credentials(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, PyObject *credentials)
203+
{
204+
int err;
205+
PyObject *py_cred, *arglist;
206+
207+
if (credentials == NULL)
208+
return 0;
209+
210+
if (!PyCallable_Check(credentials)) {
211+
PyErr_SetString(PyExc_TypeError, "credentials callback is not callable");
212+
return -1;
213+
}
214+
215+
arglist = Py_BuildValue("(szI)", url, username_from_url, allowed_types);
216+
py_cred = PyObject_CallObject(credentials, arglist);
217+
Py_DECREF(arglist);
218+
219+
if (!py_cred)
220+
return -1;
221+
222+
err = py_cred_to_git_cred(out, py_cred, allowed_types);
223+
Py_DECREF(py_cred);
224+
225+
return err;
226+
}

src/utils.h

+2
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ const char *py_str_borrow_c_str(PyObject **tvaue, PyObject *value, const char *e
117117
PyObject * get_pylist_from_git_strarray(git_strarray *strarray);
118118
int get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist);
119119

120+
int callable_to_credentials(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, PyObject *credentials);
121+
120122
#define py_path_to_c_str(py_path) \
121123
py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding)
122124

0 commit comments

Comments
 (0)