Skip to content
Permalink
master
Switch branches/tags
Go to file
1 contributor

Users who have contributed to this file

executable file 479 lines (448 sloc) 14.2 KB
#!/bin/bash
# This script talks to a dCache webdav door to obtain a share link
# (using Macaroons, see https://www.dcache.org/manuals/workshop-2017-05-29-Umea/000-Final/anupam_macaroons_v02.pdf )
# Thanks to Paul Millar for providing info and examples.
#
# Latest version available at:
# https://github.com/sara-nl/GridScripts/blob/master/get-macaroon
#
# Old versions available at:
# https://github.com/onnozweers/dcache-scripts/blob/master/get-share-link
#
# Uses a script called view-macaroon, which is available here:
# https://github.com/sara-nl/GridScripts/blob/master/view-macaroon
#
# Changes:
# 2018-06-22 - Onno - Initial version
# 2018-06-22 - Onno - Added --chroot option
# 2018-06-25 - Onno - Fixed X509 authentication; adding curl command to debug output
# 2018-07-03 - Onno - Added output options (macaroon, curl, rclone); added disclaimer
# 2018-07-10 - Onno - Added --ip option
# 2018-09-07 - Onno - Adding deserialized macaroon to debug output
# 2018-09-13 - Onno - Log macaroon properties in ~/macaroons.log, without signature
# 2018-09-14 - Onno - Added maximum upload file size option
# 2018-11-02 - Onno - Load defaults from file
# 2019-01-15 - Raymond Oonk - Fixed reference to view-macaroon
# 2019-03-21 - Onno - Added --output qr; show macaroon properties in color
# 2019-03-22 - Onno - QR option more flexible; fixed executable tests
# 2020-03-07 - Onno - Added --netrc option
# 2020-03-09 - Onno - Check for view-macaroon in same dir as get-macaroon
# 2020-03-31 - Juan Luis Font Calvo - Fix html2text call
usage() {
cat <<-EOF
Obtains a share link from a dCache webdav door using Macaroons.
Usage: $0 [options...]
Options are:
--url <url>
--chroot - Make specified path the root diectory, hiding upper dirs
--proxy - Use proxy specified in X509_USER_PROXY
--user <username> - Username/password authentication
--netrc [filename] - Authenticate with a curl netrc file.
If no filename was provided, use ~/.netrc.
--permissions <list> - Comma separated list of allowed activities
DOWNLOAD,UPLOAD,DELETE,MANAGE,LIST,
READ_METADATA,UPDATE_METADATA
--duration - Duration that the link will be valid, in ISO8601 format
See https://en.wikipedia.org/wiki/ISO_8601#Durations
The duration may be limited by the server.
--ip <subnet-list> - Allow access from these addresses/subnets.
Multiple subnets may be specified, comma separated.
Don't forget IPv6.
--max-file-size - Set a maximum file size for uploads. dCache 5 required.
--output link - Print a link that anyone can open in a browser.
If you open the link in your browser, please do it in
a new private window, to be certain you're authenticated
by the macaroon and not by x509 or username/password.
--output macaroon - Print authentication token only.
--output file <name> - Save macaroon in a file.
--output curl - Print curl command(s) to use the data.
--output rclone <name> - Save an rclone config file, <name>.conf.
You need rclone v1.42-012-gfa051ff9 or newer to use it.
--output qr <name> - Create a QR PNG image based on the share link.
--output qr display - Create a QR PNG image and show it now.
--debug - Show what's going on.
Macaroon properties are logged in ~/macaroons.log.
Their signature is not logged to prevent theft.
Examples:
$0 --url https://my-dcache-server.org/users/homer/disk-shared/ --user homer
$0 --url https://my-dcache-server.org:2882/users/homer/disk-shared/ --proxy --duration P14D
$0 --url https://my-dcache-server.org:2882/users/homer/disk-shared/ --proxy --chroot --output rclone homers-share
$0 --url https://my-dcache-server.org/users/homer/disk-shared/ --user homer --ip 145.100.0.0/16,2001:610:108::/48
Warning:
A macaroon is token that authorizes anyone who gets it. Send it through private channels (not email).
Add sufficient caveats to limit the risk of accidents or abuse.
EOF
exit 1
}
script_dir=$(dirname "$0")
url=
path_or_root='path'
activity=DOWNLOAD,LIST
auth_method=
user=
duration=PT12H # Default: 12 hours
ip=
output='link'
debug=false
max_file_size=
# Load defaults from configuration file if exists
for configfile in /etc/get-macaroon.conf ~/.get-macaroon.conf ; do
if [ -f "$configfile" ] ; then
echo "Loading $configfile"
source "$configfile"
fi
done
while [ $# -gt 0 ] ; do
case "$1" in
--url )
url="$2"
shift 2
;;
--chroot )
path_or_root="root"
shift
;;
--user )
user="$2"
auth_method=user
shift ; shift
;;
--netrc )
auth_method=netrc
case $2 in
--* | '' )
# Next argument is another option or absent; not a file name
netrcfile=~/.netrc
;;
* )
# This must be a file name
netrcfile="$2"
shift
;;
esac
shift
;;
--proxy )
auth_method=proxy
shift
;;
--permissions )
activity="$2"
shift 2
;;
--duration )
duration="$2"
shift 2
;;
--ip )
ip="$2"
shift 2
;;
--max-file-size )
max_file_size="$2"
shift 2
;;
--output )
output="$2"
shift
if [ "$output" == "file" ] ; then
filename="$2"
shift
elif [ "$output" == "rclone" ] ; then
remote_name="$2"
shift
elif [ "$output" == "qr" ] ; then
png_filename="$2"
shift
fi
shift
;;
--debug )
debug=true
shift 1
;;
*)
usage
;;
esac
done
#
# Input checking.
#
if [ -z "$url" ] ; then
usage
fi
case $auth_method in
netrc )
if [ ! -f "$netrcfile" ] ; then
echo "ERROR: could not open netrc file '$netrcfile'."
exit 1
fi
authn="--netrc-file $netrcfile"
;;
proxy )
if [ ! -f "$X509_USER_PROXY" ] ; then
echo "ERROR: could not open proxy '$X509_USER_PROXY'."
exit 1
fi
if [ ! -d /etc/grid-security/certificates ] ; then
echo "ERROR: could not find /etc/grid-security/certificates/." \
"Please install the Grid root certificates if you want to use your proxy."
exit 1
fi
# Check if the proxy is still valid; if not, exit after the error message.
if [ -x "$(command -v voms-proxy-info)" ]; then
voms-proxy-info --exists 1>&2 || exit 1
fi
authn="--capath /etc/grid-security/certificates/ --cert $X509_USER_PROXY --cacert $X509_USER_PROXY"
;;
user )
authn="-u $user"
;;
token )
# We can't specify the token as a command line argument,
# because others could read that with the ps command.
# So we have to put the authorization header in a temporary file.
# The mktemp command differs on OSX.
curl_authorization_header_file=$(mktemp authorization_header_XXXXXXXXXXXX)
chmod 600 "$curl_authorization_header_file"
# File should be cleaned up when we're done,
# unless we're debugging
if $debug ; then
trap "{
echo
echo 'WARNING: in debug mode, the authorization header file' \
'$curl_authorization_header_file will not be cleaned up.' \
'Please clean it up yourself.'
}" EXIT
else
trap 'rm -f "$curl_authorization_header_file"' EXIT
fi
# Save the header in the file
echo "header \"Authorization: Bearer $token\"" > "$curl_authorization_header_file"
# Refer to the file with the header
authn="--config $curl_authorization_header_file"
;;
* )
echo "ERROR: you have to specify a valid authentication method."
exit 1
;;
esac
server=$(echo "$url" | egrep -o 'https://[^/]+/')
dir=$(echo "$url" | sed -e 's#https://[^/]*##')
if [ -z "$dir" ] ; then
dir=/
fi
if [ -z "$server" ] ; then
echo "Please include the server in '--url'." 1>&2
exit 1
fi
# Checking the duration; should be ISO8601 format.
# The grep in OSX does not support --perl-regex, so we give it a slightly simpler regex.
case $OSTYPE in
darwin* )
if ! echo "$duration" | grep -E --silent '^P(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$' ; then
echo "Duration should be in ISO8601 duration format. Examples: PT5M (5 minutes), P1Y2M (1 year and 2 months)" 1>&2
exit 1
fi
;;
* )
if ! echo "$duration" \
| grep --silent --perl-regex '^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d+[HMS])(\d+H)?(\d+M)?(\d+S)?)?$'
then
echo "Duration should be in ISO8601 duration format. Examples: PT5M (5 minutes), P1Y2M (1 year and 2 months)" 1>&2
exit 1
fi
;;
esac
case $output in
link|macaroon|curl )
# output format OK
;;
file )
if [ -z "$filename" ] ; then
echo "If output is file, please specify a file name." 1>&2
exit 1
fi
;;
rclone )
if [ ! -x "$(command -v rclone)" ]; then
echo 'rclone not found in $PATH.' 1>&2
exit 1
fi
if [ -z "$remote_name" ] ; then
echo "If output is rclone, please specify a name for the remote." 1>&2
exit 1
fi
;;
qr )
if [ ! -x "$(command -v qrencode)" ] ; then
echo 'Need `qrencode` for this option.' 1>&2
exit 1
fi
if [ -z "$png_filename" ] ; then
echo "If output is qr, please specify a name for the PNG file." 1>&2
exit 1
fi
;;
* )
echo "Unrecognised output format '$output'." 1>&2
exit 1
esac
#
# End of input checking.
#
if [ -n "$ip" ] ; then
ip_caveat=", \"ip:$ip\""
else
ip_caveat=
fi
if [ -n "$max_file_size" ] ; then
max_size_caveat=", \"max-upload:$max_file_size\""
else
max_size_caveat=
fi
read -r -d '' json_request <<EOF
{
"caveats" : ["$path_or_root:$dir", "activity:$activity" $ip_caveat $max_size_caveat],
"validity" : "$duration"
}
EOF
if $debug ; then
echo "JSON input:"
echo "$json_request"
echo
echo "Curl command:"
echo "curl --silent " \
"$authn " \
"-X POST -H 'Content-Type: application/macaroon-request' " \
"-d \'$json_request\' " \
"$server "
fi
result=$(curl --silent \
$authn \
-X POST -H 'Content-Type: application/macaroon-request' \
-d "$json_request" \
$server )
if ! echo "$result" | grep --silent "targetWithMacaroon" ; then
{
echo "ERROR: could not get share link from $server."
# Show output from server in the nicest way possible
if [ -x "$(command -v html2text)" ]; then
echo "$result" | html2text /dev/stdin
else
echo "$result"
fi
} 1>&2
exit 1
fi
if $debug ; then
echo "JSON output:"
echo "$result"
echo
fi
macaroon=$(echo "$result" | jq -r '.macaroon')
link=$(echo "$result" | jq -r '.uri.targetWithMacaroon')
# Show contents of macaroon
if [ -x "${script_dir}/view-macaroon" ] ; then
macaroon_viewer="${script_dir}/view-macaroon"
else
macaroon_viewer="$(command -v view-macaroon)"
fi
echo "Macaroon viewer: $macaroon_viewer"
if [ -x "$macaroon_viewer" ] ; then
BLUE=$'\e[34m'
RESET=$'\e[39m'
echo -e "$BLUE"
echo "=== View deserialized macaroon ==="
$macaroon_viewer <<<"$macaroon"
echo "=== End deserialized macaroon ==="
echo -e "$RESET"
echo
#
# Log macaroon properties in a (private) file; strip signature to prevent theft
{
echo "=== $(date) ==="
$macaroon_viewer <<<"$macaroon" | grep -v signature
echo -e "=====================================\n"
} >> ~/macaroons.log
chmod 600 ~/macaroons.log
fi
case $output in
link )
# Show link that can be pasted in browser
echo "$link"
;;
macaroon )
# Just the bare token, nothing else
echo "$macaroon"
;;
file )
# Save token in a file
touch "$filename"
chmod 600 "$filename"
echo "$macaroon" > "$filename"
echo "Macaroon saved in file '$filename'."
;;
curl )
# Show download and upload command with curl
if echo "$activity" | grep --silent -e 'DOWNLOAD' -e 'LIST' ; then
echo "Curl download/listing command:"
echo
if [ "$path_or_root" = "root" ] ; then
echo "curl --fail --location $link --output myfile"
else
echo "curl --fail --location $url?authz=$macaroon --output myfile"
fi
echo
fi
if echo "$activity" | grep --silent 'UPLOAD' ; then
echo "Curl upload command:"
echo
if [ "$path_or_root" = "root" ] ; then
echo "curl --header 'Authorization: BEARER $macaroon' --upload-file myfile $server"
else
echo "curl --header 'Authorization: BEARER $macaroon' --upload-file myfile $url"
fi
echo
fi
;;
rclone )
echo "Creating rclone config file $remote_name.conf:"
echo
$debug && echo "rclone --config=$remote_name.conf config create $remote_name webdav url $server vendor other user '' password '' bearer_token $macaroon"
rclone --config=$remote_name.conf config create $remote_name webdav url $server vendor other user '' password '' bearer_token $macaroon
echo
echo "Send this file to the persons you want to share data with."
echo "They need rclone v1.42-012-gfa051ff9 or newer to access the data."
echo "Example command:"
echo "rclone --config=$remote_name.conf ls $remote_name:"
;;
qr )
if [[ $png_filename != *.png ]] ; then
png_filename="$png_filename.png"
fi
# Generate QR image
echo "Creating QR image file: $png_filename"
if qrencode -o $png_filename "$link" ; then
# If the name is "display[.png]", the user wants to see it now!
if [ "$png_filename" = "display.png" ] ; then
# Try to open it in an image viewer
for imageviewer in feh display xdg-open eog Xming ; do
if [ -x "$(command -v $imageviewer)" ] ; then
$imageviewer $png_filename
break
fi
done
else # User does not need to see it now. Show filename.
echo "Created QR image file: $png_filename"
fi
else
echo "ERROR: Could not create QR file."
exit 1
fi
;;
* )
echo "Unrecognised output format '$output'." 1>&2
exit 1
esac