This extension now provides secure access to Fuseki SPARQL endpoints through CKAN authentication. Instead of allowing anonymous access to Fuseki datasets, all access is proxied through CKAN, which checks permissions based on dataset visibility.
User Request → CKAN Proxy (/dataset/{id}/fuseki/$/*)
↓ Checks CKAN permissions
↓ Authenticates with Fuseki admin credentials
→ Fuseki Dataset ({fuseki-url}/{id}/*)
→ Returns data to user
Direct Access → Fuseki ({fuseki-url}/{id}/*)
✗ BLOCKED (requires admin authentication)
Notes:
- The
$separator in the URL prevents route conflicts with existing CKAN routes like/dataset/{id}/fuseki(management UI) and/dataset/{id}/fuseki/status. - The proxy automatically resolves dataset names/slugs to UUIDs, since Fuseki datasets are created using UUIDs. Both
/dataset/my-dataset-name/fuseki/$/sparqland/dataset/abc-123-uuid/fuseki/$/sparqlwill work correctly.
-
Fuseki Security Configuration (
config/fuseki/docker-entrypoint.sh)- Modifies
shiro.inion startup to block anonymous dataset access - Requires authentication for all dataset endpoints:
/** = authc, roles[admin]
- Modifies
-
CKAN Transparent Proxy (
ckanext/fuseki/views.py)- Route:
/dataset/<id>/fuseki/$/<path:service_path> - The
$separator prevents conflicts with existing CKAN UI routes - Accepts all HTTP methods (GET, POST, PUT, DELETE, etc.)
- Resolves dataset name/slug to UUID (Fuseki uses UUIDs)
- Checks CKAN
package_showpermission - Forwards requests to Fuseki with admin credentials
- Route:
-
Authorization (
ckanext/fuseki/logic/auth.py)fuseki_proxyauth function- Allows anonymous access for public datasets
- Requires authentication for private datasets
-
Helper Updates (
ckanext/fuseki/helpers.py)fuseki_sparql_url()now returns CKAN proxy URL- All generated links use the secure proxy
- ✅ Anonymous users can access SPARQL endpoints
- ✅ No CKAN API token required
- ℹ️ CKAN checks dataset is public before forwarding
- ✅ Dataset owner/members can access
- ✅ Requires CKAN authentication (API token or session)
- ✗ Unauthorized users receive 403 Forbidden
- ℹ️ Request never reaches Fuseki if unauthorized
- ✅ Admin users can access Fuseki UI directly
- ✅ Fuseki admin interface:
/fuseki/#/manage - ✗ Direct dataset access blocked for non-admins
- ℹ️ Only admin credentials work
This extension requires a custom Fuseki deployment with modified security settings. The necessary files are provided in the optional/ folder:
optional/fuseki/Dockerfile- Builds onsecoresearch/fuseki:4.9.0base imageoptional/fuseki/docker-entrypoint.sh- Custom entrypoint that modifiesshiro.inito block anonymous accessoptional/fuseki/index.html- Fixed Fuseki UI for subpath deploymentoptional/docker-compose.yml- Example deployment configuration
Option A: Mount Entrypoint (Simpler - No Build Required)
Just mount the security wrapper script as the entrypoint:
fuseki:
image: secoresearch/fuseki:4.9.0
entrypoint: /custom-entrypoint.sh
environment:
- ADMIN_PASSWORD=${FUSEKI_PASSWORD}
- JAVA_OPTIONS=-Xmx10g -Xms10g -DentityExpansionLimit=0
volumes:
- ./config/fuseki/docker-entrypoint.sh:/custom-entrypoint.sh:ro
- jena_data:/fuseki-baseDeploy
docker-compose up -d fusekiOption B: Build Custom Image (includes fixed index.html)
Build a custom image with the security wrapper:
fuseki:
build:
context: ckan_plugins/ckanext-fuseki/optional/fuseki/
environment:
- ADMIN_PASSWORD=${FUSEKI_PASSWORD}
- JAVA_OPTIONS=-Xmx10g -Xms10g -DentityExpansionLimit=0
volumes:
- jena_data:/fuseki-baseBuild and Deploy
docker-compose build fuseki
docker-compose up -d fusekiFor the new proxy route to be available:
docker-compose restart ckanTest that anonymous access to datasets is blocked:
# This should return 401 Unauthorized
curl http://fuseki:3030/{dataset-id}/sparql?query=SELECT%20*%20WHERE%20{?s%20?p%20?o}%20LIMIT%2010
# Admin access should work
curl -u admin:password http://fuseki:3030/{dataset-id}/sparql?query=SELECT%20*%20WHERE%20{?s%20?p%20?o}%20LIMIT%2010Test the CKAN proxy with a public dataset:
# Public dataset - no auth required
curl 'https://your-ckan-site.com/dataset/{public-dataset-id}/fuseki/$/sparql?query=SELECT%20*%20WHERE%20{?s%20?p%20?o}%20LIMIT%2010'
# Private dataset - requires API token
curl -H "Authorization: YOUR_API_TOKEN" \
'https://your-ckan-site.com/dataset/{private-dataset-id}/fuseki/$/sparql?query=SELECT%20*%20WHERE%20{?s%20?p%20?o}%20LIMIT%2010'If you want to completely block direct Fuseki access from the internet, configure your reverse proxy (Nginx):
# Block direct dataset access from external clients
location ~ ^/fuseki/[a-f0-9-]{36} {
# Only allow from internal network or reject
allow 172.16.0.0/12; # Docker network
deny all;
}
# Allow Fuseki admin interface for admins
location ~ ^/fuseki/(#|\$) {
proxy_pass http://fuseki:3030;
# Optional: restrict to admin IPs
}
# Allow CKAN proxy (handles its own auth)
location /dataset/ {
proxy_pass http://ckan:5000;
}All endpoints use the /dataset/{id}/fuseki/$/ prefix to avoid conflicts with CKAN UI routes.
GET/POST /dataset/{id}/fuseki/$/sparql
POST /dataset/{id}/fuseki/$/update
GET/PUT/POST/DELETE /dataset/{id}/fuseki/$/data?graph=<graph-uri>
POST /dataset/{id}/fuseki/$/upload
All endpoints support the same parameters and content types as Fuseki's native endpoints.
Example URLs:
- Query:
https://your-ckan.org/dataset/abc-123/fuseki/$/sparql?query=SELECT... - Update:
https://your-ckan.org/dataset/abc-123/fuseki/$/update - Data:
https://your-ckan.org/dataset/abc-123/fuseki/$/data?graph=http://example.org/graph
Required CKAN configuration (already in place):
ckanext.fuseki.url = http://fuseki:3030
ckanext.fuseki.username = admin
ckanext.fuseki.password = <admin-password>Or via environment variables:
CKANINI__CKANEXT__FUSEKI__URL=http://fuseki:3030
CKANINI__CKANEXT__FUSEKI__USERNAME=admin
CKANINI__CKANEXT__FUSEKI__PASSWORD=<admin-password>curl -H "Authorization: YOUR_API_TOKEN" \
'https://ckan-site.com/dataset/{id}/fuseki/$/sparql?query=...'When logged into CKAN, the browser session cookie is used automatically.
- ✅ Expected behavior - anonymous access is blocked
- ✅ Use CKAN proxy instead:
/dataset/{id}/fuseki/sparql
- Check dataset visibility
- Check user permissions
- Verify API token is valid
- Fuseki service may be down
- Check Fuseki is running:
docker-compose ps fuseki - Check Fuseki logs:
docker-compose logs fuseki
- Check docker-entrypoint.sh has execute permissions
- Rebuild Fuseki image:
docker-compose build fuseki - Check logs:
docker-compose logs fuseki | grep "CKAN Security"
✅ Centralized Authentication - All access controlled by CKAN
✅ Dataset-Level Permissions - Respects CKAN visibility settings
✅ No Credential Exposure - Users never need Fuseki admin password
✅ Audit Trail - All access logged through CKAN
✅ Standard SPARQL - Works with any SPARQL client
✅ Transparent Proxy - All Fuseki services available
The SPARQL resource links created in datasets will automatically use the new proxy URL after CKAN restart. Existing links in external applications should be updated to use:
https://your-ckan-site.com/dataset/{dataset-id}/fuseki/$/sparql
Instead of:
http://fuseki:3030/{dataset-id}/sparql
Note: The $ character in URLs should be properly escaped in shell commands and may need URL encoding (%24) in some contexts.
Direct Fuseki URLs will still work for admin users with proper credentials, but public/unauthenticated access will be blocked.