Skip to content

Commit

Permalink
Test framework for LibGit2 credential callback
Browse files Browse the repository at this point in the history
  • Loading branch information
omus committed Apr 3, 2017
1 parent d3cfcd4 commit dfa1a83
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 6 deletions.
53 changes: 53 additions & 0 deletions base/libgit2/callbacks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,56 @@ mirror_cb() = cfunction(mirror_callback, Cint, (Ptr{Ptr{Void}}, Ptr{Void}, Cstri
credentials_cb() = cfunction(credentials_callback, Cint, (Ptr{Ptr{Void}}, Cstring, Cstring, Cuint, Ptr{Void}))
"C function pointer for `fetchhead_foreach_callback`"
fetchhead_foreach_cb() = cfunction(fetchhead_foreach_callback, Cint, (Cstring, Cstring, Ptr{GitHash}, Cuint, Ptr{Void}))

"""
Emulates the LibGit2 credential loop to allows testing of the credential_callback function
without having to authenticate against a real server.
"""
function credential_loop(
valid_credential::AbstractCredentials,
url::AbstractString,
user::AbstractString,
allowed_types::UInt32)
cb = credentials_cb()
libgitcred_ptr_ptr = Ref{Ptr{Void}}(C_NULL)
cache = CachedCredentials()
payload_ptr = Ref(Nullable{AbstractCredentials}(cache))

# Number of times credentials were authenticated against. With the real LibGit2
# credential loop this would be how many times we sent credentials to the remote.
num_authentications = 0

# Emulate how LibGit2 uses the credential callback by repeatedly calling the function
# until we find valid credentials or an exception is raised.
err = Cint(0)
while err == 0
err = ccall(cb, Cint, (Ptr{Ptr{Void}}, Cstring, Cstring, Cuint, Ptr{Void}),
libgitcred_ptr_ptr, url, isempty(user) ? C_NULL : user, allowed_types, pointer_from_objref(payload_ptr))
num_authentications += 1

# Check if the callback provided us with valid credentials
if length(cache.cred) == 1 && first(values(cache.cred)) == valid_credential
break
end

if num_authentications > 50
error("Credential callback seems to be caught in an infinite loop")
end
end

return err, num_authentications
end

function credential_loop(
valid_credential::UserPasswordCredentials,
url::AbstractString,
user::AbstractString="")
credential_loop(valid_credential, url, user, 0x000001)
end

function credential_loop(
valid_credential::SSHCredentials,
url::AbstractString,
user::AbstractString="")
credential_loop(valid_credential, url, user, 0x000046)
end
26 changes: 20 additions & 6 deletions base/libgit2/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -670,32 +670,46 @@ function securezero!(cred::UserPasswordCredentials)
return cred
end

function Base.:(==)(a::UserPasswordCredentials, b::UserPasswordCredentials)
a.user == b.user && a.pass == b.pass
end

"SSH credentials type"
mutable struct SSHCredentials <: AbstractCredentials
user::String
pass::String
pubkey::String
prvkey::String
pubkey::String
usesshagent::String # used for ssh-agent authentication
prompt_if_incorrect::Bool # Whether to allow interactive prompting if the credentials are incorrect
count::Int

function SSHCredentials(u::AbstractString,p::AbstractString,prompt_if_incorrect::Bool=false)
c = new(u,p,"","","Y",prompt_if_incorrect,3)
function SSHCredentials(u::AbstractString,p::AbstractString,prvkey::AbstractString,pubkey::AbstractString,prompt_if_incorrect::Bool=false)
c = new(u,p,prvkey,pubkey,"Y",prompt_if_incorrect,3)
finalizer(c, securezero!)
return c
end
SSHCredentials(prompt_if_incorrect::Bool=false) = SSHCredentials("","",prompt_if_incorrect)
SSHCredentials(u::AbstractString,p::AbstractString,prompt_if_incorrect::Bool=false) = SSHCredentials(u,p,prompt_if_incorrect)
SSHCredentials(prompt_if_incorrect::Bool=false) = SSHCredentials("","","","",prompt_if_incorrect)
end

function securezero!(cred::SSHCredentials)
securezero!(cred.user)
securezero!(cred.pass)
securezero!(cred.pubkey)
securezero!(cred.prvkey)
securezero!(cred.pubkey)
cred.count = 0
return cred
end

function Base.:(==)(a::SSHCredentials, b::SSHCredentials)
return (
a.user == b.user &&
a.pass == b.pass &&
a.prvkey == b.prvkey &&
a.pubkey == b.pubkey
)
end

"Credentials that support caching"
mutable struct CachedCredentials <: AbstractCredentials
cred::Dict{String,AbstractCredentials}
Expand Down

0 comments on commit dfa1a83

Please sign in to comment.