Skip to content
Merged
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
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,16 @@ jobs:
uses: actions/checkout@v4

- name: Bring up stack
run: docker compose up -d --build --wait --wait-timeout 90
run: docker compose up -d --build --wait --wait-timeout 180

- name: Smoke (readiness < 30s, public surface served)
run: |
bash scripts/demo.sh http://localhost:8080
bash scripts/demo.sh http://localhost:8081

- name: Auth chain proof (token -> protected endpoint -> 200)
run: bash scripts/demo-auth.sh

- name: Dump container logs on failure
if: failure()
run: docker compose logs --no-color
Expand Down
10 changes: 10 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Extends the default gitleaks ruleset; only adds a path-scoped allowlist.
[extend]
useDefault = true

[allowlist]
description = "Sandbox demo Keycloak realm + token-proof script: a documented, intentionally public demo client secret, not a real credential"
paths = [
'''^deploy/keycloak/fincore-realm\.json$''',
'''^scripts/demo-auth\.sh$''',
]
71 changes: 71 additions & 0 deletions deploy/keycloak/fincore-realm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"realm": "fincore",
"enabled": true,
"accessTokenLifespan": 300,
"sslRequired": "none",
"clientScopes": [
{
"name": "ledger:read",
"protocol": "openid-connect",
"attributes": { "include.in.token.scope": "true", "display.on.consent.screen": "false" }
},
{
"name": "ledger:write",
"protocol": "openid-connect",
"attributes": { "include.in.token.scope": "true", "display.on.consent.screen": "false" }
},
{
"name": "payments:read",
"protocol": "openid-connect",
"attributes": { "include.in.token.scope": "true", "display.on.consent.screen": "false" }
},
{
"name": "payments:write",
"protocol": "openid-connect",
"attributes": { "include.in.token.scope": "true", "display.on.consent.screen": "false" }
},
{
"name": "decision:read",
"protocol": "openid-connect",
"attributes": { "include.in.token.scope": "true", "display.on.consent.screen": "false" }
},
{
"name": "decision:write",
"protocol": "openid-connect",
"attributes": { "include.in.token.scope": "true", "display.on.consent.screen": "false" }
},
{
"name": "compliance:read",
"protocol": "openid-connect",
"attributes": { "include.in.token.scope": "true", "display.on.consent.screen": "false" }
},
{
"name": "compliance:write",
"protocol": "openid-connect",
"attributes": { "include.in.token.scope": "true", "display.on.consent.screen": "false" }
}
],
"clients": [
{
"clientId": "fincore-demo",
"enabled": true,
"protocol": "openid-connect",
"publicClient": false,
"secret": "fincore-demo-secret",
"serviceAccountsEnabled": true,
"standardFlowEnabled": false,
"directAccessGrantsEnabled": false,
"defaultClientScopes": [
"ledger:read",
"ledger:write",
"payments:read",
"payments:write",
"decision:read",
"decision:write",
"compliance:read",
"compliance:write"
],
"optionalClientScopes": []
}
]
}
32 changes: 30 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,44 @@ services:
retries: 10
start_period: 5s

keycloak:
image: quay.io/keycloak/keycloak:26.6.1
command: ["start-dev", "--import-realm"]
environment:
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin}
KC_HOSTNAME: http://keycloak:8080
KC_HTTP_ENABLED: "true"
KC_HEALTH_ENABLED: "true"
ports:
- "8085:8080"
volumes:
- ./deploy/keycloak:/opt/keycloak/data/import:ro
healthcheck:
test:
[
"CMD-SHELL",
"exec 3<>/dev/tcp/localhost/9000; echo -e 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3; cat <&3 | grep -q 'HTTP/1.1 200'",
]
interval: 5s
timeout: 5s
retries: 30
start_period: 30s

ledger:
build: .
depends_on:
postgres:
condition: service_healthy
keycloak:
condition: service_started
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/ledger
SPRING_DATASOURCE_USERNAME: ledger
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD:-ledger}
KEYCLOAK_ISSUER_URI: http://localhost/realms/fincore
KEYCLOAK_ISSUER_URI: http://keycloak:8080/realms/fincore
SPRING_LIQUIBASE_CONTEXTS: production,demo
OTLP_TRACING_ENDPOINT: http://otel-collector:4318/v1/traces

Expand All @@ -37,13 +63,15 @@ services:
depends_on:
postgres:
condition: service_healthy
keycloak:
condition: service_started
ports:
- "8081:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/payments
SPRING_DATASOURCE_USERNAME: ledger
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD:-ledger}
KEYCLOAK_ISSUER_URI: http://localhost/realms/fincore
KEYCLOAK_ISSUER_URI: http://keycloak:8080/realms/fincore
FINCORE_PAYMENTS_BANK_SANDBOX_ENABLED: "true"
OTLP_TRACING_ENDPOINT: http://otel-collector:4318/v1/traces

Expand Down
46 changes: 46 additions & 0 deletions scripts/demo-auth.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: BUSL-1.1
# SPDX-FileCopyrightText: 2026 FinCore Engine Authors
set -euo pipefail

KEYCLOAK_URL="${KEYCLOAK_URL:-http://localhost:8085}"
LEDGER_URL="${LEDGER_URL:-http://localhost:8080}"
PAYMENTS_URL="${PAYMENTS_URL:-http://localhost:8081}"
CLIENT_ID="${FINCORE_DEMO_CLIENT_ID:-fincore-demo}"
CLIENT_SECRET="${FINCORE_DEMO_CLIENT_SECRET:-fincore-demo-secret}"
TOKEN_ENDPOINT="$KEYCLOAK_URL/realms/fincore/protocol/openid-connect/token"

fetch_token() {
curl -fsS -X POST "$TOKEN_ENDPOINT" \
-d grant_type=client_credentials \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET" |
sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p'
}

token=""
for attempt in $(seq 1 5); do
token=$(fetch_token || true)
[ -n "$token" ] && break
echo "token fetch attempt $attempt failed; retrying in 2s"
sleep 2
done

if [ -z "$token" ]; then
echo "FAIL: could not obtain a client_credentials token from $TOKEN_ENDPOINT"
exit 1
fi

ledger_code=$(curl -sS -o /dev/null -w '%{http_code}' -H "Authorization: Bearer $token" "$LEDGER_URL/v1/accounts")
if [ "$ledger_code" != "200" ]; then
echo "FAIL: GET /v1/accounts with a bearer returned $ledger_code (expected 200)"
exit 1
fi

payments_code=$(curl -sS -o /dev/null -w '%{http_code}' -H "Authorization: Bearer $token" "$PAYMENTS_URL/v1/payments")
if [ "$payments_code" != "200" ]; then
echo "FAIL: GET /v1/payments with a bearer returned $payments_code (expected 200)"
exit 1
fi

echo "auth chain OK: client_credentials token accepted by ledger and payments"
Loading