diff --git a/demo/complex/tests/main.bats b/demo/complex/tests/main.bats index f980cdf..6ec5782 100755 --- a/demo/complex/tests/main.bats +++ b/demo/complex/tests/main.bats @@ -21,8 +21,8 @@ load ../../../library touch $BATS_TMPDIR/not-started wait_for_midpoint_start complex_midpoint-server_1 complex_midpoint-data_1 wait_for_shibboleth_idp_start complex_idp_1 + wait_for_grouper_ui_start complex_grouper-ui_1 rm $BATS_TMPDIR/not-started -# TODO wait for shibboleth, grouper-ui, (also something other?) } @test "040 Check midPoint health" { @@ -53,7 +53,7 @@ load ../../../library add_object users /tmp/test110.xml rm /tmp/test110.xml search_and_check_object users test110 -# TODO delete user after + delete_object_by_name users test110 } @test "200 Upload objects" { @@ -61,11 +61,26 @@ load ../../../library check_health pwd >&2 ./upload-objects + search_and_check_object objectTemplates template-org-course search_and_check_object objectTemplates template-org-department search_and_check_object objectTemplates template-role-affiliation search_and_check_object objectTemplates template-role-generic-group -# TODO check other objects that were uploaded + + search_and_check_object orgs courses + search_and_check_object orgs departments + + search_and_check_object resources "OpenLDAP (directory)" + search_and_check_object resources "Grouper SQL/MQ" + search_and_check_object resources "SQL SIS courses (sources)" + search_and_check_object resources "SQL SIS persons (sources)" + + search_and_check_object roles metarole-affiliation + search_and_check_object roles metarole-course + search_and_check_object roles metarole-department + search_and_check_object roles metarole-generic-group + search_and_check_object roles role-grouper-sysadmin + search_and_check_object roles role-ldap-basic } @test "210 Test resource" { @@ -81,7 +96,7 @@ load ../../../library add_object tasks midpoint-objects-manual/tasks/task-import-sis-persons.xml search_and_check_object tasks "Import from SIS persons" - wait_for_task_completion 22c2a3d0-0961-4255-9eec-c550a79aeaaa + wait_for_task_completion 22c2a3d0-0961-4255-9eec-c550a79aeaaa 6 10 assert_task_success 22c2a3d0-0961-4255-9eec-c550a79aeaaa search_and_check_object users jsmith @@ -94,10 +109,39 @@ load ../../../library search_and_check_object users amorrison search_and_check_object users wprice search_and_check_object users mroberts - # TODO check in LDAP, check assignments etc + + check_ldap_account_by_user_name jsmith complex_directory_1 + check_ldap_account_by_user_name banderson complex_directory_1 + check_ldap_account_by_user_name kwhite complex_directory_1 + check_ldap_account_by_user_name whenderson complex_directory_1 + check_ldap_account_by_user_name ddavis complex_directory_1 + check_ldap_account_by_user_name cmorrison complex_directory_1 + check_ldap_account_by_user_name danderson complex_directory_1 + check_ldap_account_by_user_name amorrison complex_directory_1 + check_ldap_account_by_user_name wprice complex_directory_1 + check_ldap_account_by_user_name mroberts complex_directory_1 + # TODO check assignments etc } +@test "230 Check 'TestUser230' in Midpoint and LDAP" { + if [ -e $BATS_TMPDIR/not-started ]; then skip 'not started'; fi + check_health + echo "TestUser230Test User230TestUser230password" >/tmp/testuser230.xml + add_object users /tmp/testuser230.xml + rm /tmp/testuser230.xml + search_and_check_object users TestUser230 + + execute_bulk_action tests/resources/bulk-action/recom-role-grouper-sysadmin.xml + execute_bulk_action tests/resources/bulk-action/assign-role-grouper-sysadmin-to-test-user.xml + + check_ldap_account_by_user_name TestUser230 complex_directory_1 + check_of_ldap_membership TestUser230 sysadmingroup complex_directory_1 + + delete_object_by_name users TestUser230 +} + + @test "999 Clean up" { -# skip TEMP + skip TEMP docker-compose down -v } diff --git a/demo/complex/tests/resources/bulk-action/assign-role-grouper-sysadmin-to-test-user.xml b/demo/complex/tests/resources/bulk-action/assign-role-grouper-sysadmin-to-test-user.xml new file mode 100644 index 0000000..c2736da --- /dev/null +++ b/demo/complex/tests/resources/bulk-action/assign-role-grouper-sysadmin-to-test-user.xml @@ -0,0 +1,22 @@ + + + c:UserType + + + c:name + TestUser230 + + + + assign + + role + d48ec05b-fffd-4262-acd3-d9ff63365b62 + + + + diff --git a/demo/complex/tests/resources/bulk-action/recom-role-grouper-sysadmin.xml b/demo/complex/tests/resources/bulk-action/recom-role-grouper-sysadmin.xml new file mode 100644 index 0000000..1356484 --- /dev/null +++ b/demo/complex/tests/resources/bulk-action/recom-role-grouper-sysadmin.xml @@ -0,0 +1,16 @@ + + + c:RoleType + + + name + role-grouper-sysadmin + + + + recompute + + + diff --git a/demo/shibboleth/test.sh b/demo/shibboleth/test.sh deleted file mode 100755 index c23693b..0000000 --- a/demo/shibboleth/test.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -trap 'exitcode=$? ; error "Exiting $0 because of an error ($exitcode) occurred" ; exit $exitcode' ERR - -cd "$(dirname "$0")" -. ../../test/common.sh - -heading "Cleaning up containers and volumes if they exist" -docker-compose down -v || true -ok "Done" -echo - -heading "Composing and starting Shibboleth..." -docker-compose up --build -d -ok "OK" -echo - -# TODO wait for Shib to start - -heading "Composing and starting midPoint..." -pushd ../../midpoint -MPDIR=`pwd` -docker-compose down -v || true -env AUTHENTICATION=shibboleth docker-compose up --build -d -popd -$MPDIR/test/t010-wait-for-start.sh -ok "OK" -echo - -heading "Test 100: Checking health via HTTP..." -$MPDIR/test/t100-check-health.sh -ok "Health check passed" -echo diff --git a/demo/shibboleth/tests/main.bats b/demo/shibboleth/tests/main.bats index 4a791fa..105395d 100755 --- a/demo/shibboleth/tests/main.bats +++ b/demo/shibboleth/tests/main.bats @@ -15,8 +15,7 @@ load ../../../library @test "020 Initialize and start Shibboleth" { docker-compose up -d - # TODO implement wait_for_shibboleth_start - sleep 60 + wait_for_shibboleth_idp_start shibboleth_idp_1 } @test "030 Check health" { diff --git a/library.bash b/library.bash index 0b2172f..f22a759 100644 --- a/library.bash +++ b/library.bash @@ -12,7 +12,7 @@ function generic_wait_for_log () { FAILURE="$4" ADDITIONAL_CONTAINER_NAME=$5 ATTEMPT=0 - MAX_ATTEMPTS=20 + MAX_ATTEMPTS=40 DELAY=10 until [[ $ATTEMPT = $MAX_ATTEMPTS ]]; do @@ -45,28 +45,17 @@ function wait_for_midpoint_start () { generic_wait_for_log $1 "INFO (com.evolveum.midpoint.web.boot.MidPointSpringApplication): Started MidPointSpringApplication in" "midPoint to start" "midPoint did not start" $2 } -# Waits until Shibboleth IDP starts ... TODO refactor using generic waiting function +# Waits until Shibboleth IDP starts function wait_for_shibboleth_idp_start () { - CONTAINER_NAME=$1 - ATTEMPT=0 - MAX_ATTEMPTS=20 - DELAY=10 - - until [[ $ATTEMPT = $MAX_ATTEMPTS ]]; do - ATTEMPT=$((ATTEMPT+1)) - echo "Waiting $DELAY seconds for Shibboleth IDP to start (attempt $ATTEMPT) ..." - sleep $DELAY - docker ps - ( docker logs $CONTAINER_NAME 2>&1 | grep "INFO:oejs.Server:main: Started" ) && return 0 - done + generic_wait_for_log $1 "INFO:oejs.Server:main: Started" "shibboleth idp to start" "shibboleth idp did not start" $2 +} - echo Shibboleth IDP did not start in $(( $MAX_ATTEMPTS * $DELAY )) seconds in $CONTAINER_NAME - echo "========== Container log ==========" - docker logs $CONTAINER_NAME 2>&1 - echo "========== End of the container log ==========" - return 1 +# Waits until Grouper UI starts +function wait_for_grouper_ui_start () { + generic_wait_for_log $1 "INFO org.apache.catalina.startup.Catalina- Server startup in" "grouper ui to start" "grouper ui did not start" $2 } + # Checks the health of midPoint server function check_health () { echo Checking health... @@ -127,45 +116,117 @@ function get_and_check_object () { } # Adds object from a given file -# TODO Returns the OID in OID variable -# it can be found in the following HTTP reader returned: Location: "https://localhost:8443/midpoint/ws/rest/users/85e62669-d36b-41ce-b4f1-1ffdd9f66262" function add_object () { local TYPE=$1 local FILE=$2 + TMPFILE=$(mktemp /tmp/execbulkaction.XXXXXX) echo "Adding to $TYPE from $FILE..." - response=$(curl -k -sD - --silent --write-out "%{http_code}" --user administrator:5ecr3t -H "Content-Type: application/xml" -X POST "https://localhost:8443/midpoint/ws/rest/$TYPE" -d @$FILE) - http_code=$(sed '$!d' <<<"$response") + curl -k -sD - --silent --write-out "%{http_code}" --user administrator:5ecr3t -H "Content-Type: application/xml" -X POST "https://localhost:8443/midpoint/ws/rest/$TYPE" -d @$FILE >$TMPFILE + local HTTP_CODE=$(sed '$!d' $TMPFILE) - if [ "$http_code" -eq 201 ] || [ "$http_code" -eq 202 ]; then - headers=$(sed -n '1,/^\r$/p' <<<"$response") + if [ "$HTTP_CODE" -eq 201 ] || [ "$HTTP_CODE" -eq 202 ]; then + + OID=$(grep -oP "Location: \K.*" $TMPFILE | awk -F "$TYPE/" '{print $2}') || (echo "Couldn't extract oid from file:" ; cat $TMPFILE ; rm $TMPFILE; return 1) - # get the real Location - location=$(grep -oP 'Location: \K.*' <<<"$headers") - oid=$(sed 's/.*\///' <<<"$location") - - echo "Oid created object: $oid" + echo "Oid created object: $OID" + rm $TMPFILE return 0 else - echo "Error code: $http_code" - if [ "$http_code" -eq 500 ]; then + echo "Error code: $HTTP_CODE" + if [ "$HTTP_CODE" -ge 500 ]; then echo "Error message: Internal server error. Unexpected error occurred, if necessary please contact system administrator." else - error_message=$(grep 'message' <<<"$response" | head -1 | awk -F">" '{print $2}' | awk -F"<" '{print $1}') - echo "Error message: $error_message" + local ERROR_MESSAGE=$(xmllint --xpath "/*/*[local-name()='error']/text()" $TMPFILE) || (echo "Couldn't extract error message from file:" ; cat $TMPFILE ; rm $TMPFILE; return 1) + echo "Error message: $ERROR_MESSAGE" fi + rm $TMPFILE + return 1 + fi +} + +function execute_bulk_action () { + local FILE=$1 + echo "Executing bulk action from $FILE..." + TMPFILE=$(mktemp /tmp/execbulkaction.XXXXXX) + + (curl -k --silent --write-out "%{http_code}" --user administrator:5ecr3t -H "Content-Type: application/xml" -X POST "https://localhost:8443/midpoint/ws/rest/rpc/executeScript" -d @$FILE >$TMPFILE) || (echo "Midpoint logs: " ; docker logs "complex_midpoint-server_1" ; return 1) + local HTTP_CODE=$(sed '$!d' $TMPFILE) + sed -i '$ d' $TMPFILE + + if [ "$HTTP_CODE" -eq 200 ]; then + + local STATUS=$(xmllint --xpath "/*/*/*[local-name()='status']/text()" $TMPFILE) || (echo "Couldn't extract status from file:" ; cat $TMPFILE ; rm $TMPFILE; return 1) + if [ $STATUS = "success" ]; then + local CONSOLE_OUTPUT=$(xmllint --xpath "/*/*/*[local-name()='consoleOutput']/text()" $TMPFILE) || (echo "Couldn't extract console output from file:" ; cat $TMPFILE ; rm $TMPFILE; return 1) + echo "Console output: $CONSOLE_OUTPUT" + rm $TMPFILE + return 0 + else + echo "Bulk action status is not OK: $STATUS" + local CONSOLE_OUTPUT=$(xmllint --xpath "/*/*/*[local-name()='consoleOutput']/text()" $TMPFILE) || (echo "Couldn't extract console output from file:" ; cat $TMPFILE ; rm $TMPFILE; return 1) + echo "Console output: $CONSOLE_OUTPUT" + rm $TMPFILE + return 1 + fi + + else + echo "Error code: $HTTP_CODE" + if [ "$HTTP_CODE" -ge 500 ]; then + echo "Error message: Internal server error. Unexpected error occurred, if necessary please contact system administrator." + else + local ERROR_MESSAGE=$(xmllint --xpath "/*/*[local-name()='error']/text()" $TMPFILE) || (echo "Couldn't extract error message from file:" ; cat $TMPFILE ; rm $TMPFILE; return 1) + echo "Error message: $ERROR_MESSAGE" + fi + rm $TMPFILE + return 1 + fi +} + +function delete_object_by_name () { + local TYPE=$1 + local NAME=$2 + search_objects_by_name users $NAME + local OID=$(xmllint --xpath "/*/*[local-name()='object']/@oid" $SEARCH_RESULT_FILE | awk -F"\"" '{print $2}' ) || (echo "Couldn't extract oid from file:" ; cat $SEARCH_RESULT_FILE ; rm $SEARCH_RESULT_FILE; return 1) + delete_object $TYPE $OID +} + +function delete_object () { + local TYPE=$1 + local OID=$2 + echo "Deleting object with type $TYPE and oid $OID..." + TMPFILE=$(mktemp /tmp/delete.XXXXXX) + + curl -k --silent --write-out "%{http_code}" --user administrator:5ecr3t -H "Content-Type: application/xml" -X DELETE "https://localhost:8443/midpoint/ws/rest/$TYPE/$OID" >$TMPFILE + local HTTP_CODE=$(sed '$!d' $TMPFILE) + sed -i '$ d' $TMPFILE + + if [ "$HTTP_CODE" -eq 204 ]; then + + echo "Object with type $TYPE and oid $OID was deleted" + rm $TMPFILE + return 0 + else + echo "Error code: $HTTP_CODE" + if [ "$HTTP_CODE" -ge 500 ]; then + echo "Error message: Internal server error. Unexpected error occurred, if necessary please contact system administrator." + else + local ERROR_MESSAGE=$(xmllint --xpath "/*/*[local-name()='error']/text()" $TMPFILE) || (echo "Couldn't extract error message from file:" ; cat $TMPFILE ; rm $TMPFILE; return 1) + echo "Error message: $ERROR_MESSAGE" + fi + rm $TMPFILE return 1 fi - #curl -k --user administrator:5ecr3t -H "Content-Type: application/xml" -X POST "https://localhost:8443/midpoint/ws/rest/$TYPE" -d @$FILE || return 1 - #TODO check the returned XML } + + # Tries to find an object with a given name # Results of the search are in the $SEARCH_RESULT_FILE # TODO check if the result is valid (i.e. not an error) - return 1 if invalid, otherwise return 0 ("no objects" is considered OK here) function search_objects_by_name () { - TYPE=$1 - NAME="$2" + local TYPE=$1 + local NAME="$2" TMPFILE=$(mktemp /tmp/search.XXXXXX) curl -k --write-out %{http_code} --user administrator:5ecr3t -H "Content-Type: application/xml" -X POST "https://localhost:8443/midpoint/ws/rest/$TYPE/search" -d @- << EOF >$TMPFILE || (rm $TMPFILE ; return 1) @@ -178,15 +239,22 @@ function search_objects_by_name () { EOF - SEARCH_RESULT_FILE=$TMPFILE - - http_code=$(sed '$!d' <<<"$(cat $SEARCH_RESULT_FILE)") + local HTTP_CODE=$(sed '$!d' <<<"$(cat $TMPFILE)") + sed -i '$ d' $TMPFILE + cat $TMPFILE - sed -i '$ d' $SEARCH_RESULT_FILE - cat $SEARCH_RESULT_FILE - if [ "$http_code" -eq 200 ]; then + if [ "$HTTP_CODE" -eq 200 ]; then + SEARCH_RESULT_FILE=$TMPFILE return 0 else + echo "Error code: $HTTP_CODE" + if [ "$HTTP_CODE" -ge 500 ]; then + echo "Error message: Internal server error. Unexpected error occurred, if necessary please contact system administrator." + else + local ERROR_MESSAGE=$(xmllint --xpath "/*/*[local-name()='error']/text()" $TMPFILE) || (echo "Couldn't extract error message from file:" ; cat $TMPFILE ; rm $TMPFILE; return 1) + echo "Error message: $ERROR_MESSAGE" + fi + rm $SEARCH_RESULT_FILE return 1 fi } @@ -244,6 +312,84 @@ function assert_task_success () { function wait_for_task_completion () { local OID=$1 - sleep 60 # TODO + local ATTEMPT=0 + local MAX_ATTEMPTS=$2 + local DELAY=$3 + + until [[ $ATTEMPT = $MAX_ATTEMPTS ]]; do + ATTEMPT=$((ATTEMPT+1)) + echo "Waiting $DELAY seconds for task with oid $OID to finish (attempt $ATTEMPT) ..." + sleep $DELAY + get_object tasks $OID + TASK_EXECUTION_STATUS=$(xmllint --xpath "/*/*[local-name()='executionStatus']/text()" $TMPFILE) || (echo "Couldn't extract task status from task $OID" ; cat $TMPFILE ; rm $TMPFILE ; return 1) + if [[ $TASK_EXECUTION_STATUS = "suspended" ]] || [[ $TASK_EXECUTION_STATUS = "closed" ]]; then + echo "Task $OID is finished" + rm $TMPFILE + return 0 + fi + done + rm $TMPFILE + echo Task with $OID did not finish in $(( $MAX_ATTEMPTS * $DELAY )) seconds + return 1 +} + + +#search LDAP accout by uid +function search_ldap_object_by_filter () { + local BASE_CONTEXT_FOR_SEARCH=$1 + local FILTER="$2" + local LDAP_CONTAINER=$3 + TMPFILE=$(mktemp /tmp/ldapsearch.XXXXXX) + + docker exec $LDAP_CONTAINER ldapsearch -h localhost -p 389 -D "cn=Directory Manager" -w password -b "$BASE_CONTEXT_FOR_SEARCH" "($FILTER)" >$TMPFILE || (rm $TMPFILE ; return 1) + LDAPSEARCH_RESULT_FILE=$TMPFILE return 0 } + +function check_ldap_account_by_user_name () { + local NAME="$1" + local LDAP_CONTAINER=$2 + search_ldap_object_by_filter "ou=people,dc=internet2,dc=edu" "uid=$NAME" $LDAP_CONTAINER + search_objects_by_name users $NAME + + local MP_FULL_NAME=$(xmllint --xpath "/*/*/*[local-name()='fullName']/text()" $SEARCH_RESULT_FILE) || (echo "Couldn't extract user fullName from file:" ; cat $SEARCH_RESULT_FILE ; rm $SEARCH_RESULT_FILE ; rm $LDAPSEARCH_RESULT_FILE ; return 1) + local MP_GIVEN_NAME=$(xmllint --xpath "/*/*/*[local-name()='givenName']/text()" $SEARCH_RESULT_FILE) || (echo "Couldn't extract user givenName from file:" ; cat $SEARCH_RESULT_FILE ; rm $SEARCH_RESULT_FILE ; rm $LDAPSEARCH_RESULT_FILE ; return 1) + local MP_FAMILY_NAME=$(xmllint --xpath "/*/*/*[local-name()='familyName']/text()" $SEARCH_RESULT_FILE) || (echo "Couldn't extract user familyName from file:" ; cat $SEARCH_RESULT_FILE ; rm $SEARCH_RESULT_FILE ; rm $LDAPSEARCH_RESULT_FILE ; return 1) + + local LDAP_CN=$(grep -oP "cn: \K.*" $LDAPSEARCH_RESULT_FILE) || (echo "Couldn't extract user cn from file:" ; cat $LDAPSEARCH_RESULT_FILE ; rm $SEARCH_RESULT_FILE ; rm $LDAPSEARCH_RESULT_FILE ; return 1) + local LDAP_GIVEN_NAME=$(grep -oP "givenName: \K.*" $LDAPSEARCH_RESULT_FILE) || (echo "Couldn't extract user givenName from file:" ; cat $LDAPSEARCH_RESULT_FILE ; rm $SEARCH_RESULT_FILE ; rm $LDAPSEARCH_RESULT_FILE ; return 1) + local LDAP_SN=$(grep -oP "sn: \K.*" $LDAPSEARCH_RESULT_FILE) || (echo "Couldn't extract user sn from file:" ; cat $LDAPSEARCH_RESULT_FILE ; rm $SEARCH_RESULT_FILE ; rm $LDAPSEARCH_RESULT_FILE ; return 1) + + rm $SEARCH_RESULT_FILE + rm $LDAPSEARCH_RESULT_FILE + + if [[ $MP_FULL_NAME = $LDAP_CN ]] && [[ $MP_GIVEN_NAME = $LDAP_GIVEN_NAME ]] && [[ $MP_FAMILY_NAME = $LDAP_SN ]]; then + return 0 + fi + + echo "User in Midpoint and LDAP Account with uid $NAME are not same" + return 1 +} + +function check_of_ldap_membership () { + local NAME_OF_USER="$1" + local NAME_OF_GROUP="$2" + local LDAP_CONTAINER=$3 + search_ldap_object_by_filter "ou=people,dc=internet2,dc=edu" "uid=$NAME_OF_USER" $LDAP_CONTAINER + + local LDAP_ACCOUNT_DN=$(grep -oP "dn: \K.*" $LDAPSEARCH_RESULT_FILE) || (echo "Couldn't extract user dn from file:" ; cat $LDAPSEARCH_RESULT_FILE ; rm $LDAPSEARCH_RESULT_FILE ; return 1) + + search_ldap_object_by_filter "ou=groups,dc=internet2,dc=edu" "cn=$NAME_OF_GROUP" $LDAP_CONTAINER + + local LDAP_MEMBERS_DNS=$(grep -oP "uniqueMember: \K.*" $LDAPSEARCH_RESULT_FILE) || (echo "Couldn't extract user uniqueMember from file:" ; cat $LDAPSEARCH_RESULT_FILE ; rm $LDAPSEARCH_RESULT_FILE ; return 1) + + rm $LDAPSEARCH_RESULT_FILE + + if [[ $LDAP_MEMBERS_DNS =~ $LDAP_ACCOUNT_DN ]]; then + return 0 + fi + + echo "LDAP Account with uid $NAME_OF_USER is not member of LDAP Group $NAME_OF_GROUP" + return 1 +} +