Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/164 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
		
			
				
	
	
		
			262 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/bin/bash
 | |
| 
 | |
| if [ "$2" == "--show-password" ]; then
 | |
|   HSADMINNG_CAS_SHOW_PASSWORD=yes
 | |
|   shift
 | |
| else
 | |
|   HSADMINNG_CAS_SHOW_PASSWORD=
 | |
| fi
 | |
| 
 | |
| if [ "$1" == "--trace" ]; then
 | |
|   function trace() {
 | |
|     echo "$*" >&2
 | |
|   }
 | |
|   function doCurl() {
 | |
|     set -x
 | |
|     if [ -z "$HSADMINNG_CAS_ASSUME" ]; then
 | |
|       curl --fail-with-body \
 | |
|            --header "Authorization: $HSADMINNG_CAS_TICKET" \
 | |
|            "$@"
 | |
|     else
 | |
|       curl --fail-with-body \
 | |
|            --header "Authorization: $HSADMINNG_CAS_TICKET" \
 | |
|            --header "assumed-roles: $HSADMINNG_CAS_ASSUME" \
 | |
|            "$@"
 | |
|     fi
 | |
|     set +x
 | |
|   }
 | |
|   shift
 | |
| else
 | |
|   function trace() {
 | |
|     : # noop
 | |
|   }
 | |
|   function doCurl() {
 | |
|     curl --fail-with-body --header "Authorization: $HSADMINNG_CAS_TICKET" "$@"
 | |
|   }
 | |
| fi
 | |
| 
 | |
| export HSADMINNG_CAS_ASSUME_HEADER
 | |
| if [ -f ~/.cas-curl-assume ]; then
 | |
|   HSADMINNG_CAS_ASSUME="$(cat ~/.cas-curl-assume)"
 | |
| else
 | |
|   HSADMINNG_CAS_ASSUME=
 | |
| fi
 | |
| 
 | |
| if [ -z "$HSADMINNG_CAS_LOGIN" ] || [ -z "$HSADMINNG_CAS_VALIDATE" ] || \
 | |
|    [ -z "$HSADMINNG_CAS_SERVICE_ID" ]; then
 | |
|   cat >&2 <<EOF
 | |
|   ERROR: environment incomplete
 | |
| 
 | |
|   please set the following environment variables:
 | |
|   export HSADMINNG_CAS_LOGIN=https://login.hostsharing.net/cas/v1/tickets
 | |
|   export HSADMINNG_CAS_VALIDATE=https://login.hostsharing.net/cas/proxyValidate
 | |
|   export HSADMINNG_CAS_USERNAME=<<optionally, your username, or leave empty after '='>>
 | |
|   export HSADMINNG_CAS_PASSWORD=<<optionally, your password, or leave empty after '='>>
 | |
|   export HSADMINNG_CAS_SERVICE_ID=https://hsadminng.hostsharing.net:443/
 | |
| EOF
 | |
|   exit 1
 | |
| fi
 | |
| 
 | |
| function casCurlDocumentation() {
 | |
|   cat <<EOF
 | |
|   curl-wrapper utilizing CAS-authentication for hsadmin-ng
 | |
|   usage: $0 [--trace] [--show-password] <<command>> [parameters]
 | |
| 
 | |
|   commands:
 | |
| EOF
 | |
|   # filters out help texts (containing double-# and following lines with leading single-#) from the commands itself
 | |
|   # (the '' makes sure that this line is not found, just the lines with actual help texts)
 | |
|   sed -n '/#''#/ {x; p; x; s/#''#//; p; :a; n; /^[[:space:]]*#/!b; s/^[[:space:]]*#//; p; ba}' <$0
 | |
| }
 | |
| 
 | |
| function casLogin() {
 | |
|   # ticket granting ticket exists and not expired?
 | |
|   if find ~/.cas-login-tgt -type f -size +0c -mmin -60 2>/dev/null | grep -q .; then
 | |
|     return
 | |
|   fi
 | |
| 
 | |
|   if [ -z "$HSADMINNG_CAS_USERNAME" ]; then
 | |
|     read -e -p "Username: " HSADMINNG_CAS_USERNAME
 | |
|   fi
 | |
| 
 | |
|   if [ -z "$HSADMINNG_CAS_PASSWORD" ]; then
 | |
|     read -s -e -p "Password: " HSADMINNG_CAS_PASSWORD
 | |
|   fi
 | |
| 
 | |
|   if [ "$HSADMINNG_CAS_SHOW_PASSWORD" == "--show-password" ]; then
 | |
|     HSADMINNG_CAS_PASSWORD_DISPLAY=$HSADMINNG_CAS_PASSWORD
 | |
|   else
 | |
|     HSADMINNG_CAS_PASSWORD_DISPLAY="<<password hidden - use --show-password to show>>"
 | |
|   fi
 | |
| 
 | |
|   # Do NOT use doCurl here! We do neither want to print the password nor pass a CAS service ticket.
 | |
|   trace "+ curl --fail-with-body -s -i -X POST \
 | |
|         -H 'Content-Type: application/x-www-form-urlencoded' \
 | |
|         -d \"username=$HSADMINNG_CAS_USERNAME&password=$HSADMINNG_CAS_PASSWORD_DISPLAY\" \
 | |
|         $HSADMINNG_CAS_LOGIN -o ~/.cas-login-tgt.response -D -"
 | |
|   HSADMINNG_CAS_TGT=`curl --fail-with-body -s -i -X POST \
 | |
|         -H 'Content-Type: application/x-www-form-urlencoded' \
 | |
|         -d "username=$HSADMINNG_CAS_USERNAME&password=$HSADMINNG_CAS_PASSWORD" \
 | |
|         $HSADMINNG_CAS_LOGIN -o ~/.cas-login-tgt.response -D - \
 | |
|         | grep -i "^Location: " |  sed -e 's/^Location: //' -e 's/\\r//'`
 | |
|   if [ -z "$HSADMINNG_CAS_TGT" ]; then
 | |
|     echo "ERROR: could not get ticket granting ticket" >&2
 | |
|     cat ~/.cas-login-tgt.response >&2
 | |
|     exit 1
 | |
|   fi
 | |
|   echo "$HSADMINNG_CAS_TGT" >~/.cas-login-tgt
 | |
|   trace "$HSADMINNG_CAS_TGT"
 | |
| }
 | |
