Skip to content

Instantly share code, notes, and snippets.

@gordyt
Created March 20, 2020 23:23
Show Gist options
  • Save gordyt/3e3882f62b168267bc804384061341b7 to your computer and use it in GitHub Desktop.
Save gordyt/3e3882f62b168267bc804384061341b7 to your computer and use it in GitHub Desktop.
test-soap-affinity, version 2
#!/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