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
20 changes: 11 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ matrix:
- rvm: rbx-19mode
- rvm: rbx-20mode

# We need the config so the tests don't fail
script:
- git config --global user.name 'The rugged tests are fragile'
- bundle exec rake
install:
- bundle install --deployment
- sudo apt-get -qq update
- sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server

script: script/cibuild

# Notify development list when needed
notifications:
recipients:
- vicent@github.com
email:
on_success: change
on_failure: always
recipients:
- vicent@github.com
email:
on_success: change
on_failure: always
1 change: 1 addition & 0 deletions ext/rugged/rugged.c
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ void Init_rugged(void)
Init_rugged_diff_hunk();
Init_rugged_diff_line();
Init_rugged_blame();
Init_rugged_cred();

/*
* Sort the repository contents in no particular ordering;
Expand Down
1 change: 1 addition & 0 deletions ext/rugged/rugged.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ void Init_rugged_diff_delta(void);
void Init_rugged_diff_hunk(void);
void Init_rugged_diff_line(void);
void Init_rugged_blame(void);
void Init_rugged_cred(void);

VALUE rb_git_object_init(git_otype type, int argc, VALUE *argv, VALUE self);

Expand Down
40 changes: 40 additions & 0 deletions ext/rugged/rugged_cred.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* The MIT License
*
* Copyright (c) 2012 GitHub, Inc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "rugged.h"

extern VALUE rb_mRugged;
VALUE rb_mRuggedCred;
VALUE rb_cRuggedCredPlaintext;
VALUE rb_cRuggedCredSshKey;
VALUE rb_cRuggedCredDefault;

void Init_rugged_cred(void)
{
rb_mRuggedCred = rb_define_module_under(rb_mRugged, "Credentials");

rb_cRuggedCredPlaintext = rb_define_class_under(rb_mRuggedCred, "Plaintext", rb_cObject);
rb_cRuggedCredSshKey = rb_define_class_under(rb_mRuggedCred, "SshKey", rb_cObject);
rb_cRuggedCredDefault = rb_define_class_under(rb_mRuggedCred, "Default", rb_cObject);
}
194 changes: 168 additions & 26 deletions ext/rugged/rugged_repo.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ extern VALUE rb_cRuggedTag;
extern VALUE rb_cRuggedTree;
extern VALUE rb_cRuggedReference;

extern VALUE rb_cRuggedCredPlaintext;
extern VALUE rb_cRuggedCredSshKey;
extern VALUE rb_cRuggedCredDefault;

VALUE rb_cRuggedRepo;
VALUE rb_cRuggedOdbObject;

Expand Down Expand Up @@ -309,6 +313,7 @@ struct rugged_remote_cb_payload
VALUE completion;
VALUE transfer_progress;
VALUE update_tips;
VALUE credentials;
int exception;
};

Expand All @@ -326,35 +331,169 @@ static int rugged__remote_transfer_progress_cb(const git_transfer_progress *stat
return remote_payload->exception ? GIT_ERROR : GIT_OK;
}

struct extract_cred_payload
{
VALUE rb_cred;
git_cred **cred;
unsigned int allowed_types;
};

static VALUE rugged__extract_cred(VALUE payload) {
struct extract_cred_payload *cred_payload = (struct extract_cred_payload*)payload;
git_cred **cred = cred_payload->cred;
VALUE rb_cred = cred_payload->rb_cred;

if (rb_obj_is_kind_of(rb_cred, rb_cRuggedCredPlaintext)) {
if (!(cred_payload->allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT)) {
rb_raise(rb_eArgError, "Invalid credential type");
} else {
VALUE rb_username = rb_iv_get(rb_cred, "@username");
VALUE rb_password = rb_iv_get(rb_cred, "@password");

Check_Type(rb_username, T_STRING);
Check_Type(rb_password, T_STRING);


rugged_exception_check(
git_cred_userpass_plaintext_new(cred,
StringValueCStr(rb_username), StringValueCStr(rb_password)));
}
} else if (rb_obj_is_kind_of(rb_cred, rb_cRuggedCredSshKey)) {
if (!(cred_payload->allowed_types & GIT_CREDTYPE_SSH_KEY)) {
rb_raise(rb_eArgError, "Invalid credential type");
} else {
VALUE rb_username = rb_iv_get(rb_cred, "@username");
VALUE rb_publickey = rb_iv_get(rb_cred, "@publickey");
VALUE rb_privatekey = rb_iv_get(rb_cred, "@privatekey");
VALUE rb_passphrase = rb_iv_get(rb_cred, "@passphrase");

Check_Type(rb_privatekey, T_STRING);

if (!NIL_P(rb_username))
Check_Type(rb_username, T_STRING);
if (!NIL_P(rb_publickey))
Check_Type(rb_publickey, T_STRING);
if (!NIL_P(rb_passphrase))
Check_Type(rb_passphrase, T_STRING);

rugged_exception_check(
git_cred_ssh_key_new(cred,
NIL_P(rb_username) ? NULL : StringValueCStr(rb_username),
NIL_P(rb_publickey) ? NULL : StringValueCStr(rb_publickey),
StringValueCStr(rb_privatekey),
NIL_P(rb_passphrase) ? NULL : StringValueCStr(rb_passphrase)));
}
} else if (rb_obj_is_kind_of(rb_cred, rb_cRuggedCredDefault)) {
if (!(cred_payload->allowed_types & GIT_CREDTYPE_SSH_KEY)) {
rb_raise(rb_eArgError, "Invalid credential type");
} else {
rugged_exception_check(git_cred_default_new(cred));
}
}

return Qnil;
}

static int rugged__remote_credentials_cb(
git_cred **cred,
const char *url,
const char *username_from_url,
unsigned int allowed_types,
void *payload)
{
struct rugged_remote_cb_payload *remote_payload = payload;
struct extract_cred_payload cred_payload;
VALUE args = rb_ary_new2(4), rb_allowed_types = rb_ary_new();

if (allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT)
rb_ary_push(rb_allowed_types, CSTR2SYM("plaintext"));

if (allowed_types & GIT_CREDTYPE_SSH_KEY)
rb_ary_push(rb_allowed_types, CSTR2SYM("ssh_key"));

if (allowed_types & GIT_CREDTYPE_DEFAULT)
rb_ary_push(rb_allowed_types, CSTR2SYM("default"));

rb_ary_push(args, remote_payload->credentials);
rb_ary_push(args, url ? rb_str_new2(url) : Qnil);
rb_ary_push(args, username_from_url ? rb_str_new2(username_from_url) : Qnil);
rb_ary_push(args, rb_allowed_types);

cred_payload.cred = cred;
cred_payload.rb_cred = rb_protect(rugged__block_yield_splat, args, &remote_payload->exception);
cred_payload.allowed_types = allowed_types;

if (!remote_payload->exception)
rb_protect(rugged__extract_cred, (VALUE)&cred_payload, &remote_payload->exception);

return remote_payload->exception ? GIT_ERROR : GIT_OK;
}

static int rugged__default_remote_credentials_cb(
git_cred **cred,
const char *url,
const char *username_from_url,
unsigned int allowed_types,
void *payload)
{
struct rugged_remote_cb_payload *remote_payload = payload;
struct extract_cred_payload cred_payload;

cred_payload.cred = cred;
cred_payload.rb_cred = remote_payload->credentials;
cred_payload.allowed_types = allowed_types;

rb_protect(rugged__extract_cred, (VALUE)&cred_payload, &remote_payload->exception);

return remote_payload->exception ? GIT_ERROR : GIT_OK;
}

static void parse_clone_options(git_clone_options *ret, VALUE rb_options_hash, struct rugged_remote_cb_payload *remote_payload)
{
if (!NIL_P(rb_options_hash)) {
VALUE val;

val = rb_hash_aref(rb_options_hash, CSTR2SYM("bare"));
if (RTEST(val))
ret->bare = 1;

val = rb_hash_aref(rb_options_hash, CSTR2SYM("callbacks"));
if (RTEST(val)) {
git_remote_callbacks remote_callbacks = GIT_REMOTE_CALLBACKS_INIT;
VALUE cb;

cb = rb_hash_aref(val, CSTR2SYM("transfer_progress"));
if (RTEST(cb)) {
if (!rb_respond_to(cb, rb_intern("call"))) {
rb_raise(rb_eArgError, "Expected a Proc or an object that responds to call (:transfer_progress).");
}

remote_payload->transfer_progress = cb;
remote_callbacks.transfer_progress = rugged__remote_transfer_progress_cb;
}
git_remote_callbacks remote_callbacks = GIT_REMOTE_CALLBACKS_INIT;
VALUE val;

remote_callbacks.payload = remote_payload;
if (NIL_P(rb_options_hash))
return;

ret->remote_callbacks = remote_callbacks;
val = rb_hash_aref(rb_options_hash, CSTR2SYM("bare"));
if (RTEST(val))
ret->bare = 1;

val = rb_hash_aref(rb_options_hash, CSTR2SYM("credentials"));
if (RTEST(val)) {
if (rb_obj_is_kind_of(val, rb_cRuggedCredPlaintext) ||
rb_obj_is_kind_of(val, rb_cRuggedCredSshKey) ||
rb_obj_is_kind_of(val, rb_cRuggedCredDefault))
{
remote_callbacks.credentials = rugged__default_remote_credentials_cb;
remote_payload->credentials = val;
} else if (rb_respond_to(val, rb_intern("call"))) {
remote_callbacks.credentials = rugged__remote_credentials_cb;
remote_payload->credentials = val;
} else {
rb_raise(rb_eArgError,
"Expected a Rugged::Credentials type, a Proc or an object that responds to call (:credentials).");
}
}

val = rb_hash_aref(rb_options_hash, CSTR2SYM("callbacks"));
if (RTEST(val)) {
VALUE cb;

cb = rb_hash_aref(val, CSTR2SYM("transfer_progress"));
if (RTEST(cb)) {
if (!rb_respond_to(cb, rb_intern("call"))) {
rb_raise(rb_eArgError, "Expected a Proc or an object that responds to call (:transfer_progress).");
}

remote_payload->transfer_progress = cb;
remote_callbacks.transfer_progress = rugged__remote_transfer_progress_cb;
}
}

remote_callbacks.payload = remote_payload;
ret->remote_callbacks = remote_callbacks;
}

/*
Expand All @@ -378,6 +517,12 @@ static void parse_clone_options(git_clone_options *ret, VALUE rb_options_hash, s
* :ignore_cert_errors ::
* If set to +true+, errors while validating the remote's host certificate will be ignored.
*
* :credentials ::
* The credentials to use for the clone operation. Can be either an instance of one
* of the Rugged::Credentials types, or a proc returning one of the former.
* The proc will be called with the +url+, the +username+ from the url (if applicable) and
* a list of applicable credential types.
*
* :callbacks ::
* A Hash containing name-value pairs that define different callbacks to run during
* the clone operation. Possible callbacks are:
Expand All @@ -388,9 +533,6 @@ static void parse_clone_options(git_clone_options *ret, VALUE rb_options_hash, s
* :completion ::
* Not yet implemented.
*
* :credentials ::
* Not yet implemented.
*
* :transfer_progress ::
* A callback that will be executed to report clone progress information. It will be passed
* the amount of +total_objects+, +indexed_objects+, +received_objects+ and +received_bytes+.
Expand Down
1 change: 1 addition & 0 deletions lib/rugged.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
require 'rugged/diff'
require 'rugged/patch'
require 'rugged/remote'
require 'rugged/credentials'
22 changes: 22 additions & 0 deletions lib/rugged/credentials.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module Rugged
module Credentials
# A plain-text username and password credential object.
class Plaintext
def initialize(options)
@username, @password = options[:username], options[:password]
end
end

# A ssh key credential object that can optionally be passphrase-protected
class SshKey
def initialize(options)
@username, @publickey, @privatekey, @passphrase = options[:username], options[:publickey], options[:privatekey], options[:passphrase]
end
end

# A "default" credential usable for Negotiate mechanisms like NTLM or
# Kerberos authentication
class Default
end
end
end
23 changes: 23 additions & 0 deletions script/cibuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/sh

# Create a test repo which we can use for the online::push tests
mkdir $HOME/_temp
git init --bare $HOME/_temp/test.git
git daemon --listen=localhost --export-all --enable=receive-pack --base-path=$HOME/_temp $HOME/_temp 2>/dev/null &

sudo start ssh

ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
ssh-keyscan -t rsa localhost >>~/.ssh/known_hosts

export GITTEST_REMOTE_GIT_URL="git://localhost/test.git"
export GITTEST_REMOTE_SSH_URL="ssh://localhost/$HOME/_temp/test.git"
export GITTEST_REMOTE_SSH_USER=$USER
export GITTEST_REMOTE_SSH_KEY="$HOME/.ssh/id_rsa"
export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub"
export GITTEST_REMOTE_SSH_PASSPHRASE=""

# We need the config so the tests don't fail
git config --global user.name 'The rugged tests are fragile'
bundle exec rake || exit $?
Loading