| 
 | |
| function casLogout() {
 | |
|   rm -f ~/.cas-login-tgt
 | |
| }
 | |
| 
 | |
| function casTicket() {
 | |
|   HSADMINNG_CAS_TGT=$(<~/.cas-login-tgt)
 | |
|   if [[ -z "$HSADMINNG_CAS_TGT" ]]; then
 | |
|     echo "ERROR: cannot get CAS ticket granting ticket for $HSADMINNG_CAS_USERNAME" >&2
 | |
|     exit 1
 | |
|   fi
 | |
|   trace "CAS-TGT: $HSADMINNG_CAS_TGT"
 | |
| 
 | |
|   trace "fetching CAS service ticket"
 | |
|   trace "curl -s -d \"service=$HSADMINNG_CAS_SERVICE_ID\" $HSADMINNG_CAS_TGT"
 | |
|   HSADMINNG_CAS_TICKET=$(curl -s -d "service=$HSADMINNG_CAS_SERVICE_ID" $HSADMINNG_CAS_TGT)
 | |
|   if [[ -z "$HSADMINNG_CAS_TICKET" ]]; then
 | |
|     echo "ERROR: cannot get CAS service ticket" >&2
 | |
|     exit 1
 | |
|   fi
 | |
| 
 | |
|   echo $HSADMINNG_CAS_TICKET
 | |
| }
 | |
| 
 | |
| function casTgt() {
 | |
|   HSADMINNG_CAS_TGT=$(<~/.cas-login-tgt)
 | |
|     if [[ -z "$HSADMINNG_CAS_TGT" ]]; then
 | |
|       echo "ERROR: cannot get CAS ticket granting ticket for $HSADMINNG_CAS_USERNAME" >&2
 | |
|       exit 1
 | |
|     fi
 | |
|     echo "CAS-TGT: $HSADMINNG_CAS_TGT"
 | |
| }
 | |
| 
 | |
| function casValidate() {
 | |
|   HSADMINNG_CAS_TICKET=`casTicket`
 | |
| 
 | |
|   trace "validating CAS-TICKET: $HSADMINNG_CAS_TICKET"
 | |
|   # Do NOT use doCurl here! We do not pass a CAS service ticket.
 | |
|   trace curl -i -s $HSADMINNG_CAS_VALIDATE?ticket=${HSADMINNG_CAS_TICKET}\&service=${HSADMINNG_CAS_SERVICE_ID}
 | |
|   HSADMINNG_CAS_USER=`curl -i -s $HSADMINNG_CAS_VALIDATE?ticket=${HSADMINNG_CAS_TICKET}\&service=${HSADMINNG_CAS_SERVICE_ID} | grep -oPm1 "(?<=<cas:user>)[^<]+"`
 | |
|   if [ -z "$HSADMINNG_CAS_USER" ]; then
 | |
|     echo "validation failed" >&2
 | |
|     exit 1
 | |
|   fi
 | |
|   echo "CAS-User: $HSADMINNG_CAS_USER"
 | |
| }
 | |
| 
 | |
| case "${1,,}" in
 | |
| 
 | |
|   # -- generic commands --------------------------------------------------------------------------
 | |
| 
 | |
|   ""|"-h"|"--help"|"help") ## prints documentation about commands and options
 | |
|       casCurlDocumentation
 | |
|       exit
 | |
|     ;;
 | |
| 
 | |
|   "env") ## prints all related HSADMINNG_CAS_... environment variables; use '--show-password' to show the password as well
 | |
|   #       example: cas-curl env --show-password
 | |
|     echo "HSADMINNG_CAS_LOGIN: $HSADMINNG_CAS_LOGIN"
 | |
|     echo "HSADMINNG_CAS_VALIDATE: $HSADMINNG_CAS_VALIDATE"
 | |
|     echo "HSADMINNG_CAS_USERNAME: $HSADMINNG_CAS_USERNAME"
 | |
