Skip to content

Instantly share code, notes, and snippets.

@trscavo
Last active August 29, 2015 14:07
Show Gist options
  • Save trscavo/3555b3e23ef2ee3cc4ab to your computer and use it in GitHub Desktop.
Save trscavo/3555b3e23ef2ee3cc4ab to your computer and use it in GitHub Desktop.
Bash script to filter the entity attributes from a SAML metadata file
#!/bin/bash
#####################################################################
# Inspect a SAML metadata file, iterate over the entity descriptors
# in the file, and pass each entity descriptor through a filter that
# produces a list of entity attributes in metadata.
#
# Usage: filter_mdattrs.sh [-v] [MD_FILE]
#
# Optionally takes the path to the metadata file as a command-line
# parameter. If none is given, takes its input from stdin instead.
#
# For every entity attribute value in metadata, a line of output with
# five fields is written to stdout. See below for more details about
# the output file.
#
# Examples:
#
# $ curl --remote-name http://md.example.org/some-metadata.xml
# $ filter_mdattrs.sh some-metadata.xml
# ...
#
# $ cat some-metadata.xml | filter_mdattrs.sh -v
# ...
#
#####################################################################
script_name=${0##*/} # equivalent to basename $0
# process command-line option(s)
verbose_mode=false
while getopts ":v" opt; do
case $opt in
v)
verbose_mode=true
;;
\?)
echo "ERROR: $script_name: Unrecognized option: -$OPTARG" >&2
exit 2
;;
esac
done
# make sure there is at most one command-line argument
shift $(( OPTIND - 1 ))
if [ $# -gt 1 ]; then
echo "ERROR: $script_name: too many arguments: $# (0 or 1 required)" >&2
exit 2
fi
# create a temporary directory
tmp_dir=$( mktemp -d 2>/dev/null || mktemp -d -t "${script_name%%.*}" )
if [ ! -d "$tmp_dir" ] ; then
printf "ERROR: Unable to create temporary dir\n" >&2
exit 2
fi
$verbose_mode && printf "$script_name using temp dir: %s\n" "$tmp_dir"
# read the input into a temporary file
md_file=${tmp_dir}/tmp_metadata.xml
if [ $# -eq 1 ]; then
if [ ! -f "$1" ] ; then
printf "ERROR: The metadata file does not exist: %s\n" "$1" >&2
exit 2
fi
# copy input file into the temp file
cat "$1" > "$md_file"
else
# read input from stdin into the temp file
cat - > "$md_file"
fi
# Does the file contain an aggregate of SAML metadata?
entities_descriptors=$( cat "$md_file" | grep -E '<(md:)?EntitiesDescriptor ' )
if [ -z "$entities_descriptors" ]; then
printf "ERROR: The file is NOT a SAML metadata aggregate: %s\n" "$md_file" >&2
exit 2
fi
num_descriptors=$( echo "$entities_descriptors" | wc -l )
if [ "$num_descriptors" -gt 1 ]; then
printf "ERROR: Multiple EntitiesDescriptor elements found: %d\n" "$num_descriptors" >&2
exit 2
fi
#####################################################################
# An entity attribute filter that returns a list of entity attributes
# bound to an entity descriptor. The filter produces one line of output
# for each entity attribute value in the entity descriptor:
#
# <registrarID> <entityID> <role> <attr_name> <attr_value>
#
# where <role> is either "IdP" or "SP" (or "UNKNOWN" if the role can
# not be determined).
#
# A typical entity attribute looks like this:
#
# <mdattr:EntityAttributes xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute">
# <saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
# Name="http://macedir.org/entity-category"
# NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
# <saml:AttributeValue>
# http://refeds.org/category/research-and-scholarship
# </saml:AttributeValue>
# </saml:Attribute>
# </mdattr:EntityAttributes>
#
# The above entity attribute has name "http://macedir.org/entity-category"
# and value "http://refeds.org/category/research-and-scholarship".
#
# Note that entity attributes may be multi-valued, in which case multiple
# lines of output are returned per entity attribute, one for each value.
#####################################################################
filter_entity () {
# make sure there is one (and only one) command-line argument
if [ $# -ne 1 ]; then
echo "ERROR: $FUNCNAME: incorrect number of arguments: $# (1 required)" >&2
return 2
fi
# get the entity attributes for this entity (if any)
entityAttributes=$( echo "$1" \
| sed -n -e '\;<\(mdattr:\)\{0,1\}EntityAttributes;,\;EntityAttributes>;p'
)
# if there aren't any entity attributes, we're done
if [ -z "$entityAttributes" ]; then
return 0
fi
# get the Registrar ID
if echo "$1" | grep -Fq ' registrationAuthority='; then
registrarID=$( echo "$1" \
| grep -F -m 1 ' registrationAuthority=' \
| sed -e 's/^.* registrationAuthority="\([^"]*\)".*$/\1/'
)
else
registrarID=UNKNOWN
fi
# get the entityID
entityID=$( echo "$1" \
| grep -F -m 1 ' entityID=' \
| sed -e 's/^.* entityID="\([^"]*\)".*$/\1/'
)
# get the primary role of this entity
if echo "$1" | grep -Eq '<(md:)?IDPSSODescriptor '; then
if echo "$1" | grep -Evq '<(md:)?SPSSODescriptor '; then
role=IdP
fi
elif echo "$1" | grep -Eq '<(md:)?SPSSODescriptor '; then
if echo "$1" | grep -Evq '<(md:)?IDPSSODescriptor '; then
role=SP
fi
else
role=UNKNOWN
fi
# get a list of the entity attribute names
attr_names=$( echo "$entityAttributes" \
| grep -F ' Name=' \
| sed -e 's/^.* Name="\([^"]*\)".*$/\1/'
)
# iterate over the attribute names
for attr_name in $attr_names; do
# get the entity attribute with the given name
entityAttribute=$( echo "$entityAttributes" \
| sed -n -e '\;<\([[:alnum:]]\{1,\}:\)\{0,1\}Attribute.* Name="'${attr_name}'";,\;Attribute>;p'
)
# get a list of the entity attribute values
attr_values=$( echo "$entityAttribute" \
| grep -E '<([^:]+:)?AttributeValue[^>]*>' \
| sed -e 's/^.*<\([[:alnum:]]\{1,\}:\)\{0,1\}AttributeValue[^>]*>\([^<]*\).*$/\2/'
)
# iterate over the attribute values
for attr_value in $attr_values; do
echo "$registrarID $entityID $role $attr_name $attr_value"
done
done
return 0
}
#####################################################################
# begin processing
#####################################################################
# get a list of entityIDs from the metadata aggregate
entityIDs=$( /bin/cat $md_file | grep -F ' entityID=' \
| sed -E 's/^.+ entityID="([^"]+).+$/\1/'
)
if $verbose_mode; then
num_entities=$( echo "$entityIDs" | /usr/bin/wc -l )
printf "Number of entities found: %d\n" $num_entities
fi
for entityID in $entityIDs; do
# get the entity descriptor for this entity
entityDescriptor=$( /bin/cat $md_file \
| sed -n -e '\;<\(md:\)\{0,1\}EntityDescriptor.* entityID="'${entityID}'";,\;EntityDescriptor>;p'
)
filtered_result=$( filter_entity "$entityDescriptor" )
return_status=$?
if [ "$return_status" -ne 0 ]; then
echo "ERROR: $script_name: failed to execute filter: $filter_file" >&2
fi
if [ ! -z "$filtered_result" ]; then
echo "$filtered_result"
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment