Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions tests/system_tests/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ services:
- KC_HOSTNAME=http://localhost:8081
command: ["start-dev"]
volumes:
- ./services/keycloak_config/startup.sh:/tmp/startup.sh
- ./services/keycloak_config/:/tmp/config/
post_start:
- command: bash /tmp/startup.sh
- command: bash /tmp/config/startup.sh
ports:
- 8081:8080
healthcheck:
Expand Down Expand Up @@ -59,3 +59,31 @@ services:
environment:
- ISSUER=http://localhost:8081/realms/master
entrypoint: "sh /mnt/entrypoint.sh"

blueapi-oauth2-proxy:
network_mode: host
image: "quay.io/oauth2-proxy/oauth2-proxy:v7.13.0"
volumes:
- ./services/blueapi-oauth2-proxy/:/opt/config
command:
[
"--alpha-config=/opt/config/oauth2-alpha.yaml",
"--config=/opt/config/oauth2-proxy.cfg",
]
depends_on:
keycloak:
condition: service_healthy

tiled-oauth2-proxy:
network_mode: host
image: "quay.io/oauth2-proxy/oauth2-proxy:v7.13.0"
volumes:
- ./services/tiled-oauth2-proxy/:/opt/config
command:
[
"--alpha-config=/opt/config/oauth2-alpha.yaml",
"--config=/opt/config/oauth2-proxy.cfg",
]
depends_on:
keycloak:
condition: service_healthy
6 changes: 6 additions & 0 deletions tests/system_tests/config-cli.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
stomp:
enabled: true
auth:
username: guest
password: guest
url: tcp://localhost:61613
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
injectRequestHeaders:
- name: Authorization
values:
- claim: access_token
prefix: "Bearer "
providers:
- provider: oidc
clientID: ixx-blueapi
clientSecret: blueapi-secret
id: authn
oidcConfig:
audienceClaims: ["aud"]
extraAudiences: ["account"]
emailClaim: sub
insecureAllowUnverifiedEmail: true
insecureSkipNonce: true
issuerURL: http://localhost:8081/realms/master
server:
BindAddress: localhost:4180
SecureBindAddress: ""
TLS: null
upstreamConfig:
proxyRawPath: true
upstreams:
- id: ixx-blueapi
path: /
uri: http://localhost:8000
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## OAuth2 Proxy Config File
## https://github.com/oauth2-proxy/oauth2-proxy

email_domains = [
"*"
]

skip_auth_routes=[
"GET=^/config/oidc",
"GET=^/healthz"
]

skip_jwt_bearer_tokens = true
skip_provider_button = true

cookie_secret = "Dhf3pGspLQ5DjtzIz_la8mq2MkCsXzeV"
cookie_expire="30m"
cookie_refresh="1m"
cookie_secure = false
whitelist_domains = "localhost:8081"
Original file line number Diff line number Diff line change
@@ -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__"
}
}
]
}
123 changes: 59 additions & 64 deletions tests/system_tests/services/keycloak_config/startup.sh
Original file line number Diff line number Diff line change
@@ -1,74 +1,69 @@
#!/bin/bash
export PATH=$PATH:/opt/keycloak/bin

# --- Config ---
export KC_CLI_PASSWORD="admin"
SERVER="http://localhost:8080"
TEMPLATE="/tmp/config/mappers-template.json"

# Wait for Keycloak
sleep 30
while ! kcadm.sh config credentials --server http://localhost:8080 --realm master --user admin --password admin; do
sleep 1
done
until kcadm.sh config credentials --server $SERVER --realm master --user admin; do sleep 1; done

allowed_protocol_mappers=$(kcadm.sh get components -q name="Allowed Protocol Mapper Types" --fields id --format csv --noquotes)
allowed_client_scopes=$(kcadm.sh get components -q name="Allowed Client Scopes" --fields id --format csv --noquotes)
for i in $allowed_protocol_mappers $allowed_client_scopes;do
kcadm.sh delete components/$i
# Cleanup logic
for type in "Allowed Protocol Mapper Types" "Allowed Client Scopes"; do
for id in $(kcadm.sh get components -q name="$type" --fields id --format csv --noquotes); do
kcadm.sh delete components/$id
done
done

kcreg.sh config credentials --server http://localhost:8080 --realm master --user admin --password admin
kcreg.sh config credentials --server $SERVER --realm master --user admin

