diff --git a/bin/list_local_saml_idp_endpoints.sh b/bin/list_local_saml_idp_endpoints.sh new file mode 100755 index 0000000..d087b6e --- /dev/null +++ b/bin/list_local_saml_idp_endpoints.sh @@ -0,0 +1,407 @@ +#!/bin/bash + +####################################################################### +# Copyright 2015--2017 Internet2 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +####################################################################### + +####################################################################### +# help message +####################################################################### + +display_help () { +/bin/cat <<- HELP_MSG + NOTE: Run probe_saml_idps.sh before running this script! + + Given the results of a previously executed probe, this script + converts the flat text files produced by that probe into a set + of JSON files suitable for display on a web page. + Each run of this script also produces two IdP summary files in + JSON file format. + + Usage: ${0##*/} [-hvq] [-23] -d IO_DIR [-o OUT_DIR] REGISTRAR_ID + + The -d option is required. The IO_DIR argument is an input + directory that contains the output of the probe_shib_idps.sh + script (which happens to have an identical command-line option, + btw). The OUT_DIR argument to the -o option is the output + directory for the current script. All of the JSON files will + be written to this directory. If the -o option is omitted, the + JSON files will be written to IO_DIR instead. + + By default, the script produces three disjoint lists of Shibboleth + IdPs, in three separate files. One file lists all instances of + Shibboleth IdP V3, a second file lists all V2 instances, and a + third file lists all Shibboleth IdP instances of unknown version. + If the -3 option is given, only the list of Shibboleth IdP V3 + instances is output. Similarly, if the -2 option is given, only + the list of V2 instances is output. +HELP_MSG +} + +####################################################################### +# Bootstrap +####################################################################### + +script_name=${0##*/} # equivalent to basename $0 +script_bin=${0%/*} # equivalent to dirname $0 + +####################################################################### +# Process command-line options and arguments +####################################################################### + +help_mode=false; quiet_mode=false; verbose_mode=false +local_opts= +while getopts ":hqvd:o:" opt; do + case $opt in + h) + help_mode=true + ;; + q) + quiet_mode=true + verbose_mode=false + #local_opts="$local_opts -$opt" + ;; + v) + quiet_mode=false + verbose_mode=true + local_opts="$local_opts -$opt" + ;; + d) + io_dir="$OPTARG" + ;; + o) + out_dir="$OPTARG" + ;; + \?) + echo "ERROR: $script_name: Unrecognized option: -$OPTARG" >&2 + exit 2 + ;; + :) + echo "ERROR: $script_name: Option -$OPTARG requires an argument" >&2 + exit 2 + ;; + esac +done + +if $help_mode; then + display_help + exit 0 +fi + +# check the IO directory +if [ -z "$io_dir" ]; then + echo "ERROR: $script_name: IO_DIR (option -d) is missing" >&2 + exit 2 +fi +if [ ! -d "$io_dir" ]; then + echo "ERROR: $script_name: IO_DIR does not exist: $io_dir" >&2 + exit 2 +fi +$verbose_mode && printf "$script_name using IO_DIR: %s\n" "$io_dir" + +# check the output directory +if [ -z "$out_dir" ]; then + out_dir="$io_dir" +else + if [ ! -d "$out_dir" ]; then + echo "ERROR: $script_name: OUT_DIR does not exist: $out_dir" >&2 + exit 2 + fi +fi +$verbose_mode && printf "$script_name using OUT_DIR: %s\n" "$out_dir" + +shift $(( OPTIND - 1 )) +if [ "$#" -ne 1 ]; then + echo "ERROR: $script_name found $# command-line arguments (1 required)" >&2 + exit 2 +fi +registrarID=$1 + +####################################################################### +# Initialization +####################################################################### + +# Load the compatibility script +COMPATIBILITY_SCRIPT="$io_dir/compatibility.sh" +$verbose_mode && printf "$script_name sourcing compatibility script: %s\n" "$COMPATIBILITY_SCRIPT" +source "$COMPATIBILITY_SCRIPT" >&2 +exit_status=$? +if [ $exit_status -ne 0 ]; then + echo "ERROR: $script_name failed to source $COMPATIBILITY_SCRIPT (exit status $exit_status)" >&2 + exit $exit_status +fi + +# sanity check +if [ ! -f "$IDP_ENDPOINTS_LOG_FILE" ]; then + printf "ERROR: IDP_ENDPOINTS_LOG_FILE does not exist: %s\n" "$IDP_ENDPOINTS_LOG_FILE" >&2 + exit 2 +fi +$verbose_mode && printf "$script_name using file: %s\n" "$IDP_ENDPOINTS_LOG_FILE" +if [ ! -f "$IDP_ENDPOINTS_RESPONSIVE_FILE" ]; then + printf "ERROR: IDP_ENDPOINTS_RESPONSIVE_FILE does not exist: %s\n" "$IDP_ENDPOINTS_RESPONSIVE_FILE" >&2 + exit 2 +fi +$verbose_mode && printf "$script_name using file: %s\n" "$IDP_ENDPOINTS_RESPONSIVE_FILE" +if [ ! -f "$IDP_ENDPOINTS_NOT_RESPONSIVE_FILE" ]; then + printf "ERROR: IDP_ENDPOINTS_NOT_RESPONSIVE_FILE does not exist: %s\n" "$IDP_ENDPOINTS_NOT_RESPONSIVE_FILE" >&2 + exit 2 +fi +$verbose_mode && printf "$script_name using file: %s\n" "$IDP_ENDPOINTS_NOT_RESPONSIVE_FILE" + +if $quiet_mode; then + # redirect stdout to the bit bucket (stderr below) + exec 1>/dev/null + + # redirect stderr to a file + exec 2>"$ERROR_LOG_FILE" +fi + +# determine the source lib directory +if [ -z "$LIB_DIR" ]; then + echo "ERROR: $script_name requires env var LIB_DIR" >&2 + exit 2 +fi +if [ ! -d "$LIB_DIR" ]; then + echo "ERROR: $script_name: directory does not exist: $LIB_DIR" >&2 + exit 2 +fi +$verbose_mode && printf "$script_name using source lib directory: %s\n" "$LIB_DIR" + +# library filenames (always list command_paths first) +LIB_FILENAMES="command_paths.sh +compatible_mktemp.sh" + +# source lib files +for lib_filename in $LIB_FILENAMES; do + lib_file="$LIB_DIR/$lib_filename" + if [ ! -f "$lib_file" ]; then + echo "ERROR: $script_name: lib file does not exist: $lib_file" >&2 + exit 2 + fi + $verbose_mode && printf "$script_name sourcing lib file: %s\n" "$lib_file" + source "$lib_file" + status_code=$? + if [ $status_code -ne 0 ]; then + echo "ERROR: $script_name failed to source lib file ($status_code) $lib_file" >&2 + exit 2 + fi +done + +# create a temporary directory +#tmp_dir=$( make_temp_file -d ) +#if [ ! -d "$tmp_dir" ] ; then +# printf "ERROR: $script_name unable to create temporary dir\n" >&2 +# exit 2 +#fi +#$verbose_mode && printf "$script_name creating temp dir: %s\n" "$tmp_dir" + +####################################################################### +# Functions +####################################################################### + +escape_special_json_chars () { + local str="$1" + echo "$str" | $_SED -e 's/"/\\"/g' +} + +append_json_total_object () { + /bin/cat <<- JSON_TOTAL_OBJECT + { + "heading": "$heading", + "value": $( printf "%d" $value ) + } +JSON_TOTAL_OBJECT +} + +print_summary_endpoints () { + + # begin array + printf "[\n" + + # always emit exactly two JSON objects + + heading="# of responsive endpoints" + value=$( /bin/cat $IDP_ENDPOINTS_RESPONSIVE_FILE \ + | $_GREP "$registrarID\$" \ + | /usr/bin/wc -l + ) + append_json_total_object + + heading="# of non-responsive endpoints" + value=$( /bin/cat $IDP_ENDPOINTS_NOT_RESPONSIVE_FILE \ + | $_GREP "$registrarID\$" \ + | /usr/bin/wc -l + ) + printf " ,\n" + append_json_total_object + + # end array + printf "]\n" +} + +print_summary_non_responsive_endpoints () { + + # tally the number of IdPs with one or more non-responsive endpoints + idp_counts=$( /bin/cat $IDP_ENDPOINTS_NOT_RESPONSIVE_FILE \ + | $_GREP "$registrarID\$" \ + | $_CUT -f5 \ + | $_SORT | $_UNIQ -c + ) + num_idps_with_1_non_responsive_endpoint=$( echo "$idp_counts" \ + | $_GREP "^ *1 " \ + | /usr/bin/wc -l + ) + num_idps_with_2_non_responsive_endpoints=$( echo "$idp_counts" \ + | $_GREP "^ *2 " \ + | /usr/bin/wc -l + ) + num_idps_with_3_non_responsive_endpoints=$( echo "$idp_counts" \ + | $_GREP "^ *3 " \ + | /usr/bin/wc -l + ) + + # the rest of the IdPs have 0 non-responsive endpoints + num_idps_with_a_saml2_sso_endpoint=$( /bin/cat $IDP_ENDPOINTS_LOG_FILE \ + | $_GREP "$registrarID\$" \ + | $_CUT -f5 -d" " | $_SORT | $_UNIQ \ + | /usr/bin/wc -l + ) + # obtain the desired result by subtraction + num_idps_with_0_non_responsive_endpoints=$(( \ + num_idps_with_a_saml2_sso_endpoint - \ + num_idps_with_1_non_responsive_endpoint - \ + num_idps_with_2_non_responsive_endpoints - \ + num_idps_with_3_non_responsive_endpoints + )) + + # begin array + printf "[\n" + + # always emit exactly four JSON objects (even if zero) + + heading="# of IdPs with 0 non-responsive endpoints" + value=$num_idps_with_0_non_responsive_endpoints + append_json_total_object + + heading="# of IdPs with 1 non-responsive endpoints" + value=$num_idps_with_1_non_responsive_endpoint + printf " ,\n" + append_json_total_object + + heading="# of IdPs with 2 non-responsive endpoints" + value=$num_idps_with_2_non_responsive_endpoints + printf " ,\n" + append_json_total_object + + heading="# of IdPs with 3 non-responsive endpoints" + value=$num_idps_with_3_non_responsive_endpoints + printf " ,\n" + append_json_total_object + + # end array + printf "]\n" +} + +append_json_object () { + local orgName=$( escape_special_json_chars "$orgName" ) + local displayName=$( escape_special_json_chars "$displayName" ) + local entityID=$( escape_special_json_chars "$entityID" ) + local binding=$( escape_special_json_chars "$binding" ) + local location=$( escape_special_json_chars "$location" ) + local curlExitCode=$( escape_special_json_chars "$curlExitCode" ) + local httpResponseCode=$( escape_special_json_chars "$httpResponseCode" ) + + /bin/cat <<- JSON_OBJECT + { + "OrganizationName": "$orgName", + "DisplayName": "$displayName", + "entityID": "$entityID", + "Binding": "$binding", + "Location": "$location", + "curlExitCode": "$curlExitCode", + "HTTPResponseCode": "$httpResponseCode" + } +JSON_OBJECT +} + +print_list_endpoints () { + + # begin output list + printf "[\n" + + isFirstObject=true + # use IFS= to preserve leading and trailing whitespace + # (which is not actually required in this case) + /bin/cat - | while IFS= read -r detail_line; do + + # print leading comma unless this is the first object in the array + if $isFirstObject; then + isFirstObject=false + else + printf " ,\n" + fi + + # process the detail line + curlExitCode=$( echo "$detail_line" | $_CUT -f1 ) + httpResponseCode=$( echo "$detail_line" | $_CUT -f2 ) + location=$( echo "$detail_line" | $_CUT -f3 ) + binding=$( echo "$detail_line" | $_CUT -f4 ) + entityID=$( echo "$detail_line" | $_CUT -f5 ) + displayName=$( echo "$detail_line" | $_CUT -f6 ) + orgName=$( echo "$detail_line" | $_CUT -f7 ) + + append_json_object + done + + # end output list + printf "]\n" +} + +####################################################################### +# Main processing +####################################################################### + +# output files +idp_endpoints_summary_file="$out_dir/local-idp-endpoints-summary.json" +idp_endpoints_non_responsive_summary_file="$out_dir/local-idp-endpoints-non-responsive-summary.json" +idp_endpoints_responsive_list_file="$out_dir/local-idp-endpoints-responsive-list.json" +idp_endpoints_non_responsive_list_file="$out_dir/local-idp-endpoints-non-responsive-list.json" + +# clean up +/bin/rm -f \ + "$idp_endpoints_summary_file" \ + "$idp_endpoints_non_responsive_summary_file" \ + "$idp_endpoints_responsive_list_file" \ + "$idp_endpoints_non_responsive_list_file" + +# print summaries +print_summary_endpoints > "$idp_endpoints_summary_file" +print_summary_non_responsive_endpoints > "$idp_endpoints_non_responsive_summary_file" + +# print list of SAML IdPs with responsive endpoints +$verbose_mode && printf "$script_name printing file: %s\n" "$idp_endpoints_responsive_list_file" +/bin/cat "$IDP_ENDPOINTS_RESPONSIVE_FILE" \ + | $_GREP "$registrarID\$" \ + | print_list_endpoints \ + > "$idp_endpoints_responsive_list_file" + +# print list of SAML IdPs with non-responsive endpoints +$verbose_mode && printf "$script_name printing file: %s\n" "$idp_endpoints_non_responsive_list_file" +/bin/cat "$IDP_ENDPOINTS_NOT_RESPONSIVE_FILE" \ + | $_GREP "$registrarID\$" \ + | print_list_endpoints \ + > "$idp_endpoints_non_responsive_list_file" + +exit diff --git a/install.sh b/install.sh index caa68d1..f2e7eee 100755 --- a/install.sh +++ b/install.sh @@ -94,6 +94,7 @@ while read script_file; do done <<SCRIPTS $script_bin/bin/check_idp_error_urls.sh $script_bin/bin/list_local_idp_error_urls.sh +$script_bin/bin/list_local_saml_idp_endpoints.sh $script_bin/bin/probe_saml_idp.sh $script_bin/bin/probe_saml_idps.sh SCRIPTS