|     if [ "$2" == "--show-password" ]; then
 | |
|       echo "HSADMINNG_CAS_PASSWORD: $HSADMINNG_CAS_PASSWORD"
 | |
|     elif [ -z "$HSADMINNG_CAS_PASSWORD" ]; then
 | |
|       echo "HSADMINNG_CAS_PASSWORD: <<not given>>"
 | |
|     else
 | |
|       echo "HSADMINNG_CAS_PASSWORD: <<given, but hidden - add --show-password to show>>"
 | |
|     fi
 | |
|     echo "HSADMINNG_CAS_SERVICE_ID: $HSADMINNG_CAS_SERVICE_ID"
 | |
|     ;;
 | |
| 
 | |
|   # --- authentication-related commands ------------------------------------------------------------
 | |
| 
 | |
|   "login") ## reads username+password and fetches ticket granting ticket (bypasses HSADMINNG_CAS_USERNAME+HSADMINNG_CAS_PASSWORD)
 | |
|   #       example: cas-curl login
 | |
|     casLogout
 | |
|     export HSADMINNG_CAS_USERNAME=
 | |
|     export HSADMINNG_CAS_PASSWORD=
 | |
|     casLogin
 | |
|     ;;
 | |
|   "assume") ## assumes the given comma-separated roles
 | |
|   #       example using object-id-name: cas-curl assume 'hs_office.relation#ExampleMandant-with-PARTNER-ExamplePartner:AGENT'
 | |
|   #       example using object-uuid:    cas-curl assume 'hs_office.relation#1d3bc468-c5c8-11ef-9d0d-4751ecfda2b7:AGENT'
 | |
|     shift
 | |
|     if [ -z "$1" ]; then
 | |
|       echo "ERROR: requires comma-separated list of roles to assume" >&2
 | |
|       exit 1
 | |
|     fi
 | |
|     echo "$1" >~/.cas-curl-assume
 | |
|     ;;
 | |
|   "unassume") ## do not assume any particular role anymore, use the plain user as RBAC subject
 | |
|     rm ~/.cas-curl-assume
 | |
|     ;;
 | |
|   "tgt") ## prints the current ticket granting ticket
 | |
|     casTgt
 | |
|     ;;
 | |
|   "validate") ## validates current ticket granting ticket and prints currently logged in user
 | |
|     casValidate
 | |
|     ;;
 | |
|   "logout") ## logout, deletes ticket granting ticket
 | |
|     casLogout
 | |
|     ;;
 | |
| 
 | |
|   # --- HTTP-commands ----------------------------------------------------------------------
 | |
| 
 | |
|   "get") ## HTTP GET, add URL as parameter
 | |
|   #       example: cas-curl GET http://localhost:8080/api/hs/office/partners/P-10003 | jq
 | |
|   #       hint: '| jq' is just for human-readable formatted JSON output
 | |
|     shift
 | |
|     casLogin
 | |
|     HSADMINNG_CAS_TICKET=`casTicket`
 | |
|     doCurl "$*"
 | |
|     ;;
 | |
|   "post") ## HTTP POST, add curl options to specify the request body and the URL as last parameter
 | |
|   #       example: cas-curl POST \
 | |
|   #                   -d '{ "prefix":"ttt", "reference":80001, "adminUserName":"admin@ttt.example.com" }' \
 | |
|   #                   http://localhost:8080/api/test/customers | jq
 | |
|   #       hint: '| jq' is just for human-readable formatted JSON output
 | |
|     shift
 | |
|     casLogin
 | |
|     HSADMINNG_CAS_TICKET=`casTicket`
 | |
|     doCurl --header "Content-Type: application/json" -X POST "$@"
 | |
|     ;;
 | |
|   "patch") ## HTTP PATCH, add curl options to specify the request body and the URL as last parameterparameter
 | |
|   #       example: cas-curl PATCH \
 | |
|   #                   -d '{ "reference":80002 }' \
 | |
|   #                   http://localhost:8080/api/test/customers/ae90ac2a-4728-4ca9-802e-a0d0108b2324 | jq
 | |
|   #       hint: '| jq' is just for human-readable formatted JSON output
 | |
|     shift
 | |
|     casLogin
 | |
|     HSADMINNG_CAS_TICKET=`casTicket`
 | |
|     doCurl --header "Content-Type: application/json" -X POST "$*"
 | |
|     ;;
 | |
|   "delete") ## HTTP DELETE, add curl options to specify the request body and the URL as last parameter
 | |
|   #     example: cas-curl DELETE http://localhost:8080/api/hs/office/persons/ae90ac2a-4728-4ca9-802e-a0d0108b2324
 | |
|     shift
 | |
|     casLogin
 | |
|     HSADMINNG_CAS_TICKET=`casTicket`
 | |
|     curl -X POST "$@"
 | |
|     ;;
 | |
|   *)
 | |
|     cat >&2 <<EOF
 | |
|     unknown command: '$1'
 | |
|     valid commands: help, login, logout, validate, get, post, patch, delete
 | |
| EOF
 | |
|     exit 1
 | |
|     ;;
 | |
| esac
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 |