Add: julia-0.6.2
Former-commit-id: ccc667cf67d569f3fb3df39aa57c2134755a7551
This commit is contained in:
265
julia-0.6.2/share/julia/base/libgit2/callbacks.jl
Normal file
265
julia-0.6.2/share/julia/base/libgit2/callbacks.jl
Normal file
@@ -0,0 +1,265 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
"""Mirror callback function
|
||||
|
||||
Function sets `+refs/*:refs/*` refspecs and `mirror` flag for remote reference.
|
||||
"""
|
||||
function mirror_callback(remote::Ptr{Ptr{Void}}, repo_ptr::Ptr{Void},
|
||||
name::Cstring, url::Cstring, payload::Ptr{Void})
|
||||
# Create the remote with a mirroring url
|
||||
fetch_spec = "+refs/*:refs/*"
|
||||
err = ccall((:git_remote_create_with_fetchspec, :libgit2), Cint,
|
||||
(Ptr{Ptr{Void}}, Ptr{Void}, Cstring, Cstring, Cstring),
|
||||
remote, repo_ptr, name, url, fetch_spec)
|
||||
err != 0 && return Cint(err)
|
||||
|
||||
# And set the configuration option to true for the push command
|
||||
config = GitConfig(GitRepo(repo_ptr,false))
|
||||
name_str = unsafe_string(name)
|
||||
err= try set!(config, "remote.$name_str.mirror", true)
|
||||
catch -1
|
||||
finally close(config)
|
||||
end
|
||||
err != 0 && return Cint(err)
|
||||
return Cint(0)
|
||||
end
|
||||
|
||||
function authenticate_ssh(creds::SSHCredentials, libgit2credptr::Ptr{Ptr{Void}},
|
||||
username_ptr, schema, host)
|
||||
isusedcreds = checkused!(creds)
|
||||
|
||||
# Note: The same SSHCredentials can be used to authenticate separate requests using the
|
||||
# same credential cache. e.g. using Pkg.update when there are two private packages.
|
||||
errcls, errmsg = Error.last_error()
|
||||
if errcls != Error.None
|
||||
# Check if we used ssh-agent
|
||||
if creds.usesshagent == "U"
|
||||
creds.usesshagent = "E" # reported ssh-agent error, disables ssh agent use for the future
|
||||
end
|
||||
end
|
||||
|
||||
# first try ssh-agent if credentials support its usage
|
||||
if creds.usesshagent == "Y" || creds.usesshagent == "U"
|
||||
err = ccall((:git_cred_ssh_key_from_agent, :libgit2), Cint,
|
||||
(Ptr{Ptr{Void}}, Cstring), libgit2credptr, username_ptr)
|
||||
creds.usesshagent = "U" # used ssh-agent only one time
|
||||
err == 0 && return Cint(0)
|
||||
end
|
||||
|
||||
if creds.prompt_if_incorrect
|
||||
# if username is not provided, then prompt for it
|
||||
username = if username_ptr == Cstring(C_NULL)
|
||||
uname = creds.user # check if credentials were already used
|
||||
!isusedcreds ? uname : prompt("Username for '$schema$host'", default=uname)
|
||||
else
|
||||
unsafe_string(username_ptr)
|
||||
end
|
||||
isempty(username) && return Cint(Error.EAUTH)
|
||||
|
||||
# For SSH we need a private key location
|
||||
privatekey = if haskey(ENV,"SSH_KEY_PATH")
|
||||
ENV["SSH_KEY_PATH"]
|
||||
else
|
||||
keydefpath = creds.prvkey # check if credentials were already used
|
||||
if isempty(keydefpath) || isusedcreds
|
||||
defaultkeydefpath = joinpath(homedir(),".ssh","id_rsa")
|
||||
if isempty(keydefpath) && isfile(defaultkeydefpath)
|
||||
keydefpath = defaultkeydefpath
|
||||
else
|
||||
keydefpath =
|
||||
prompt("Private key location for '$schema$username@$host'", default=keydefpath)
|
||||
end
|
||||
end
|
||||
keydefpath
|
||||
end
|
||||
|
||||
# If the private key changed, invalidate the cached public key
|
||||
(privatekey != creds.prvkey) &&
|
||||
(creds.pubkey = "")
|
||||
|
||||
# For SSH we need a public key location, look for environment vars SSH_* as well
|
||||
publickey = if haskey(ENV,"SSH_PUB_KEY_PATH")
|
||||
ENV["SSH_PUB_KEY_PATH"]
|
||||
else
|
||||
keydefpath = creds.pubkey # check if credentials were already used
|
||||
if isempty(keydefpath) || isusedcreds
|
||||
if isempty(keydefpath)
|
||||
keydefpath = privatekey*".pub"
|
||||
end
|
||||
if !isfile(keydefpath)
|
||||
prompt("Public key location for '$schema$username@$host'", default=keydefpath)
|
||||
end
|
||||
end
|
||||
keydefpath
|
||||
end
|
||||
|
||||
passphrase_required = true
|
||||
if !isfile(privatekey)
|
||||
warn("Private key not found")
|
||||
else
|
||||
# In encrypted private keys, the second line is "Proc-Type: 4,ENCRYPTED"
|
||||
open(privatekey) do f
|
||||
readline(f)
|
||||
passphrase_required = readline(f) == "Proc-Type: 4,ENCRYPTED"
|
||||
end
|
||||
end
|
||||
|
||||
passphrase = if haskey(ENV,"SSH_KEY_PASS")
|
||||
ENV["SSH_KEY_PASS"]
|
||||
else
|
||||
passdef = creds.pass # check if credentials were already used
|
||||
if passphrase_required && (isempty(passdef) || isusedcreds)
|
||||
if is_windows()
|
||||
passdef = Base.winprompt(
|
||||
"Your SSH Key requires a password, please enter it now:",
|
||||
"Passphrase required", privatekey; prompt_username = false)
|
||||
isnull(passdef) && return Cint(Error.EAUTH)
|
||||
passdef = Base.get(passdef)[2]
|
||||
else
|
||||
passdef = prompt("Passphrase for $privatekey", password=true)
|
||||
end
|
||||
end
|
||||
passdef
|
||||
end
|
||||
((creds.user != username) || (creds.pass != passphrase) ||
|
||||
(creds.prvkey != privatekey) || (creds.pubkey != publickey)) && reset!(creds)
|
||||
|
||||
creds.user = username # save credentials
|
||||
creds.prvkey = privatekey # save credentials
|
||||
creds.pubkey = publickey # save credentials
|
||||
creds.pass = passphrase
|
||||
else
|
||||
isusedcreds && return Cint(Error.EAUTH)
|
||||
end
|
||||
|
||||
return ccall((:git_cred_ssh_key_new, :libgit2), Cint,
|
||||
(Ptr{Ptr{Void}}, Cstring, Cstring, Cstring, Cstring),
|
||||
libgit2credptr, creds.user, creds.pubkey, creds.prvkey, creds.pass)
|
||||
end
|
||||
|
||||
function authenticate_userpass(creds::UserPasswordCredentials, libgit2credptr::Ptr{Ptr{Void}},
|
||||
schema, host, urlusername)
|
||||
isusedcreds = checkused!(creds)
|
||||
|
||||
if creds.prompt_if_incorrect
|
||||
username = creds.user
|
||||
userpass = creds.pass
|
||||
if is_windows()
|
||||
if isempty(username) || isempty(userpass) || isusedcreds
|
||||
res = Base.winprompt("Please enter your credentials for '$schema$host'", "Credentials required",
|
||||
isempty(username) ? urlusername : username; prompt_username = true)
|
||||
isnull(res) && return Cint(Error.EAUTH)
|
||||
username, userpass = Base.get(res)
|
||||
end
|
||||
elseif isusedcreds
|
||||
username = prompt("Username for '$schema$host'",
|
||||
default=isempty(username) ? urlusername : username)
|
||||
userpass = prompt("Password for '$schema$username@$host'", password=true)
|
||||
end
|
||||
((creds.user != username) || (creds.pass != userpass)) && reset!(creds)
|
||||
creds.user = username # save credentials
|
||||
creds.pass = userpass # save credentials
|
||||
|
||||
isempty(username) && isempty(userpass) && return Cint(Error.EAUTH)
|
||||
else
|
||||
isusedcreds && return Cint(Error.EAUTH)
|
||||
end
|
||||
|
||||
return ccall((:git_cred_userpass_plaintext_new, :libgit2), Cint,
|
||||
(Ptr{Ptr{Void}}, Cstring, Cstring),
|
||||
libgit2credptr, creds.user, creds.pass)
|
||||
end
|
||||
|
||||
|
||||
"""Credentials callback function
|
||||
|
||||
Function provides different credential acquisition functionality w.r.t. a connection protocol.
|
||||
If a payload is provided then `payload_ptr` should contain a `LibGit2.AbstractCredentials` object.
|
||||
|
||||
For `LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT` type, if the payload contains fields:
|
||||
`user` & `pass`, they are used to create authentication credentials.
|
||||
Empty `user` name and `pass`word trigger an authentication error.
|
||||
|
||||
For `LibGit2.Consts.CREDTYPE_SSH_KEY` type, if the payload contains fields:
|
||||
`user`, `prvkey`, `pubkey` & `pass`, they are used to create authentication credentials.
|
||||
Empty `user` name triggers an authentication error.
|
||||
|
||||
Credentials are checked in the following order (if supported):
|
||||
- ssh key pair (`ssh-agent` if specified in payload's `usesshagent` field)
|
||||
- plain text
|
||||
|
||||
**Note**: Due to the specifics of the `libgit2` authentication procedure, when
|
||||
authentication fails, this function is called again without any indication whether
|
||||
authentication was successful or not. To avoid an infinite loop from repeatedly
|
||||
using the same faulty credentials, the `checkused!` function can be called. This
|
||||
function returns `true` if the credentials were used.
|
||||
Using credentials triggers a user prompt for (re)entering required information.
|
||||
`UserPasswordCredentials` and `CachedCredentials` are implemented using a call
|
||||
counting strategy that prevents repeated usage of faulty credentials.
|
||||
"""
|
||||
function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring,
|
||||
username_ptr::Cstring,
|
||||
allowed_types::Cuint, payload_ptr::Ptr{Void})
|
||||
err = Cint(0)
|
||||
url = unsafe_string(url_ptr)
|
||||
|
||||
# parse url for schema and host
|
||||
urlparts = match(URL_REGEX, url)
|
||||
schema = urlparts[:scheme] === nothing ? "" : urlparts[:scheme] * "://"
|
||||
urlusername = urlparts[:user] === nothing ? "" : urlparts[:user]
|
||||
host = urlparts[:host]
|
||||
|
||||
# get credentials object from payload pointer
|
||||
@assert payload_ptr != C_NULL
|
||||
creds = unsafe_pointer_to_objref(payload_ptr)
|
||||
explicit = !isnull(creds[]) && !isa(Base.get(creds[]), CachedCredentials)
|
||||
# use ssh key or ssh-agent
|
||||
if isset(allowed_types, Cuint(Consts.CREDTYPE_SSH_KEY))
|
||||
sshcreds = get_creds!(creds, "ssh://$host", reset!(SSHCredentials(true), -1))
|
||||
if isa(sshcreds, SSHCredentials)
|
||||
err = authenticate_ssh(sshcreds, libgit2credptr, username_ptr, schema, host)
|
||||
err == 0 && return err
|
||||
end
|
||||
end
|
||||
|
||||
if isset(allowed_types, Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT))
|
||||
defaultcreds = reset!(UserPasswordCredentials(true), -1)
|
||||
credid = "$schema$host"
|
||||
upcreds = get_creds!(creds, credid, defaultcreds)
|
||||
# If there were stored SSH credentials, but we ended up here that must
|
||||
# mean that something went wrong. Replace the SSH credentials by user/pass
|
||||
# credentials
|
||||
if !isa(upcreds, UserPasswordCredentials)
|
||||
upcreds = defaultcreds
|
||||
isa(Base.get(creds[]), CachedCredentials) && (Base.get(creds[]).creds[credid] = upcreds)
|
||||
end
|
||||
return authenticate_userpass(upcreds, libgit2credptr, schema, host, urlusername)
|
||||
end
|
||||
|
||||
# No authentication method we support succeeded. The most likely cause is
|
||||
# that explicit credentials were passed in, but said credentials are incompatible
|
||||
# with the remote host.
|
||||
if err == 0
|
||||
if explicit
|
||||
warn("The explicitly provided credentials were incompatible with " *
|
||||
"the server's supported authentication methods")
|
||||
end
|
||||
err = Cint(Error.EAUTH)
|
||||
end
|
||||
return err
|
||||
end
|
||||
|
||||
function fetchhead_foreach_callback(ref_name::Cstring, remote_url::Cstring,
|
||||
oid_ptr::Ptr{GitHash}, is_merge::Cuint, payload::Ptr{Void})
|
||||
fhead_vec = unsafe_pointer_to_objref(payload)::Vector{FetchHead}
|
||||
push!(fhead_vec, FetchHead(unsafe_string(ref_name), unsafe_string(remote_url),
|
||||
unsafe_load(oid_ptr), is_merge == 1))
|
||||
return Cint(0)
|
||||
end
|
||||
|
||||
"C function pointer for `mirror_callback`"
|
||||
mirror_cb() = cfunction(mirror_callback, Cint, Tuple{Ptr{Ptr{Void}}, Ptr{Void}, Cstring, Cstring, Ptr{Void}})
|
||||
"C function pointer for `credentials_callback`"
|
||||
credentials_cb() = cfunction(credentials_callback, Cint, Tuple{Ptr{Ptr{Void}}, Cstring, Cstring, Cuint, Ptr{Void}})
|
||||
"C function pointer for `fetchhead_foreach_callback`"
|
||||
fetchhead_foreach_cb() = cfunction(fetchhead_foreach_callback, Cint, Tuple{Cstring, Cstring, Ptr{GitHash}, Cuint, Ptr{Void}})
|
||||
Reference in New Issue
Block a user