Last active
August 29, 2015 14:07
-
-
Save trscavo/3555b3e23ef2ee3cc4ab to your computer and use it in GitHub Desktop.
Bash script to filter the entity attributes from a SAML metadata file
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 | |
##################################################################### | |
# 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