mirror of
https://github.com/ben-grande/qusal.git
synced 2025-02-25 09:11:18 -05:00
refactor: git Qrexec helper with drop-in commands
Drop-in scripts can complement the remote-helper ability. Basic trace of the communication of git with the helper.
This commit is contained in:
parent
a27493c5d9
commit
a820751ba3
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
|
case "${GIT_TRACE_HELPER:-}" in
|
||||||
|
true|1) set -x;;
|
||||||
|
esac
|
||||||
|
|
||||||
usage(){
|
usage(){
|
||||||
echo "Usage: ${helper} [<qube>] [<repository>]"
|
echo "Usage: ${helper} [<qube>] [<repository>]"
|
||||||
echo "Note: qube defaults to '@default' and repository to the current repository"
|
echo "Note: qube defaults to '@default' and repository to the current repository"
|
||||||
|
@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
## Portable git-remote-helper.
|
## Portable git-remote-helper.
|
||||||
## Rename this helper to git-remote-<scheme>.
|
## Rename this helper to git-remote-<scheme>.
|
||||||
## Valid URL format: <scheme>://<authority>/<path>.
|
## URL: <scheme>://<authority>/<path>[?query=value][&another_query=value]
|
||||||
## Supported commands: capabilities, connect.
|
|
||||||
## Capabilities commands are sent to git-remote-<scheme>-<capability>.
|
## Capabilities commands are sent to git-remote-<scheme>-<capability>.
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
usage(){
|
usage(){
|
||||||
echo "Usage: ${helper} <remote> [${scheme}://<authority>/<path>]" >&2
|
echo "Usage: ${helper} <remote> [${scheme}://<authority>/<path>[?query=value][&other_query=value]]" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
die(){
|
die(){
|
||||||
@ -21,6 +21,12 @@ die(){
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log(){
|
||||||
|
case "${GIT_TRACE_REMOTE_HELPER:-}" in
|
||||||
|
true|1) echo "${@}" >&2
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
## Validate URL and return it without the scheme.
|
## Validate URL and return it without the scheme.
|
||||||
validate_url(){
|
validate_url(){
|
||||||
url_valid=""
|
url_valid=""
|
||||||
@ -31,30 +37,79 @@ validate_url(){
|
|||||||
## Checks if Authority and Path exist, but not if they are valid, this is
|
## Checks if Authority and Path exist, but not if they are valid, this is
|
||||||
## implementation specific and should be checked by the connect helper.
|
## implementation specific and should be checked by the connect helper.
|
||||||
case "${url_check}" in
|
case "${url_check}" in
|
||||||
"${scheme}"://*?/*?) url_valid="$(echo "${url_check}" | sed "s|.*://||")";;
|
"${scheme}"://*?/*?) url_valid="${url_check}";;
|
||||||
"${scheme}"://*?) die "URL has no path to resource: '${url_check}'";;
|
"${scheme}"://*?) die "URL has no path to resource: '${url_check}'";;
|
||||||
"${scheme}"://) die "URL has no authority: '${url_check}'";;
|
"${scheme}"://) die "URL has no authority: '${url_check}'";;
|
||||||
*?://*) die "URL has unsupported scheme: '${scheme_user_url}'";;
|
*?://*) die "URL has unsupported scheme: '${scheme_user_url}'";;
|
||||||
*) die "URL has no scheme: '${url_check}'";;
|
*) die "URL has no scheme: '${url_check}'";;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
urn_pattern="[0-9A-Za-z@:_.-]+/[0-9A-Za-z_.-]+(\?[0-9A-Za-z=&_-]*)?"
|
||||||
|
if ! (echo "${url_valid}" | grep -qE "^${scheme}://${urn_pattern}$")
|
||||||
|
then
|
||||||
|
die "URL contains forbidden characters"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "${url_valid}"
|
echo "${url_valid}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_urn(){
|
||||||
|
echo "${1#*://}"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_authority(){
|
||||||
|
echo "${1%%/*}"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_path(){
|
||||||
|
echo "${1##*/}" | cut -d "?" -f1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_query(){
|
||||||
|
query=""
|
||||||
|
## If URL contains '?', set query.
|
||||||
|
if test "${1}" != "${1##*\?}"; then
|
||||||
|
query="${1##*\?}"
|
||||||
|
fi
|
||||||
|
echo "${query}"
|
||||||
|
}
|
||||||
|
|
||||||
|
## Find remote-<scheme>-<command>.
|
||||||
|
find_capabilities(){
|
||||||
|
cap_helpers=""
|
||||||
|
for f in "${exec_path}/${script}"-*; do
|
||||||
|
test -f "${f}" || continue
|
||||||
|
test -x "${f}" || continue
|
||||||
|
if test -z "${cap_helpers}"; then
|
||||||
|
cap_helpers="${f##*"${script}-"}"
|
||||||
|
else
|
||||||
|
cap_helpers="${cap_helpers}\n${f##*"${script}-"}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "${cap_helpers}"
|
||||||
|
}
|
||||||
|
|
||||||
## Send capabilities to remote helper specific for that capability.
|
## Send capabilities to remote helper specific for that capability.
|
||||||
send_cap(){
|
send_cap(){
|
||||||
exec_path="$(git --exec-path)"
|
|
||||||
test -n "${exec_path}" || die "Couldn't locate Git's executables path"
|
|
||||||
|
|
||||||
cap="${1}"
|
cap="${1}"
|
||||||
shift
|
shift
|
||||||
cap_file="${script}-${cap}"
|
|
||||||
cap_path="${exec_path}/${cap_file}"
|
|
||||||
|
|
||||||
test -e "${cap_path}" || die "Git's exec path missing: '${cap_file}'"
|
if ! (echo "${capabilities}" | grep -q "^${cap}$"); then
|
||||||
test -x "${cap_path}" || die "Git script is not executable: '${cap_file}'"
|
die "Unsupported capability: '${cap}'"
|
||||||
|
fi
|
||||||
|
|
||||||
"${cap_path}" "${@}"
|
cap_helper="${helper}-${cap}"
|
||||||
|
#cap_file="${script}-${cap}"
|
||||||
|
#cap_path="${exec_path}/${cap_file}"
|
||||||
|
|
||||||
|
## Call capability helper.
|
||||||
|
remote="${remote}" pushurl="${pushurl}" url="${url}" \
|
||||||
|
authority="${authority}" path="${path}" query="${query}" \
|
||||||
|
pushauthority="${pushauthority}" pushpath="${pushpath}" \
|
||||||
|
pushquery="${pushquery}" \
|
||||||
|
git "${cap_helper}" "${@}"
|
||||||
|
#"${cap_path}" "${@}"
|
||||||
}
|
}
|
||||||
|
|
||||||
## Basic requirements.
|
## Basic requirements.
|
||||||
@ -75,7 +130,10 @@ esac
|
|||||||
## Get URL and Push URL (fallback to URL)
|
## Get URL and Push URL (fallback to URL)
|
||||||
case "${2-}" in
|
case "${2-}" in
|
||||||
"")
|
"")
|
||||||
## Happens when 'remote-qrexec' is called directly from the command-line.
|
## Necessary when the remote-helper is called from the command-line.
|
||||||
|
if ! git remote get-url "${remote}" >/dev/null 2>&1; then
|
||||||
|
die "Remote doesn't exist: '${remote}'"
|
||||||
|
fi
|
||||||
url="$(git remote get-url "${remote}" || true)"
|
url="$(git remote get-url "${remote}" || true)"
|
||||||
pushurl="$(git remote get-url --push "${remote}" || true)"
|
pushurl="$(git remote get-url --push "${remote}" || true)"
|
||||||
;;
|
;;
|
||||||
@ -85,23 +143,32 @@ esac
|
|||||||
test -n "${url}" || die "Remote URL is unset"
|
test -n "${url}" || die "Remote URL is unset"
|
||||||
test -n "${pushurl}" || die "Remote Push URL is unset"
|
test -n "${pushurl}" || die "Remote Push URL is unset"
|
||||||
|
|
||||||
url="$(validate_url "${url}")"
|
|
||||||
pushurl="$(validate_url "${pushurl}")"
|
pushurl="$(validate_url "${pushurl}")"
|
||||||
|
pushurn="$(get_urn "${pushurl}")"
|
||||||
|
pushauthority="$(get_authority "${pushurn}")"
|
||||||
|
pushpath="$(get_path "${pushurn}")"
|
||||||
|
pushquery="$(get_query "${pushurn}")"
|
||||||
|
|
||||||
|
url="$(validate_url "${url}")"
|
||||||
|
urn="$(get_urn "${url}")"
|
||||||
|
authority="$(get_authority "${urn}")"
|
||||||
|
path="$(get_path "${urn}")"
|
||||||
|
query="$(get_query "${urn}")"
|
||||||
|
|
||||||
|
exec_path="$(git --exec-path)"
|
||||||
|
test -n "${exec_path}" || die "Git's executables path not found"
|
||||||
|
|
||||||
|
capabilities="$(find_capabilities)"
|
||||||
|
|
||||||
## Communicate with the git-remote-helpers protocol.
|
## Communicate with the git-remote-helpers protocol.
|
||||||
while read -r cmd arg; do
|
while read -r cmd arg; do
|
||||||
|
log "<- $cmd $arg"
|
||||||
case "${cmd}" in
|
case "${cmd}" in
|
||||||
"") exit 0;;
|
capabilities)
|
||||||
"capabilities") printf "connect\n\n";;
|
for c in ${capabilities}; do log "-> ${c}"; done; log "->"
|
||||||
"connect")
|
printf %s"${capabilities}\n\n";;
|
||||||
printf "\n";
|
*) send_cap "${cmd}" "${arg}";;
|
||||||
case "${arg}" in
|
|
||||||
git-upload-pack) send_cap "${cmd}" "${arg}" "${url}";;
|
|
||||||
git-receive-pack) send_cap "${cmd}" "${arg}" "${pushurl}";;
|
|
||||||
"") die "Argument can't be empty";;
|
|
||||||
*) die "Unsupported argument: '${arg}'";;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
*) die "Unsupported command: '${cmd}'";;
|
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
log "<-"
|
||||||
|
@ -4,11 +4,10 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
## Should be called by git-remote-qrexec.
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
usage(){
|
usage(){
|
||||||
echo "Usage: ${helper} git-upload-pack|git-receive-pack <qube>/<path>"
|
echo "Usage: ${helper} (git-upload-pack|git-receive-pack)"
|
||||||
echo "Note: ${helper} is supposed to be called by ${parent_helper}"
|
echo "Note: ${helper} is supposed to be called by ${parent_helper}"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
@ -18,45 +17,69 @@ die(){
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log(){
|
||||||
|
case "${GIT_TRACE_REMOTE_HELPER:-}" in
|
||||||
|
true|1) echo "${@}" >&2
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
## Establish capability working.
|
||||||
|
log "->"
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
helper="${0##*/git-}"
|
helper="${0##*/git-}"
|
||||||
parent_helper="${helper%-*}"
|
parent_helper="${helper%-*}"
|
||||||
|
scheme="${helper##*remote-}"; scheme="${scheme%%-*}"
|
||||||
|
|
||||||
case "${1-}" in
|
case "${1-}" in
|
||||||
-h|--?help|"") usage;;
|
-h|--?help|"") usage;;
|
||||||
*) arg="${1}";;
|
*) arg="${1}";;
|
||||||
esac
|
esac
|
||||||
case "${2-}" in
|
|
||||||
"") usage;;
|
test -n "${remote-}" || die "Remote is unset"
|
||||||
*) url="${2}";;
|
test -n "${url-}" || die "URL is unset"
|
||||||
esac
|
test -n "${authority-}" || die "URL Authority is unset"
|
||||||
|
test -n "${path-}" || die "URL Path is unset"
|
||||||
|
: "${query-}"
|
||||||
|
test -n "${pushurl-}" || die "PushURL is unset"
|
||||||
|
test -n "${pushauthority-}" || die "PushURL Authority is unset"
|
||||||
|
test -n "${pushpath-}" || die "PushURL Path is unset"
|
||||||
|
: "${pushquery-}"
|
||||||
|
|
||||||
case "${arg}" in
|
case "${arg}" in
|
||||||
git-upload-pack) rpc=GitFetch;;
|
git-upload-pack) rpc=GitFetch;;
|
||||||
git-receive-pack) rpc=GitPush;;
|
git-receive-pack) rpc=GitPush; url="${pushurl}";
|
||||||
"") die "Argument can't be empty";;
|
authority="${pushauthority}"; path="${pushpath}"; query="${pushquery}";;
|
||||||
*) die "Unsupported argument: '${arg}'";;
|
"") die "${helper} requires an argument";;
|
||||||
|
*) die "${helper} called with unsupported argument: '${arg}'";;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
qube="$(echo "${url}" | cut -d "/" -f1)"
|
test -n "${path}" || die "Repository name can't be empty"
|
||||||
repo="$(echo "${url}" | cut -d "/" -f2-)"
|
|
||||||
test -n "${repo}" || die "Repository name can't be empty"
|
|
||||||
vendor="qusal"
|
vendor="qusal"
|
||||||
default_qube="sys-git"
|
default_qube="sys-git"
|
||||||
rpc_cmd="${vendor}.${rpc}+${repo}"
|
rpc_cmd="${vendor}.${rpc}+${path}"
|
||||||
|
|
||||||
|
if echo "${query}" | grep -qE "(^|&)verify_signatures=(1|[tT]rue|yes|on)($|&)"
|
||||||
|
then
|
||||||
|
die "Remote helper does not support signature verification yet"
|
||||||
|
fi
|
||||||
|
|
||||||
if command -v qrexec-client-vm >/dev/null; then
|
if command -v qrexec-client-vm >/dev/null; then
|
||||||
exec qrexec-client-vm -- "${qube}" "${rpc_cmd}"
|
log "->" qrexec-client-vm -- "${authority}" "${rpc_cmd}"
|
||||||
|
exec qrexec-client-vm -- "${authority}" "${rpc_cmd}"
|
||||||
elif command -v qrexec-client >/dev/null; then
|
elif command -v qrexec-client >/dev/null; then
|
||||||
qubes_version="$(awk -F '=' '/^VERSION_ID=/{print $2}' /etc/os-release)"
|
qubes_version="$(awk -F '=' '/^VERSION_ID=/{print $2}' /etc/os-release)"
|
||||||
if test "$(echo "${qubes_version}" | tr -d ".")" -le 41; then
|
if test "$(echo "${qubes_version}" | tr -d ".")" -le 41; then
|
||||||
if test "${qube}" = "@default"; then
|
if test "${authority}" = "@default"; then
|
||||||
qube="${default_qube}"
|
authority="${default_qube}"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
policy="$(qrexec-policy --assume-yes-for-ask dom0 "${qube}" "${rpc_cmd}")"
|
policy="$(qrexec-policy --assume-yes-for-ask dom0 "${authority}" "${rpc_cmd}")"
|
||||||
qube="$(echo "${policy}" | awk -F '=' '/^target=/{print $2}')"
|
authority="$(echo "${policy}" | awk -F '=' '/^target=/{print $2}')"
|
||||||
fi
|
fi
|
||||||
exec qrexec-client -d "${qube}" -- "DEFAULT:QUBESRPC ${rpc_cmd} dom0"
|
log "->" qrexec-client -d "${authority}" -- "DEFAULT:QUBESRPC ${rpc_cmd} dom0"
|
||||||
else
|
exec qrexec-client -d "${authority}" -- "DEFAULT:QUBESRPC ${rpc_cmd} dom0"
|
||||||
|
fi
|
||||||
|
|
||||||
die "Qrexec programs not found: qrexec-client-vm, qrexec-client"
|
die "Qrexec programs not found: qrexec-client-vm, qrexec-client"
|
||||||
fi
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user