diff --git a/.vscode/launch.json b/.vscode/launch.json index 2308cfec65..577a30c3f5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -36,6 +36,7 @@ "env": { "OTLP_EXPORT_ENABLED": "false" }, + "envFile": "${workspaceFolder}/example-services/.env" }, { "name": "Blueapi Controller", diff --git a/tests/system_tests/compose.yaml b/tests/system_tests/compose.yaml index 7a73ef5306..d7772edd63 100644 --- a/tests/system_tests/compose.yaml +++ b/tests/system_tests/compose.yaml @@ -64,6 +64,7 @@ services: network_mode: host # Port 8181 volumes: - "./services/opa_config:/mnt" + - "/scratch/ckk43848/authz/policy:/mnt/policy/" environment: - ISSUER=http://localhost:8081/realms/master entrypoint: "sh /mnt/entrypoint.sh" diff --git a/tests/system_tests/config.yaml b/tests/system_tests/config.yaml index 9a35e517cb..a55417d4cf 100644 --- a/tests/system_tests/config.yaml +++ b/tests/system_tests/config.yaml @@ -25,3 +25,8 @@ oidc: issuer: "http://localhost:8081/realms/master" client_id: "ixx-cli-blueapi" client_audience: "ixx-blueapi" +opa: + root: "http://localhost:8181/v1/data/diamond/policy/" + tiled_service_account_check: "blueapi/tiled_service_account_for_beamline" + submit_task_check: "blueapi/write_to_beamline_visit" + admin_check: "admin/admin" diff --git a/tests/system_tests/services/keycloak_config/mappers-template.json b/tests/system_tests/services/keycloak_config/mappers-template.json index 6196af5f19..27be3fb865 100644 --- a/tests/system_tests/services/keycloak_config/mappers-template.json +++ b/tests/system_tests/services/keycloak_config/mappers-template.json @@ -1,14 +1,18 @@ { "protocolMappers": [ { - "name": "fedid", + "name": "username", "protocol": "openid-connect", - "protocolMapper": "oidc-hardcoded-claim-mapper", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, "config": { + "aggregate.attrs": "false", "introspection.token.claim": "true", - "claim.value": "__CLAIM_VALUE__", + "multivalued": "false", "userinfo.token.claim": "true", + "user.attribute": "username", "id.token.claim": "true", + "lightweight.claim": "false", "access.token.claim": "true", "claim.name": "fedid", "jsonType.label": "String" diff --git a/tests/system_tests/services/keycloak_config/service-account.json b/tests/system_tests/services/keycloak_config/service-account-beamline.json similarity index 100% rename from tests/system_tests/services/keycloak_config/service-account.json rename to tests/system_tests/services/keycloak_config/service-account-beamline.json diff --git a/tests/system_tests/services/keycloak_config/service-account-user.json b/tests/system_tests/services/keycloak_config/service-account-user.json new file mode 100644 index 0000000000..6196af5f19 --- /dev/null +++ b/tests/system_tests/services/keycloak_config/service-account-user.json @@ -0,0 +1,28 @@ +{ + "protocolMappers": [ + { + "name": "fedid", + "protocol": "openid-connect", + "protocolMapper": "oidc-hardcoded-claim-mapper", + "config": { + "introspection.token.claim": "true", + "claim.value": "__CLAIM_VALUE__", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "fedid", + "jsonType.label": "String" + } + }, + { + "name": "audience-mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true", + "included.custom.audience": "__AUDIENCE__" + } + } + ] +} diff --git a/tests/system_tests/services/keycloak_config/startup.sh b/tests/system_tests/services/keycloak_config/startup.sh index 6ca37c2953..32b22f3e4d 100644 --- a/tests/system_tests/services/keycloak_config/startup.sh +++ b/tests/system_tests/services/keycloak_config/startup.sh @@ -4,11 +4,14 @@ export PATH=$PATH:/opt/keycloak/bin # --- Config --- export KC_CLI_PASSWORD="admin" SERVER="http://localhost:8080" -TEMPLATE="/tmp/config/mappers-template.json" +GENERAL_TEMPLATE="/tmp/config/mappers-template.json" +BEAMLINE_SERVICE_TEMPLATE="/tmp/config/service-account-beamline.json" +USER_SERVICE_TEMPLATE="/tmp/config/service-account-user.json" +REALM="master" # Wait for Keycloak -sleep 30 -until kcadm.sh config credentials --server $SERVER --realm master --user admin; do sleep 1; done +sleep 5 +until kcadm.sh config credentials --server $SERVER --realm $REALM --user admin; do sleep 3; done # Cleanup logic for type in "Allowed Protocol Mapper Types" "Allowed Client Scopes"; do @@ -17,14 +20,30 @@ for type in "Allowed Protocol Mapper Types" "Allowed Client Scopes"; do done done -kcreg.sh config credentials --server $SERVER --realm master --user admin + +USERS=("alice:alice" "bob:bob") + +for entry in "${USERS[@]}"; do + # Split the string into username and password + username="${entry%%:*}" + password="${entry##*:}" + # Create the user + kcadm.sh create users -r "$REALM" -s username="$username" -s enabled=true + # Set the password + kcadm.sh set-password -r "$REALM" --username "$username" --new-password "$password" + echo "User '$username' created successfully." +done + +kcreg.sh config credentials --server $SERVER --realm $REALM --user admin # --- Client Creation Function --- -# Args: client_id, audience, extra_flags +# Args: client_id, audience, type, fedid, extra_flags create_client() { local client_id=$1 local aud=$2 - shift 2 # The rest are Keycloak attributes (-s key=value) + local type=$3 + local fedid=$4 + shift 4 # The rest are Keycloak attributes (-s key=value) if kcreg.sh get "$client_id" >/dev/null 2>&1; then echo ">> Skipping $client_id (exists)" @@ -34,11 +53,13 @@ create_client() { echo ">> Creating $client_id..." local tmpfile=$(mktemp) - if [[ "$client_id" == "tiled-writer" ]]; then - cp /tmp/config/service-account.json "$tmpfile" + if [[ "$type" == "BEAMLINE_SERVICE_ACCOUNT" ]]; then + cp $BEAMLINE_SERVICE_TEMPLATE "$tmpfile" + elif [[ "$type" == "USER_SERVICE_ACCOUNT" ]]; then + sed "s/__AUDIENCE__/$aud/g; s/__CLAIM_VALUE__/$fedid/g" "$USER_SERVICE_TEMPLATE" > "$tmpfile" else # Use sed to replace placeholders in the JSON template - sed "s/__AUDIENCE__/$aud/g; s/__CLAIM_VALUE__/alice/g" "$TEMPLATE" > "$tmpfile" + sed "s/__AUDIENCE__/$aud/g;" "$GENERAL_TEMPLATE" > "$tmpfile" fi kcreg.sh create -x -s clientId="$client_id" -f "$tmpfile" "$@" @@ -47,31 +68,39 @@ create_client() { # --- Create Clients --- -# System Test -create_client "system-test-blueapi" "ixx-blueapi" \ - -s secret="secret" -s standardFlowEnabled=false -s serviceAccountsEnabled=true -s 'redirectUris=["/*"]' - # ixx CLI -create_client "ixx-cli-blueapi" "ixx-blueapi" \ +create_client "ixx-cli-blueapi" "ixx-blueapi" "" "" \ -s standardFlowEnabled=false -s publicClient=true -s 'redirectUris=["/*"]' \ -s 'attributes={"frontchannel.logout.session.required":"true","oauth2.device.authorization.grant.enabled":"true","use.refresh.tokens":"true","backchannel.logout.session.required":"true"}' # ixx BlueAPI -create_client "ixx-blueapi" "ixx-blueapi" \ +create_client "ixx-blueapi" "ixx-blueapi" "" "" \ -s standardFlowEnabled=true -s secret="blueapi-secret" -s rootUrl="http://localhost:4180" \ -s 'redirectUris=["http://localhost:4180/*"]' \ -s 'attributes={"frontchannel.logout.session.required":"true","use.refresh.tokens":"true"}' # Tiled -create_client "tiled" "tiled" \ +create_client "tiled" "tiled" "" "" \ -s standardFlowEnabled=true -s secret="tiled-secret" -s rootUrl="http://localhost:4181" \ -s 'redirectUris=["http://localhost:4181/*"]' # Tiled CLI -create_client "tiled-cli" "tiled" \ +create_client "tiled-cli" "tiled" "" ""\ -s standardFlowEnabled=false -s publicClient=true -s 'redirectUris=["/*"]' \ -s 'attributes={"frontchannel.logout.session.required":"true","oauth2.device.authorization.grant.enabled":"true","use.refresh.tokens":"true","backchannel.logout.session.required":"true"}' # Service account tiled-writer -create_client "tiled-writer" "" \ +create_client "tiled-writer" "" "BEAMLINE_SERVICE_ACCOUNT" "" \ + -s secret="secret" -s standardFlowEnabled=false -s serviceAccountsEnabled=true -s 'redirectUris=["/*"]' + +# System Test admin +create_client "system-test-blueapi-admin" "ixx-blueapi" "USER_SERVICE_ACCOUNT" "admin" \ + -s secret="secret" -s standardFlowEnabled=false -s serviceAccountsEnabled=true -s 'redirectUris=["/*"]' + +# System Test alice +create_client "system-test-blueapi-alice" "ixx-blueapi" "USER_SERVICE_ACCOUNT" "alice"\ + -s secret="secret" -s standardFlowEnabled=false -s serviceAccountsEnabled=true -s 'redirectUris=["/*"]' + +# System Test bob +create_client "system-test-blueapi-bob" "ixx-blueapi" "USER_SERVICE_ACCOUNT" "bob"\ -s secret="secret" -s standardFlowEnabled=false -s serviceAccountsEnabled=true -s 'redirectUris=["/*"]' diff --git a/tests/system_tests/services/opa_config/entrypoint.sh b/tests/system_tests/services/opa_config/entrypoint.sh index ff5d3b719d..73e271162b 100644 --- a/tests/system_tests/services/opa_config/entrypoint.sh +++ b/tests/system_tests/services/opa_config/entrypoint.sh @@ -1,3 +1,3 @@ #!/bin/sh /opa build -b /mnt/opa_data -o /tmp/bundle.tar.gz -/opa run --server --addr localhost:8181 -b /tmp/bundle.tar.gz --config-file /mnt/config.yaml +/opa run --server --addr localhost:8181 -b /tmp/bundle.tar.gz /mnt/policy/ diff --git a/tests/system_tests/services/opa_config/opa_data/data.json b/tests/system_tests/services/opa_config/opa_data/data.json index 165b8241d4..20566d8014 100644 --- a/tests/system_tests/services/opa_config/opa_data/data.json +++ b/tests/system_tests/services/opa_config/opa_data/data.json @@ -11,7 +11,8 @@ "proposals": { "12345": { "sessions": { - "1": 1 + "1": 1, + "2": 2 } } }, @@ -20,17 +21,32 @@ "beamline": "adsim", "proposal_number": 12345, "visit_number": 1 + }, + "2": { + "beamline": "adsim", + "proposal_number": 12345, + "visit_number": 2 } }, "subjects": { + "admin": { + "permissions": ["super_admin"], + "proposals": [], + "sessions": [] + }, "alice": { "permissions": [], - "proposals": [ - 12345 - ], + "proposals": [], "sessions": [ 1 ] + }, + "bob": { + "permissions": [], + "proposals": [], + "sessions": [ + 2 + ] } } } diff --git a/tests/system_tests/test_blueapi_system.py b/tests/system_tests/test_blueapi_system.py index c470f08448..2243da3c33 100644 --- a/tests/system_tests/test_blueapi_system.py +++ b/tests/system_tests/test_blueapi_system.py @@ -34,7 +34,6 @@ WorkerTask, ) from blueapi.worker.event import ( - TaskError, TaskResult, TaskStatus, WorkerEvent, @@ -94,12 +93,12 @@ def load_config(path: Path) -> ApplicationConfig: return loader.load() -def get_access_token() -> str: +def get_access_token(user: str = "alice") -> str: token_url = "http://localhost:8081/realms/master/protocol/openid-connect/token" response = requests.post( token_url, data={ - "client_id": "system-test-blueapi", + "client_id": "system-test-blueapi-" + user, "client_secret": "secret", "grant_type": "client_credentials", }, @@ -612,14 +611,8 @@ def on_event(event: AnyEvent) -> None: if event.name == "stream_resource": resource.put_nowait(event.doc) - outcome = client_with_stomp.run_task(task, on_event) - assert outcome.task_failed - assert outcome.task_complete - assert isinstance(outcome.result, TaskError) - assert outcome.result.type == "ClientError" - assert outcome.result.message.startswith( - "403: Access policy rejects the provided access blob" - ) + with pytest.raises(UnauthorisedAccessError, match="Not authorized to submit task"): + client_with_stomp.run_task(task, on_event) # Regression test for #1480