|
| 1 | +<!-- |
| 2 | +SPDX-FileCopyrightText: 2025 Deutsche Telekom AG |
| 3 | +
|
| 4 | +SPDX-License-Identifier: Apache-2.0 |
| 5 | +--> |
| 6 | + |
| 7 | +# Test Suite Documentation |
| 8 | + |
| 9 | +## Overview |
| 10 | +This directory contains integration tests for the kong-plugin-jwt-keycloak plugin. Tests are executed in Docker containers with a full Kong + Keycloak environment. |
| 11 | + |
| 12 | +## Running Tests |
| 13 | +```bash |
| 14 | +# Start all services |
| 15 | +docker compose up -d |
| 16 | + |
| 17 | +# Run the complete test suite |
| 18 | +docker compose up tests |
| 19 | +``` |
| 20 | + |
| 21 | +## Test Files |
| 22 | + |
| 23 | +### Core Test Scripts |
| 24 | +- **`_env.sh`**: Environment setup and helper functions |
| 25 | + - Sets up environment variables (Kong URL, Keycloak URL, etc.) |
| 26 | + - Provides `wait_for_kong()` and `wait_for_keycloak()` functions |
| 27 | + - Includes `retry_test_after_plugin_change()` helper for testing with retries |
| 28 | + |
| 29 | +- **`run_tests.sh`**: Main test orchestrator |
| 30 | + - Runs all test phases in sequence |
| 31 | + - Exits on first failure |
| 32 | + |
| 33 | +- **`run_unit_tests.sh`**: Lua unit test runner |
| 34 | + - Executes busted unit tests from `spec/` directory |
| 35 | + |
| 36 | +### Setup Scripts |
| 37 | +- **`configure_keycloak.sh`**: Keycloak configuration |
| 38 | +Sets up Keycloak for testing: |
| 39 | +- Creates test realm with configured signing algorithm |
| 40 | +- Creates test client with OAuth2 client credentials flow |
| 41 | +- Configures client with ES256 signature algorithm by default |
| 42 | + |
| 43 | +- **`configure_jwt_plugin.sh`**: Kong plugin configuration |
| 44 | + - Creates Kong service and route |
| 45 | + - Configures jwt-keycloak plugin |
| 46 | + - Sets up Kong consumer |
| 47 | + |
| 48 | +### Test Suites |
| 49 | + |
| 50 | +#### 1. `test_jwt_keycloak_plugin.sh` |
| 51 | +Basic JWT validation functionality: |
| 52 | +- Valid token acceptance |
| 53 | +- Invalid token rejection |
| 54 | +- Consumer matching |
| 55 | + |
| 56 | +#### 2. `test_algorithms.sh` |
| 57 | +Algorithm validation and security tests: |
| 58 | +- **ES256 token validation** (Elliptic Curve) |
| 59 | + - Validates plugin accepts ES256 tokens from Keycloak |
| 60 | +- **'none' algorithm rejection** (Security) |
| 61 | + - Creates unsigned token with `"alg":"none"` |
| 62 | + - Validates plugin rejects with 401 |
| 63 | + - Protects against "none algorithm attack" |
| 64 | +- **Unsupported algorithm rejection** (Security) |
| 65 | + - Creates token with `"alg":"HS256"` |
| 66 | + - Validates plugin rejects with 401 |
| 67 | + - Ensures only supported algorithms are accepted |
| 68 | + |
| 69 | +**Note:** The plugin automatically validates tokens against all supported algorithms: |
| 70 | +- RSA: RS256, RS384, RS512 |
| 71 | +- EC: ES256, ES384, ES512 |
| 72 | + |
| 73 | +The `config.algorithm` parameter is deprecated and no longer used. RSA algorithm support is verified through the schema validation and the security tests demonstrate that the plugin correctly validates the algorithm header. |
| 74 | + |
| 75 | +#### 3. `test_error_conditions.sh` |
| 76 | +Error handling scenarios: |
| 77 | +- Missing token (401) |
| 78 | +- Invalid token format (401) |
| 79 | +- Expired tokens (401) |
| 80 | +- Wrong issuer (401/403) |
| 81 | +- OPTIONS preflight requests |
| 82 | + |
| 83 | +#### 4. `test_roles_scopes.sh` |
| 84 | +Authorization validation: |
| 85 | +- Scope-based authorization |
| 86 | + - Valid scope acceptance (200) |
| 87 | + - Invalid scope rejection (403) |
| 88 | +- Realm role validation |
| 89 | + - Valid realm role acceptance (200) |
| 90 | + - Invalid realm role rejection (403) |
| 91 | +- Client role validation |
| 92 | + - Valid client role acceptance (200) |
| 93 | + - Invalid client role rejection (403) |
| 94 | + |
| 95 | +#### 5. `test_security_logging.sh` |
| 96 | +Security event logging: |
| 97 | +- Authentication failure events |
| 98 | +- Wrong issuer security events |
| 99 | +- Security event code validation (ua200, ua201, ua220, ua222) |
| 100 | + |
| 101 | +## Test Environment Variables |
| 102 | +Set in `_env.sh`: |
| 103 | +- `KONG_ADMIN_URL`: Kong Admin API endpoint (http://kong:8001) |
| 104 | +- `KONG_PROXY_URL`: Kong Proxy endpoint (http://kong:8000) |
| 105 | +- `KC_URL`: Keycloak server URL (http://kc:8080) |
| 106 | +- `KC_CLIENT_ID`: Test client ID (test-user) |
| 107 | +- `KC_CLIENT_SECRET`: Generated client secret (random) |
| 108 | +- `KC_SIGNING_KEY_ALGORITHM`: Default signing algorithm (ES256) |
| 109 | +- `KC_REALM`: Keycloak realm name (default) |
| 110 | + |
| 111 | +## Helper Functions |
| 112 | + |
| 113 | +### `retry_test_after_plugin_change(description, expected_status, curl_command)` |
| 114 | +Retries a test up to 3 times with delays to allow Kong to apply plugin changes. |
| 115 | + |
| 116 | +**Example:** |
| 117 | +```bash |
| 118 | +if ! retry_test_after_plugin_change "Valid token test" "200" \ |
| 119 | + "curl -s -w \"%{http_code}\" -X GET $KONG_PROXY_URL/example/get -H \"Authorization: Bearer $TOKEN\" -o /dev/null"; then |
| 120 | + exit 1 |
| 121 | +fi |
| 122 | +``` |
| 123 | + |
| 124 | +**Parameters:** |
| 125 | +- `description`: Human-readable test description |
| 126 | +- `expected_status`: Expected HTTP status code(s), can be multiple like "200|201" |
| 127 | +- `curl_command`: The curl command to execute |
| 128 | + |
| 129 | +## Test Patterns |
| 130 | + |
| 131 | +### Plugin Reconfiguration Pattern |
| 132 | +When testing different plugin configurations: |
| 133 | + |
| 134 | +```bash |
| 135 | +# Delete existing plugin |
| 136 | +curl -s -X DELETE $KONG_ADMIN_URL/plugins/$(curl -s $KONG_ADMIN_URL/plugins | jq -r '.data[] | select(.name=="jwt-keycloak") | .id') > /dev/null |
| 137 | + |
| 138 | +# Create new plugin configuration |
| 139 | +curl -s -X POST $KONG_ADMIN_URL/plugins \ |
| 140 | + --data "name=jwt-keycloak" \ |
| 141 | + --data "config.allowed_iss=$KC_URL/auth/realms/$KC_REALM" \ |
| 142 | + --data "config.scope[]=email" \ |
| 143 | + --data "route.id=$(curl -s $KONG_ADMIN_URL/routes/example-route | jq -r '.id')" |
| 144 | + |
| 145 | +# Test with retry helper |
| 146 | +if ! retry_test_after_plugin_change "Test description" "200" "curl command"; then |
| 147 | + exit 1 |
| 148 | +fi |
| 149 | +``` |
| 150 | + |
| 151 | +### Token Retrieval Pattern |
| 152 | +```bash |
| 153 | +TOKEN_ENDPOINT="$KC_URL/auth/realms/$KC_REALM/protocol/openid-connect/token" |
| 154 | + |
| 155 | +ACCESS_TOKEN=$(curl -s -X POST $TOKEN_ENDPOINT \ |
| 156 | + -H "Content-Type: application/x-www-form-urlencoded" \ |
| 157 | + -d "client_id=$KC_CLIENT_ID" \ |
| 158 | + -d "client_secret=$KC_CLIENT_SECRET" \ |
| 159 | + -d "grant_type=client_credentials" | jq -r '.access_token') |
| 160 | + |
| 161 | +if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then |
| 162 | + echo "❌ Failed to obtain access token" |
| 163 | + exit 1 |
| 164 | +fi |
| 165 | +``` |
| 166 | + |
| 167 | +### Creating Test Tokens with Specific Algorithms |
| 168 | + |
| 169 | +#### Unsigned Token (none algorithm) |
| 170 | +```bash |
| 171 | +# Header: {"alg":"none","typ":"JWT"} |
| 172 | +NONE_HEADER=$(echo -n '{"alg":"none","typ":"JWT"}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n') |
| 173 | +NONE_PAYLOAD=$(echo -n '{"iss":"http://keycloak/realm","sub":"test","exp":9999999999}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n') |
| 174 | +NONE_TOKEN="${NONE_HEADER}.${NONE_PAYLOAD}." |
| 175 | +``` |
| 176 | + |
| 177 | +#### Unsupported Algorithm Token (HS256) |
| 178 | +```bash |
| 179 | +# Header: {"alg":"HS256","typ":"JWT"} |
| 180 | +HS256_HEADER=$(echo -n '{"alg":"HS256","typ":"JWT"}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n') |
| 181 | +HS256_PAYLOAD=$(echo -n '{"iss":"http://keycloak/realm","sub":"test","exp":9999999999}' | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n') |
| 182 | +HS256_SIGNATURE=$(echo -n "dummy_signature" | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n') |
| 183 | +HS256_TOKEN="${HS256_HEADER}.${HS256_PAYLOAD}.${HS256_SIGNATURE}" |
| 184 | +``` |
| 185 | + |
| 186 | +## Adding New Tests |
| 187 | +1. Create a new test file: `test_<feature>.sh` |
| 188 | +2. Source `_env.sh` for helper functions |
| 189 | +3. Add test execution to `run_tests.sh` |
| 190 | +4. Make the script executable: `chmod +x test_<feature>.sh` |
| 191 | +5. Document the test in this README |
| 192 | + |
| 193 | +**Example test structure:** |
| 194 | +```bash |
| 195 | +#!/bin/bash |
| 196 | +# SPDX-FileCopyrightText: 2025 Deutsche Telekom AG |
| 197 | +# |
| 198 | +# SPDX-License-Identifier: Apache-2.0 |
| 199 | + |
| 200 | +# Source environment helpers |
| 201 | +if [ -f ./_env.sh ]; then |
| 202 | + . ./_env.sh |
| 203 | +fi |
| 204 | + |
| 205 | +echo "🧪 Testing [feature name]..." |
| 206 | + |
| 207 | +# Your test logic here |
| 208 | + |
| 209 | +echo "✅ All [feature] tests passed" |
| 210 | +``` |
| 211 | + |
| 212 | +## Troubleshooting |
| 213 | + |
| 214 | +### Tests Fail Intermittently |
| 215 | +- Kong may take time to apply plugin changes |
| 216 | +- Use `retry_test_after_plugin_change()` helper |
| 217 | +- Increase retry count or wait time in `_env.sh` |
| 218 | + |
| 219 | +### Services Not Ready |
| 220 | +- Increase timeout in `wait_for_kong()` or `wait_for_keycloak()` |
| 221 | +- Check service logs: `docker compose logs kong` or `docker compose logs kc` |
| 222 | + |
| 223 | +### Token Validation Fails |
| 224 | +- Verify Keycloak configuration in `configure_keycloak.sh` |
| 225 | +- Check that token's algorithm is supported (RS256/384/512 or ES256/384/512) |
| 226 | +- Inspect token: `echo $ACCESS_TOKEN | cut -d. -f2 | base64 -d | jq .` |
| 227 | +- Verify token's `alg` header matches a supported algorithm |
| 228 | + |
| 229 | +### Algorithm-Related Issues |
| 230 | +- The plugin automatically validates against all supported algorithms |
| 231 | +- `config.algorithm` parameter is deprecated and ignored |
| 232 | +- Supported algorithms: RS256, RS384, RS512, ES256, ES384, ES512 |
| 233 | +- Any other algorithm (including 'none', HS256, etc.) will be rejected with 401 |
0 commit comments