#!/usr/bin/env bash
set -Euo pipefail
shopt -s extglob
shopt -s nocasematch
shopt -s extdebug
trap 'declare rc=$?;
>&2 echo "Unexpected error executing $BASH_COMMAND at ${BASH_SOURCE[0]} line $LINENO";
exit $rc' ERR
exec 9> "${BASH_SOURCE[0]%.sh}.$$.log"
{
printf "Command line: \"%q\"" "$0" ; printf " \"%q\"" "$@" ; printf "\n"
printf "\$-: %s\n" "$-"
printf "BASHOPTS: %s\n" "$BASHOPTS"
printf "SHELLOPTS: %s\n" "$SHELLOPTS"
printf "Start time: %(%Y%m%d %H%M%S %z)T\n" -2
if type -P "git" >/dev/null 2>&1 && [[ -d "${BASH_SOURCE[0]%/*}/.git" ]]
then
printf "Git revision: %s\n" \
"$(git -C "${BASH_SOURCE[0]%/*}" rev-parse --verify HEAD 2>&1)"
fi
printf "Hostname: %s\n" "${HOSTNAME:-$(hostname -f 2>&1)}"
printf "\n" ; env ; printf "\n"
} >&9 2>&9
export BASH_XTRACEFD=9
export PS4='+ $$($BASHPID) +${SECONDS}s (${BASH_SOURCE[0]}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
set -x
log_warn () {
>&9 echo -e "$*"
>&2 echo -e "$*"
}
log_error () {
log_warn "[err]" "$@"
}
log_info () {
>&9 echo -e "$*"
echo -e "$*"
}
curl () {
command curl --header "PRIVATE-TOKEN: $token" -Lk "$@" >&9 2>&9
command curl --header "PRIVATE-TOKEN: $token" -sSLk "$@"
}
api () {
if [[ "$*" =~ [[:space:]]page=[0-9]+ ]] ; then
api_query "$@"
else
api_pages "$@"
fi
}
api_query_url () {
local IFS="&"
echo "${gitlab%%/}/api/v3/$(echo "$*" | sed "s/&/?/")"
}
api_query () {
declare query="$(api_query_url "$@")" rc=0
curl "$query" || rc+=$?
[[ $rc -eq 0 ]] || log_error "api_query to $query failed!"
return $rc
}
api_pages () {
declare query="$(api_query_url "$@")" rc=0
curl -I "$query" | egrep "^X-Total-Pages:" | egrep -o "[0-9]+" || rc+=$?
[[ $rc -eq 0 ]] || log_error "api_pages to $query failed!"
return $rc
}
ssh_keys () {
api "users/$1/keys" page=1 | jq -r '.[].key'
}
groups () {
declare -i pages=1 p=1
pages=$(api groups per_page=100 "$@") || pages=1
for (( p=1; p<=pages; p++ )) ; do
api groups per_page=100 page=$p "$@" | jq -r '.[].id'
done
}
group_members () {
declare -i pages=1 p=1
pages=$(api "groups/$1/members" per_page=100) || pages=5
for (( p=1; p<=pages; p++ )) ; do
api "groups/$1/members" per_page=100 page=$p \
| jq -r '.[] as $u|[$u.id|tostring,$u.username]|join(" ")'
done
}
main () {
declare token="${1:-}"
declare gitlab="${2:-https://my.gitlab.local}"
declare group_name="${3:-my_group}"
if [[ -z "${token:-}" || -z "${gitlab:-}" || -z "${group_name:-}" ]] ; then
log_warn "Syntax: ${0##*/} <access_token> [gitlab_url] [group]"
exit 64
fi
declare group_id=""
group_id=$(groups "search=$group_name")
if [[ ! "$group_id" =~ ^[0-9]+$ ]] ; then
log_error "group_id '$group_id' returned for group name search" \
"'$group_name' is not a single integar as expected; try refining" \
"search terms."
exit 2
fi
log_info "Found group ID $group_id matches group name $group_name."
declare tmpdir="$(mktemp -d)"
trap "rm -Rf '$tmpdir'" EXIT
declare -i total_members=0
declare user_id="" username=""
while read -r user_id username ; do
total_members+=1
log_info "[$total_members] Saving SSH public keys to" \
"${tmpdir%/}/$username for $username (UID $user_id) ..."
ssh_keys "$user_id" > "${tmpdir%/}/$username" || true
done < <(group_members "$group_id")
log_info "Found total of $total_members group members."
log_info "Creating tarball archive $PWD/ssh_keys.tar.gz ..."
tar -C "$(dirname "$tmpdir")" \
-zcf "$PWD/ssh_keys.tar.gz" \
"$(basename "$tmpdir")"
}
main "$@"