Skip to content
Merged
9 changes: 5 additions & 4 deletions .github/workflows/checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ jobs:
- name: Setup Bats and bats libs
uses: bats-core/bats-action@42fcc8700f773c075a16a90eb11674c0318ad507 # 3.0.1
- run: test/service-start.bats
- run: test/service-cors.bats
- run: test/tdf-roundtrips.bats
- run: test/policy-service.bats
- name: verify builtin casbin policy
Expand Down Expand Up @@ -395,7 +396,7 @@ jobs:
focus-sdk: go
# use commit instead of ref so we can "go get" specific sdk version
platform-ref: ${{ github.event.pull_request.head.sha || github.sha }} lts

tests-bdd:
name: Cucumber BDD Tests
runs-on: ubuntu-22.04
Expand Down Expand Up @@ -423,21 +424,21 @@ jobs:
with:
go-version-file: ./tests-bdd/go.mod
cache: false

- name: Build local platform-cukes image for testing
run: docker build -t platform-cukes .

- name: Run BDD Tests
run: |
CUKES_LOG_HANDLER=console go test ./tests-bdd -v --tags=cukes --godog.random --godog.format="cucumber:$(pwd)/cukes_platform_report.json,pretty:$(pwd)/cukes_platform_report.log,pretty" ./features

- name: Check for undefined steps
run: |
if grep -qi "Undefined" cukes_platform_report.log; then
echo "❌ Undefined steps found in BDD tests!"
exit 1
fi

- name: Get logs on failure
if: failure() || cancelled()
run: |
Expand Down
3 changes: 2 additions & 1 deletion opentdf-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ server:
username_claim: # preferred_username
# That claim to access groups (i.e. realm_access.roles)
groups_claim: # realm_access.roles
# Claim the represents the idP client ID
# Claim the represents the idP client ID
client_id_claim: # azp
## Extends the builtin policy
extension: |
Expand Down Expand Up @@ -119,6 +119,7 @@ server:
# insecure: true # If collector is just HTTP, not HTTPS
# headers: {} # Add if authentication is needed
cors:
enabled: true
# "*" to allow any origin or a specific domain like "https://yourdomain.com"
allowedorigins:
- "*"
Expand Down
2 changes: 1 addition & 1 deletion service/internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ var rpcPathRegex = regexp.MustCompile(`^/[\w\.]+\.[\w\.]+/[\w]+$`)
func routeConnectRPCRequests(connectRPC http.Handler, httpHandler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// contentType := r.Header.Get("Content-Type")
if (r.Method == http.MethodPost || r.Method == http.MethodGet) && rpcPathRegex.MatchString(r.URL.Path) {
if (r.Method == http.MethodPost || r.Method == http.MethodGet || r.Method == http.MethodOptions) && rpcPathRegex.MatchString(r.URL.Path) {
connectRPC.ServeHTTP(w, r)
} else {
httpHandler.ServeHTTP(w, r)
Expand Down
112 changes: 112 additions & 0 deletions test/service-cors.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env bats

# Tests for validating CORS configuration allows Authorization header

# Set base URL based on TLS configuration
BASE_URL="http://localhost:8080"
CURL_OPTIONS=""

# Check if TLS is enabled via environment variable
if [[ "${TLS_ENABLED:-false}" == "true" ]]; then
BASE_URL="https://localhost:8080"
CURL_OPTIONS="-k" # Allow insecure connections for self-signed certs
fi

@test "CORS: preflight request includes Authorization in allowed headers" {
run curl -i -X OPTIONS $CURL_OPTIONS \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: authorization,content-type,connect-protocol-version" \
${BASE_URL}/policy.namespaces.NamespaceService/GetNamespace

echo "$output"

# Verify 200 OK response (HTTP/1.1 or HTTP/2)
[[ "$output" =~ "HTTP/2 200" ]] || [[ "$output" =~ "HTTP/1.1 200 OK" ]]

# Verify Access-Control-Allow-Headers includes Authorization
[[ "$output" =~ [Aa]ccess-[Cc]ontrol-[Aa]llow-[Hh]eaders:.*[Aa]uthorization ]]

# Verify Access-Control-Allow-Origin is set
[[ "$output" =~ [Aa]ccess-[Cc]ontrol-[Aa]llow-[Oo]rigin:\ http://localhost:3000 ]]

# Verify credentials are allowed
[[ "$output" =~ [Aa]ccess-[Cc]ontrol-[Aa]llow-[Cc]redentials:\ true ]]

# Verify max-age is set
[[ "$output" =~ [Aa]ccess-[Cc]ontrol-[Mm]ax-[Aa]ge:\ 3600 ]]
}

@test "CORS: preflight request with different headers" {
run curl -i -X OPTIONS $CURL_OPTIONS \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: authorization" \
${BASE_URL}/policy.namespaces.NamespaceService/GetNamespace

echo "$output"

# Verify 200 OK response (HTTP/1.1 or HTTP/2)
[[ "$output" =~ "HTTP/2 200" ]] || [[ "$output" =~ "HTTP/1.1 200 OK" ]]

# Verify Authorization is in allowed headers
[[ "$output" =~ [Aa]ccess-[Cc]ontrol-[Aa]llow-[Hh]eaders:.*[Aa]uthorization ]]
}

@test "CORS: actual request with Authorization header" {
run curl -i -X POST $CURL_OPTIONS \
-H "Origin: http://localhost:3000" \
-H "Authorization: Bearer test-token" \
-H "Content-Type: application/json" \
-H "Connect-Protocol-Version: 1" \
${BASE_URL}/policy.namespaces.NamespaceService/GetNamespace

echo "$output"

# Verify CORS headers are in response (status may be 401 due to invalid token, but CORS should work)
[[ "$output" =~ [Aa]ccess-[Cc]ontrol-[Aa]llow-[Oo]rigin:\ http://localhost:3000 ]]
[[ "$output" =~ [Aa]ccess-[Cc]ontrol-[Aa]llow-[Cc]redentials:\ true ]]
}

@test "CORS: wildcard origin configuration" {
run curl -i -X OPTIONS $CURL_OPTIONS \
-H "Origin: http://example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: authorization,content-type" \
${BASE_URL}/policy.namespaces.NamespaceService/GetNamespace

echo "$output"

# With wildcard ("*") in config, different origins should work
# Server should return 200 OK (HTTP/1.1 or HTTP/2)
[[ "$output" =~ "HTTP/2 200" ]] || [[ "$output" =~ "HTTP/1.1 200 OK" ]]

# Origin should be reflected back or wildcard
[[ "$output" =~ [Aa]ccess-[Cc]ontrol-[Aa]llow-[Oo]rigin: ]]
}

@test "CORS: verify Content-Type in allowed headers" {
run curl -i -X OPTIONS $CURL_OPTIONS \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: content-type" \
${BASE_URL}/policy.namespaces.NamespaceService/GetNamespace

echo "$output"

# Verify Content-Type is in allowed headers
[[ "$output" =~ [Aa]ccess-[Cc]ontrol-[Aa]llow-[Hh]eaders:.*[Cc]ontent-[Tt]ype ]]
}

@test "CORS: verify Connect-Protocol-Version in allowed headers" {
run curl -i -X OPTIONS $CURL_OPTIONS \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: connect-protocol-version" \
${BASE_URL}/policy.namespaces.NamespaceService/GetNamespace

echo "$output"

# Verify Connect-Protocol-Version is in allowed headers
[[ "$output" =~ [Aa]ccess-[Cc]ontrol-[Aa]llow-[Hh]eaders:.*[Cc]onnect-[Pp]rotocol-[Vv]ersion ]]
}
Loading