/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
stdlib/LibGit2/src/gitcredential.jl
307 строк
10 KB
Jameson Nash
docs: fix grammar and clarity in comments and docstrings (#62083)
17 июн 2026, 05:57
Не верифицирован
17 июн 2026, 05:57
27858bb
Код
Авторство
О чём код?
# This file is a part of Julia. License is MIT: https://julialang.org/license const GIT_CRED_ATTRIBUTES = ("protocol", "host", "path", "username", "password", "url") """ GitCredential Git credential information used in communication with git credential helpers. The fields are named using the [input/output key specification](https://git-scm.com/docs/git-credential#IOFMT). """ mutable struct GitCredential protocol::Union{String, Nothing} host::Union{String, Nothing} path::Union{String, Nothing} username::Union{String, Nothing} password::Union{Base.SecretBuffer, Nothing} use_http_path::Bool function GitCredential( protocol::Union{AbstractString, Nothing}=nothing, host::Union{AbstractString, Nothing}=nothing, path::Union{AbstractString, Nothing}=nothing, username::Union{AbstractString, Nothing}=nothing, password::Union{AbstractString, Nothing}=nothing) new(protocol, host, path, username, password, true) end end function GitCredential(cfg::GitConfig, url::AbstractString) fill!(cfg, parse(GitCredential, url)) end function GitCredential(user_pass_cred::UserPasswordCredential, url::AbstractString) cred = parse(GitCredential, url) cred.username = user_pass_cred.user cred.password = deepcopy(user_pass_cred.pass) return cred end Base.:(==)(c1::GitCredential, c2::GitCredential) = (c1.protocol, c1.host, c1.path, c1.username, c1.password, c1.use_http_path) == (c2.protocol, c2.host, c2.path, c2.username, c2.password, c2.use_http_path) Base.hash(cred::GitCredential, h::UInt) = hash(GitCredential, hash((cred.protocol, cred.host, cred.path, cred.username, cred.password, cred.use_http_path), h)) function Base.shred!(cred::GitCredential) cred.protocol = nothing cred.host = nothing cred.path = nothing cred.username = nothing pwd = cred.password pwd !== nothing && Base.shred!(pwd) cred.password = nothing return cred end """ ismatch(url, git_cred)::Bool Checks if the `git_cred` is valid for the given `url`. """ function ismatch(url::AbstractString, git_cred::GitCredential) isempty(url) && return true m = match(URL_REGEX, url) m === nothing && error("Unable to parse URL") # Note: missing URL groups match anything (m[:scheme] === nothing ? true : m[:scheme] == git_cred.protocol) && (m[:host] === nothing ? true : m[:host] == git_cred.host) && (m[:path] === nothing ? true : m[:path] == git_cred.path) && (m[:user] === nothing ? true : m[:user] == git_cred.username) end function isfilled(cred::GitCredential) cred.username !== nothing && cred.password !== nothing end function Base.parse(::Type{GitCredential}, url::AbstractString) m = match(URL_REGEX, url) m === nothing && error("Unable to parse URL") return GitCredential( m[:scheme], m[:host], m[:path], m[:user], m[:password], ) end function Base.copy!(a::GitCredential, b::GitCredential) Base.shred!(a) a.protocol = b.protocol a.host = b.host a.path = b.path a.username = b.username a.password = b.password === nothing ? nothing : copy(b.password) return a end function Base.write(io::IO, cred::GitCredential) cred.protocol !== nothing && write(io, "protocol=", cred.protocol, '\n') cred.host !== nothing && write(io, "host=", cred.host, '\n') cred.path !== nothing && cred.use_http_path && write(io, "path=", cred.path, '\n') cred.username !== nothing && write(io, "username=", cred.username, '\n') cred.password !== nothing && write(io, "password=", cred.password, '\n') nothing end function Base.read!(io::IO, cred::GitCredential) # https://git-scm.com/docs/git-credential#IOFMT while !(eof(io)::Bool) key::AbstractString = readuntil(io, '=') if key == "password" value = Base.SecretBuffer() while !(eof(io)::Bool) && (c = read(io, UInt8)) != UInt8('\n') write(value, c) end seekstart(value) else value = readuntil(io, '\n') end if key == "url" # Any components which are missing from the URL will be set to empty # https://git-scm.com/docs/git-credential#git-credential-codeurlcode Base.shred!(parse(GitCredential, value::AbstractString)) do urlcred copy!(cred, urlcred) end elseif key in GIT_CRED_ATTRIBUTES field = getproperty(cred, Symbol(key)) field !== nothing && Symbol(key) === :password && Base.shred!(field) setproperty!(cred, Symbol(key), value) elseif !all(isspace, key) @warn "Unknown git credential attribute found: $(repr(key))" end end return cred end function fill!(cfg::GitConfig, cred::GitCredential) cred.use_http_path = use_http_path(cfg, cred) # When the username is missing default to using the username set in the configuration if cred.username === nothing cred.username = default_username(cfg, cred) end for helper in credential_helpers(cfg, cred) fill!(helper, cred) # "Once Git has acquired both a username and a password, no more helpers will be # tried." – https://git-scm.com/docs/gitcredentials#gitcredentials-helper isfilled(cred) && break end return cred end struct GitCredentialHelper cmd::Cmd end function Base.parse(::Type{GitCredentialHelper}, helper::AbstractString) # The helper string can take on different behaviors depending on the value: # - "Code after `!` evaluated in shell" – https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage # - "If the helper name is not an absolute path, then the string `git credential-` is # prepended." – https://git-scm.com/docs/gitcredentials#gitcredentials-helper if startswith(helper, '!') cmd_str = helper[2:end] elseif isabspath(first(Base.shell_split(helper))) cmd_str = helper else cmd_str = "git credential-$helper" end GitCredentialHelper(`$(Base.shell_split(cmd_str))`) end function Base.:(==)(a::GitCredentialHelper, b::GitCredentialHelper) a.cmd == b.cmd end function run!(helper::GitCredentialHelper, operation::AbstractString, cred::GitCredential) cmd = `$(helper.cmd) $operation` open(cmd, "r+") do p # Provide the helper with the credential information we know write(p, cred) write(p, "\n") t = @async close(p.in) # Process the response from the helper Base.read!(p, cred) wait(t) end return cred end function run(helper::GitCredentialHelper, operation::AbstractString, cred::GitCredential) run!(helper, operation, deepcopy(cred)) end # The available actions between using `git credential` and helpers are slightly different. # We will directly interact with the helpers as that way we can request credential # information without a prompt (helper `get` vs. git credential `fill`). # https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage fill!(helper::GitCredentialHelper, cred::GitCredential) = run!(helper, "get", cred) approve(helper::GitCredentialHelper, cred::GitCredential) = run(helper, "store", cred) reject(helper::GitCredentialHelper, cred::GitCredential) = run(helper, "erase", cred) """ credential_helpers(config, git_cred)::Vector{GitCredentialHelper} Return all of the `GitCredentialHelper`s found within the provided `config` which are valid for the specified `git_cred`. """ function credential_helpers(cfg::GitConfig, cred::GitCredential) helpers = GitCredentialHelper[] # https://git-scm.com/docs/gitcredentials#gitcredentials-helper for entry in GitConfigIter(cfg, r"credential.*\.helper$") section, url, name, value = split_cfg_entry(entry) @assert name == "helper" # Only use configuration settings where the URL applies to the git credential ismatch(url, cred) || continue # An empty credential.helper resets the list to empty if isempty(value) empty!(helpers) else Base.push!(helpers, parse(GitCredentialHelper, value)) end end return helpers end """ default_username(config, git_cred)::Union{String, Nothing} Return the default username, if any, provided by the `config` which is valid for the specified `git_cred`. """ function default_username(cfg::GitConfig, cred::GitCredential) # https://git-scm.com/docs/gitcredentials#gitcredentials-username for entry in GitConfigIter(cfg, r"credential.*\.username") section, url, name, value = split_cfg_entry(entry) @assert name == "username" # Only use configuration settings where the URL applies to the git credential ismatch(url, cred) || continue return value end return nothing end function use_http_path(cfg::GitConfig, cred::GitCredential) seen_specific = false use_path = false # Default is to ignore the path # https://git-scm.com/docs/gitcredentials#gitcredentials-useHttpPath # # Note: Ideally the regular expression should use "useHttpPath" # https://github.com/libgit2/libgit2/issues/4390 for entry in GitConfigIter(cfg, r"credential.*\.usehttppath") section, url, name, value = split_cfg_entry(entry) # Ignore global configuration if we have already encountered more specific entry if ismatch(url, cred) && (!isempty(url) || !seen_specific) seen_specific = !isempty(url) use_path = value == "true" end end return use_path end approve(cfg::GitConfig, cred::AbstractCredential, url::AbstractString) = nothing reject(cfg::GitConfig, cred::AbstractCredential, url::AbstractString) = nothing function approve(cfg::GitConfig, cred::UserPasswordCredential, url::AbstractString) git_cred = GitCredential(cred, url) git_cred.use_http_path = use_http_path(cfg, git_cred) for helper in credential_helpers(cfg, git_cred) approve(helper, git_cred) end Base.shred!(git_cred) nothing end function reject(cfg::GitConfig, cred::UserPasswordCredential, url::AbstractString) git_cred = GitCredential(cred, url) git_cred.use_http_path = use_http_path(cfg, git_cred) for helper in credential_helpers(cfg, git_cred) reject(helper, git_cred) end Base.shred!(git_cred) nothing end