protocolMappers='{
"protocolMappers": [
{
"name": "fedid",
"protocol": "openid-connect",
"protocolMapper": "oidc-hardcoded-claim-mapper",
"config": {
"introspection.token.claim": "true",
"claim.value": "alice",
"userinfo.token.claim": "true",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "fedid",
"jsonType.label": "String"
}
},
{
"name": "blueapi",
"protocol": "openid-connect",
"protocolMapper": "oidc-audience-mapper",
"config": {
"introspection.token.claim": "true",
"access.token.claim": "true",
"included.custom.audience": "blueapi"
}
}
]
}'
# --- Client Creation Function ---
# Args: client_id, audience, extra_flags
create_client() {
local client_id=$1
local aud=$2
shift 2 # The rest are Keycloak attributes (-s key=value)

if kcreg.sh get "$client_id" >/dev/null 2>&1; then
echo ">> Skipping $client_id (exists)"
return
fi

for client in "system-test-blueapi" "ixx-cli-blueapi"; do
if ! kcreg.sh get "$client" >/dev/null 2>&1; then
tmpfile=$(mktemp)
echo $protocolMappers > $tmpfile
case $client in
"system-test-blueapi")
kcreg.sh create -x \
-s clientId=$client \
-s secret="secret" \
-s standardFlowEnabled=false \
-s serviceAccountsEnabled=true \
-s 'redirectUris=["/*"]' \
-s attributes='{"access.token.lifespan":"86400"}' \
-f $tmpfile
;;
"ixx-cli-blueapi")
kcreg.sh create -x \
-s clientId=$client \
-s standardFlowEnabled=false \
-s publicClient=true \
-s 'redirectUris=["/*"]' \
-s 'attributes={"access.token.lifespan":"86400","frontchannel.logout.session.required":"true","oauth2.device.authorization.grant.enabled":"true","use.refresh.tokens":"true","backchannel.logout.session.required":"true"}' \
-f $tmpfile
;;
esac
rm $tmpfile
fi
done
echo ">> Creating $client_id..."
local tmpfile=$(mktemp)

# Use sed to replace placeholders in the JSON template
sed "s/__AUDIENCE__/$aud/g; s/__CLAIM_VALUE__/alice/g" "$TEMPLATE" > "$tmpfile"

kcreg.sh create -x -s clientId="$client_id" -f "$tmpfile" "$@"
rm "$tmpfile"
}

# --- 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" \
-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" \
-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","standard.token.exchange.enabled": "true","standard.token.exchange.enableRefreshRequestedTokenType": "SAME_SESSION"}'

# 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" \
-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"}'
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
injectRequestHeaders:
- name: Authorization
values:
- claim: access_token
prefix: "Bearer "
providers:
- provider: oidc
clientID: tiled
clientSecret: tiled-secret
id: authn
oidcConfig:
audienceClaims: ["aud"]
extraAudiences: ["account"]
emailClaim: sub
insecureAllowUnverifiedEmail: true
insecureSkipNonce: true
issuerURL: http://localhost:8081/realms/master
server:
BindAddress: localhost:4181
SecureBindAddress: ""
TLS: null
upstreamConfig:
proxyRawPath: true
upstreams:
- id: tiled
path: /
uri: http://localhost:8407
18 changes: 18 additions & 0 deletions tests/system_tests/services/tiled-oauth2-proxy/oauth2-proxy.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## OAuth2 Proxy Config File
## https://github.com/oauth2-proxy/oauth2-proxy

email_domains = [
"*"
]

skip_auth_routes=[
"GET=^/api/v1",
]

skip_jwt_bearer_tokens = true
skip_provider_button = true
cookie_secret = "isNsE3dWf1jum4BrqaU7PvXmwCjaqNQJ4sfAWTfuhLY="
cookie_expire="30m"
cookie_refresh="1m"
cookie_secure = false
whitelist_domains = "localhost:8081"
23 changes: 8 additions & 15 deletions tests/system_tests/test_blueapi_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import pytest
import requests
from bluesky_stomp.models import BasicAuthentication
from pydantic import TypeAdapter
from requests.exceptions import ConnectionError

Expand All @@ -21,7 +20,6 @@
ApplicationConfig,
ConfigLoader,
OIDCConfig,
StompConfig,
)
from blueapi.core.bluesky_types import DataEvent
from blueapi.service.model import (
Expand Down Expand Up @@ -84,6 +82,12 @@
OIDC_TOKEN_ENDPOINT = KEYCLOAK_BASE_URL + "realms/master/protocol/openid-connect/token"


def load_config(path: Path) -> ApplicationConfig:
loader = ConfigLoader(ApplicationConfig)
loader.use_values_from_yaml(path)
return loader.load()


@pytest.fixture
def client_without_auth() -> Generator[BlueapiClient]:
with patch(
Expand Down Expand Up @@ -116,12 +120,7 @@ def client_with_stomp() -> Generator[BlueapiClient]:
return_value=mock_session_manager,
):
yield BlueapiClient.from_config(
config=ApplicationConfig(
stomp=StompConfig(
enabled=True,
auth=BasicAuthentication(username="guest", password="guest"), # type: ignore
)
)
config=load_config(_DATA_PATH / "config-cli.yaml")
)


Expand Down Expand Up @@ -185,15 +184,9 @@ def clean_existing_tasks(client: BlueapiClient):
yield


@pytest.fixture(scope="module")
def server_config() -> ApplicationConfig:
loader = ConfigLoader(ApplicationConfig)
loader.use_values_from_yaml(Path("tests", "system_tests", "config.yaml"))
return loader.load()


@pytest.fixture(autouse=True, scope="module")
def reset_numtracker(server_config: ApplicationConfig):
server_config = load_config(Path(_DATA_PATH, "config.yaml"))
nt_url = server_config.numtracker.url # type: ignore - if numtracker is None we should fail
requests.post(
str(nt_url),
Expand Down
Loading