Created
March 20, 2020 23:23
-
-
Save gordyt/3e3882f62b168267bc804384061341b7 to your computer and use it in GitHub Desktop.
test-soap-affinity, version 2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# ************************************************************************ | |
# Script requirements: | |
# - xmlstarlet | |
# - jsonpp | |
# Deployment requirements | |
# - You must have DEBUG logging enabled for the zmc-mls service | |
# ************************************************************************ | |
VERSION=2 | |
# ************************************************************************ | |
# Declarations | |
# ************************************************************************ | |
# Set to one to enable extra output (--debug) | |
DEBUG=0 | |
# Set to one to force refresh user's cached jwt info (--refresh) | |
REFRESH=0 | |
# Zimbra server with JWT support (--server SERVER) | |
# this should point to the zmc-proxy service | |
SERVER=localhost | |
# zmc-proxy port to use (--port PORT) for regular soap requests | |
PORT=443 | |
# zmc-proxy port to use (--admin-port PORT) for admin soap requests | |
ADMIN_PORT=9071 | |
# MLS Service hosts | |
MLS_SERVER=localhost | |
# MLS Servic port | |
MLS_PORT=7072 | |
# User email (--user USER) | |
USER= | |
# User password. Required only to generate and cache the | |
# JWT token and salt and the Zimbra Auth Token. (--pw PW) | |
PW= | |
# Admin user email (--admin-user USER) | |
ADMIN_USER=admin@zmc.com | |
# Admin user password (--admin-pw PW) | |
# Needed for the admin test functions | |
ADMIN_PW= | |
# ************************************************************************ | |
# IMPORTANT - if necessary, update the following to reflect how | |
# xmlstarlet is named on your system | |
# ALSO - If you have a different JSON pretty-printer, update that as well | |
# ************************************************************************ | |
# Used to parse XMLS | |
XML=xmlstarlet | |
# Used to pretty-print JSON | |
JSON=json_pp | |
# These two are filled in by function load_jwt_creds | |
COOKIE= | |
JWT= | |
# ************************************************************************ | |
# Debug logging | |
# ************************************************************************ | |
function debug { | |
func=$1 | |
name=$2 | |
data=$3 | |
if [ "$DEBUG" -eq 1 ]; then | |
echo "---------- ${func} / ${name} begin ----------" | |
echo "${data}" | |
echo "---------- ${func} / ${name} end ----------" | |
fi | |
} | |
# ************************************************************************ | |
# Call the MLS service to lookup USER | |
# Sets AFFINITY GLOBAL and eches result | |
# ************************************************************************ | |
function lookup_user { | |
AFFINITY=$(curl -k -s https://${MLS_SERVER}:${MLS_PORT}/search?email=$USER) | |
echo "User: $USER, Affinity: $AFFINITY" | |
} | |
function lookup_user_admin { | |
AFFINITY=$(curl -k -s https://${MLS_SERVER}:${MLS_PORT}/search?email=$ADMIN_USER) | |
echo "Admin User: $ADMIN_USER, Affinity: $AFFINITY" | |
} | |
# ************************************************************************ | |
# Issue an auth request to request JWT creds. Parse out the | |
# JWT token and the salt value from the cookie. | |
# Cache the results in CRED_CACHE for future requests. | |
# ************************************************************************ | |
function auth_jwt { | |
CRED_CACHE="/tmp/.${USER}.jwt.cache" | |
rm -f "${CRED_CACHE}" | |
T1=$(mktemp) | |
T2=$(mktemp) | |
# shellcheck disable=SC2064 | |
trap "rm -f \"$T1\" \"$T2\"" INT TERM HUP EXIT | |
( | |
curl -v -s -k -X POST https://$SERVER:$PORT/service/soap -H "Content-Type:application/soap+xml" --data-binary @- <<EOF | |
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> | |
<soap:Header> | |
<context xmlns="urn:zimbra"> | |
<authToken/> | |
<nosession/> | |
<userAgent name="zmsoap"/> | |
<authTokenControl voidOnExpired="0"/> | |
</context> | |
</soap:Header> | |
<soap:Body> | |
<AuthRequest xmlns="urn:zimbraAccount" tokenType="JWT"> | |
<account by="name">$USER</account> | |
<password>$PW</password> | |
</AuthRequest> | |
</soap:Body> | |
</soap:Envelope> | |
EOF | |
) 1>"$T1" 2>"$T2" | |
if [ "$DEBUG" -eq 1 ]; then | |
>&2 echo | |
>&2 echo "---------- STDOUT from SOAP request start ----------" | |
cat "$T1" | |
>&2 echo "" | |
>&2 echo "---------- STDOUT from SOAP request end ----------" | |
>&2 echo "---------- STDERR from SOAP request start ----------" | |
cat "$T2" | |
>&2 echo "---------- STDERR from SOAP request end ----------" | |
fi | |
JWT=$($XML sel -N zimbra=urn:zimbraAccount -t -v "//soap:Envelope/soap:Body/zimbra:AuthResponse/*[1]" <"$T1") | |
COOKIE=$(awk '/ZM_JWT/ {print $3}' <"$T2" | sed -e 's/ZM_JWT=//' -e 's/;.*$//') | |
if [ "$DEBUG" -eq 1 ]; then | |
>&2 echo "---------- Extracted JWT value start ----------" | |
>&2 echo "$JWT" | |
>&2 echo "---------- Extracted ZM_JWT Cookie value end ----------" | |
>&2 echo "$COOKIE" | |
>&2 echo "---------- Extracted JWT value end ----------" | |
fi | |
[ -z "$JWT" ] && >&2 echo "No JWT extracted, failure to authenticate" && exit 1 | |
[ -z "$COOKIE" ] && >&2 echo "No ZM_JWT cookie found, failure to authenticate" && exit 1 | |
echo "JWT=$JWT" > "${CRED_CACHE}" | |
echo "COOKIE=$COOKIE" >> "${CRED_CACHE}" | |
} | |
# ************************************************************************ | |
# Checked to see if we have already cached the JWT token and salt | |
# for the current $USER. If so, load from that. If not, authenticate | |
# and load them. | |
# ************************************************************************ | |
function load_jwt_creds { | |
CRED_CACHE="/tmp/.${USER}.jwt.cache" | |
if [ "$REFRESH" -eq 1 ]; then | |
rm -f "$CRED_CACHE" | |
fi | |
if [ -s "${CRED_CACHE}" ]; then | |
# shellcheck disable=SC1090 | |
source "${CRED_CACHE}" | |
else | |
auth_jwt | |
# shellcheck disable=SC1090 | |
source "${CRED_CACHE}" | |
fi | |
} | |
# ************************************************************************ | |
# Attempt to authenticate with USER and PW. Cache the token. | |
# Cache the results in AUTH_TOKEN_PATH for future requests. | |
# ************************************************************************ | |
function auth { | |
AUTH_TOKEN_PATH=$(auth_token_path) | |
rm -f "${AUTH_TOKEN_PATH}" | |
T1=$(mktemp) | |
T2=$(mktemp) | |
# shellcheck disable=SC2064 | |
trap "rm -f \"$T1\" \"$T2\"" INT TERM HUP EXIT | |
( | |
curl -v -s -k -X POST https://$SERVER:$PORT/service/soap -H "Content-Type:application/soap+xml" --data-binary @- <<EOF | |
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> | |
<soap:Header> | |
<context xmlns="urn:zimbra"> | |
<authToken/> | |
<nosession/> | |
<userAgent name="zmsoap"/> | |
<authTokenControl voidOnExpired="0"/> | |
</context> | |
</soap:Header> | |
<soap:Body> | |
<AuthRequest xmlns="urn:zimbraAccount"> | |
<account by="name">$USER</account> | |
<password>$PW</password> | |
</AuthRequest> | |
</soap:Body> | |
</soap:Envelope> | |
EOF | |
) 1>"$T1" 2>"$T2" | |
debug $0 stdout $(cat "$T1") | |
debug $0 stderr $(cat "$T2") | |
AUTHCOOKIE=$(awk '/ZM_AUTH_TOKEN/ {print $3}' <"$T2" | sed -e 's/ZM_AUTH_TOKEN=//' -e 's/;.*$//') | |
debug $0 zm-auth-token $AUTHCOOKIE | |
echo "${AUTHCOOKIE}" > ${AUTH_TOKEN_PATH} | |
} | |
# ************************************************************************ | |
# Attempt to authenticate with ADMIN_USER and ADMIN_PW. Cache the token. | |
# Cache the results in AUTH_TOKEN_PATH for future requests. | |
# ************************************************************************ | |
function auth_admin { | |
AUTH_TOKEN_PATH_ADMIN=$(auth_token_path_admin) | |
rm -f "${AUTH_TOKEN_PATH_ADMIN}" | |
T1=$(mktemp) | |
T2=$(mktemp) | |
# shellcheck disable=SC2064 | |
trap "rm -f \"$T1\" \"$T2\"" INT TERM HUP EXIT | |
( | |
curl -v -s -k -X POST https://$SERVER:$ADMIN_PORT/service/admin/soap -H "Content-Type:application/soap+xml" --data-binary @- <<EOF | |
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> | |
<soap:Body> | |
<AuthRequest xmlns="urn:zimbraAdmin"> | |
<account by="name">$ADMIN_USER</account> | |
<password>$ADMIN_PW</password> | |
</AuthRequest> | |
</soap:Body> | |
</soap:Envelope> | |
EOF | |
) 1>"$T1" 2>"$T2" | |
debug $0 auth_admin-stdout "$(cat $T1)" | |
debug $0 auth_admin-stderr "$(cat $T2)" | |
AUTHCOOKIE=$(awk '/ZM_ADMIN_AUTH_TOKEN/ {print $3}' <"$T2" | sed -e 's/ZM_ADMIN_AUTH_TOKEN=//' -e 's/;.*$//') | |
debug $0 zm-admin-auth-token $AUTHCOOKIE | |
echo "${AUTHCOOKIE}" > ${AUTH_TOKEN_PATH_ADMIN} | |
} | |
# ************************************************************************ | |
# Return path to cached auth token file for USER | |
# ************************************************************************ | |
function auth_token_path { | |
echo "/tmp/.${USER}.auth_token.cache" | |
} | |
# ************************************************************************ | |
# Return path to cached auth token file for ADMIN_USER | |
# ************************************************************************ | |
function auth_token_path_admin { | |
echo "/tmp/.${ADMIN_USER}.auth_token.cache" | |
} | |
# ************************************************************************ | |
# Get cached auth token for USER, will authenticate if needed | |
# ************************************************************************ | |
function auth_token { | |
AUTH_TOKEN_PATH=$(auth_token_path) | |
if [[ ! ( -f "${AUTH_TOKEN_PATH}" ) ]]; then | |
auth | |
fi | |
cat "${AUTH_TOKEN_PATH}" | |
} | |
# ************************************************************************ | |
# Get cached auth token for ADMIN_USER, will authenticate if needed | |
# ************************************************************************ | |
function auth_token_admin { | |
AUTH_TOKEN_PATH_ADMIN=$(auth_token_path_admin) | |
if [ "$REFRESH" -eq 1 ]; then | |
rm -f "$AUTH_TOKEN_PATH_ADMIN" | |
fi | |
if [[ ! ( -f "${AUTH_TOKEN_PATH_ADMIN}" ) ]]; then | |
auth_admin | |
fi | |
cat "${AUTH_TOKEN_PATH_ADMIN}" | |
} | |
# ************************************************************************ | |
# Perform a basic GetFolderRequest using JWT auth using cookie | |
# ************************************************************************ | |
function jwt_cookie_json { | |
lookup_user | |
load_jwt_creds | |
T1=$(mktemp) | |
T2=$(mktemp) | |
# shellcheck disable=SC2064 | |
trap "rm -f \"$T1\" \"$T2\"" INT TERM HUP EXIT | |
( | |
curl -v -s -k "https://${SERVER}:${PORT}/service/soap/SearchRequest" \ | |
-H "Authorization: Bearer ${JWT}" \ | |
--cookie "ZM_JWT=${COOKIE}" \ | |
--data-binary @- <<EOF | |
{ | |
"Header": { | |
"context": { | |
"_jsns": "urn:zimbra", | |
"account": { | |
"_content": "$USER", | |
"by": "name" | |
}, | |
"csrfToken": "0_f9b2d299b2d4015a19e23e25ecdb238b751a19bb" | |
} | |
}, | |
"Body": { | |
"GetFolderRequest": { | |
"_jsns": "urn:zimbraMail", | |
"header": [ | |
{ | |
"n": "List-ID" | |
}, | |
{ | |
"n": "X-Zimbra-DL" | |
}, | |
{ | |
"n": "IN-REPLY-TO" | |
} | |
], | |
} | |
} | |
} | |
EOF | |
) 1>"$T1" 2>"$T2" | |
echo -n "jwt_cookie_json: " | |
cat "$T2" | grep Auth-Server | awk '{print $3}' | |
} | |
# ************************************************************************ | |
# Perform a basic GetFolderRequest using JWT auth passed in body of req | |
# ************************************************************************ | |
function jwt_body_json { | |
lookup_user | |
load_jwt_creds | |
T1=$(mktemp) | |
T2=$(mktemp) | |
# shellcheck disable=SC2064 | |
trap "rm -f \"$T1\" \"$T2\"" INT TERM HUP EXIT | |
( | |
curl -v -k "https://${SERVER}:${PORT}/service/soap/SearchRequest" \ | |
--data-binary @- <<EOF | |
{ | |
"Header": { | |
"context": { | |
"_jsns": "urn:zimbra", | |
"account": { | |
"_content": "$USER", | |
"by": "name" | |
}, | |
"jwtToken": "${JWT}", | |
"jwtSalt": "${COOKIE}", | |
"csrfToken": "0_f9b2d299b2d4015a19e23e25ecdb238b751a19bb" | |
} | |
}, | |
"Body": { | |
"GetFolderRequest": { | |
"_jsns": "urn:zimbraMail", | |
"header": [ | |
{ | |
"n": "List-ID" | |
}, | |
{ | |
"n": "X-Zimbra-DL" | |
}, | |
{ | |
"n": "IN-REPLY-TO" | |
} | |
] | |
} | |
} | |
} | |
EOF | |
) 1>"$T1" 2>"$T2" | |
echo -n "jwt_body_json: " | |
cat "$T2" | grep Auth-Server | awk '{print $3}' | |
} | |
function auth_body_xml { | |
lookup_user | |
at=$(auth_token) | |
T1=$(mktemp) | |
T2=$(mktemp) | |
# shellcheck disable=SC2063 | |
trap "rm -f \"$T1\" \"$T2\"" INT TERM HUP EXIT | |
( | |
curl -v -s -k -X POST https://$SERVER:$PORT/service/soap -H "Content-Type:application/soap+xml" --data-binary @- <<EOF | |
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> | |
<soap:Header> | |
<context xmlns="urn:zimbra"> | |
<authToken>${at}</authToken> | |
</context> | |
</soap:Header> | |
<soap:Body> | |
<GetFolderRequest xmlns = "urn:zimbraMail"/> | |
</soap:Body> | |
</soap:Envelope> | |
EOF | |
) 1>"$T1" 2>"$T2" | |
debug $0 stdout $(cat "$T1") | |
debug $0 stderr $(cat "$T2") | |
echo -n "auth_body_xml: " | |
cat "$T2" | grep Auth-Server | awk '{print $3}' | |
} | |
function auth_body_xml_admin { | |
lookup_user_admin | |
at=$(auth_token_admin) | |
T1=$(mktemp) | |
T2=$(mktemp) | |
# shellcheck disable=SC2063 | |
trap "rm -f \"$T1\" \"$T2\"" INT TERM HUP EXIT | |
( | |
curl -v -s -k -X POST https://$SERVER:$ADMIN_PORT/service/admin/soap -H "Content-Type:application/soap+xml" --data-binary @- <<EOF | |
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> | |
<soap:Header> | |
<context xmlns="urn:zimbra"> | |
<authToken>${at}</authToken> | |
</context> | |
</soap:Header> | |
<soap:Body> | |
<GetAllServersRequest xmlns = "urn:zimbraAdmin"/> | |
</soap:Body> | |
</soap:Envelope> | |
EOF | |
) 1>"$T1" 2>"$T2" | |
debug $0 stdout "$(cat $T1)" | |
debug $0 stderr "$(cat $T2)" | |
echo -n "auth_body_xml_admin: " | |
cat "$T2" | grep Auth-Server | awk '{print $3}' | |
} | |
function auth_cookie_xml { | |
lookup_user | |
at=$(auth_token) | |
T1=$(mktemp) | |
T2=$(mktemp) | |
# shellcheck disable=SC2064 | |
trap "rm -f \"$T1\" \"$T2\"" INT TERM HUP EXIT | |
( | |
curl -v -s -k -X POST https://$SERVER:$PORT/service/soap -H "Content-Type:application/soap+xml" \ | |
--cookie "ZM_AUTH_TOKEN=${at}" \ | |
--data-binary @- <<EOF | |
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> | |
<soap:Header> | |
<context xmlns="urn:zimbra"> | |
</context> | |
</soap:Header> | |
<soap:Body> | |
<GetFolderRequest xmlns = "urn:zimbraMail"/> | |
</soap:Body> | |
</soap:Envelope> | |
EOF | |
) 1>"$T1" 2>"$T2" | |
echo -n "auth_cookie_xml: " | |
cat "$T2" | grep Auth-Server | awk '{print $3}' | |
} | |
# This does a GetAllServers call | |
function auth_cookie_xml_admin { | |
lookup_user_admin | |
at=$(auth_token_admin) | |
T1=$(mktemp) | |
T2=$(mktemp) | |
# shellcheck disable=SC2064 | |
trap "rm -f \"$T1\" \"$T2\"" INT TERM HUP EXIT | |
( | |
curl -v -s -k -X POST https://$SERVER:$ADMIN_PORT/service/admin/soap -H "Content-Type:application/soap+xml" \ | |
--cookie "ZM_ADMIN_AUTH_TOKEN=${at}" \ | |
--data-binary @- <<EOF | |
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> | |
<soap:Header> | |
<context xmlns="urn:zimbra"> | |
</context> | |
</soap:Header> | |
<soap:Body> | |
<GetAllServersRequest xmlns = "urn:zimbraAdmin"/> | |
</soap:Body> | |
</soap:Envelope> | |
EOF | |
) 1>"$T1" 2>"$T2" | |
debug $0 stdout "$(cat $T1)" | |
debug $0 stderr "$(cat $T2)" | |
echo -n "auth_cookie_xml_admin: " | |
cat "$T2" | grep Auth-Server | awk '{print $3}' | |
} | |
# ************************************************************************ | |
# Print usage and exit | |
# ************************************************************************ | |
FUNCTIONS=( | |
auth_body_xml | |
auth_body_xml_admin | |
auth_cookie_xml_admin | |
auth_cookie_xml | |
jwt_cookie_json | |
jwt_body_json | |
) | |
function usage { | |
>&2 echo "$0 [OPTIONS] [-- [ARGS]] (version $VERSION)" | |
>&2 echo "where OPTIONS may be any of the following:" | |
>&2 echo "--server SERVER (default=${SERVER}) JWT-enabled Zimbra server proxy" | |
>&2 echo "--admin-port PORT (default=${ADMIN_PORT}) port number on proxy admin soap" | |
>&2 echo "--port PORT (default=${PORT}) port number on proxy soap" | |
>&2 echo "--mls-server SERVER (default=${MLS_SERVER}) MLS service endpoint" | |
>&2 echo "--mls-port PORT (default=${MLS_PORT}) port number for MLS service" | |
>&2 echo "--admin-user USER_EMAIL (default=${ADMIN_USER}) The email address of admin user to auth as" | |
>&2 echo "--user USER_EMAIL (required) The email address of user to auth as" | |
>&2 echo "--admin-pw PASSWORD The password for the specified admin user" | |
>&2 echo "--pw PASSWORD The password for the specified user" | |
>&2 echo " This is needed just to do the auth request and fetch" | |
>&2 echo " the JWT token and salt." | |
>&2 echo "--function FUNCTION The function to execute. Available functions:" | |
for f in "${FUNCTIONS[@]}"; do | |
>&2 echo " ${f}" | |
done | |
>&2 echo " The default function is ${FUNCTIONS[0]}." | |
>&2 echo " Any ARGS passed in are sent as arguments to the" | |
>&2 echo " selected function." | |
>&2 echo "--refresh Remove the cached auth info for user to force refesh" | |
>&2 echo "--debug Print out extra debug info" | |
>&2 echo "-h|--help Print help message and exit" | |
exit 1 | |
} | |
# ************************************************************************ | |
# Check the --function arg. If invalid, print error and exit | |
# ************************************************************************ | |
function check_function { | |
func="$1" | |
if [ "$func" == "ALL" ] ;then | |
return | |
fi | |
for f in "${FUNCTIONS[@]}"; do | |
if [ "$func" = "$f" ]; then | |
return | |
fi | |
done | |
>&2 echo "Invalid --function: '$func'" | |
exit 1 | |
} | |
# ************************************************************************ | |
# Parse command line args | |
# ************************************************************************ | |
# Default Function | |
FUNCTION=ALL | |
while [ $# -ne 0 ]; do | |
case "$1" in | |
--server) | |
shift | |
SERVER=$1 | |
;; | |
--admin-port) | |
shift | |
ADMIN_PORT=$1 | |
;; | |
--port) | |
shift | |
PORT=$1 | |
;; | |
--mls-server) | |
shift | |
MLS_SERVER=$1 | |
;; | |
--mls-port) | |
shift | |
MLS_PORT=$1 | |
;; | |
--admin-user) | |
shift | |
ADMIN_USER=$1 | |
;; | |
--user) | |
shift | |
USER=$1 | |
;; | |
--admin-pw) | |
shift | |
ADMIN_PW=$1 | |
;; | |
--pw) | |
shift | |
PW=$1 | |
;; | |
--function) | |
shift | |
FUNCTION=$1 | |
;; | |
--debug) | |
DEBUG=1 | |
;; | |
--refresh) | |
REFRESH=1 | |
;; | |
--) | |
shift | |
break | |
;; | |
*) | |
usage | |
;; | |
esac | |
shift | |
done | |
check_function "$FUNCTION" | |
if [ "$FUNCTION" == "ALL" ]; then | |
for f in "${FUNCTIONS[@]}"; do | |
# shellcheck disable=SC2086 | |
eval $f "$@" | |
done | |
else | |
# shellcheck disable=SC2086 | |
eval $FUNCTION "$@" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment