Getting Started
This guide presents three approaches to working with SAPL, each suited to different goals and technical environments. Start with the playground to learn policy syntax, use Docker for local experimentation with a running PDP, or integrate SAPL directly into applications using the embedded PDP or HTTP API.
Learning Policy Syntax
The SAPL Playground runs entirely in your browser and requires no installation. Open the playground, write policies, create authorization subscriptions, and observe how the PDP evaluates them. The playground includes example policies demonstrating common authorization patterns.
The playground is primarily useful for learning the policy syntax and testing basic policy logic. The playground cannot connect to external attribute sources, and access to PIPs calling out to location tracking, HTTP servers, or MQTT brokers are present but calls are blocked. However, it is a useful tool to learn how a PDP works, how multiple policies and policy sets interact with each other, and how the streaming nature of SAPL works. It even allows you to graphically dig into traces of individual decisions for learning or debugging of your policies. You can also use it to share authorization scenarios with others.
Running a Local PDP Server
SAPL 4.0 can be used with an embedded PDP by importing the matching dependencies, or by deploying a SAPL Node, a lightweight and headless implementation PDP Server.
To get up and running quickly, it is straightforward to set up as a Docker container.
The SAPL Server strictly follows a secure-by-default philosophy. This means that the application comes with secure default settings, and it intentionally makes it difficult to operate without secure settings. Any SAPL Server by default expects to be set up with TLS enabled for transport-level encryption of access tokens, authorization subscriptions, and authorization decisions.
For a quickstart, we provide a simple pre-configuration to use which can be dropped into the volume of the Docker container.
- Have Docker installed locally as a runtime environment for the SAPL Node
- Install curl to test the server
- Select a local folder to be used as a volume for the Docker container where the configuration files and policies will be located. For this tutorial, we use
~/saplon Linux/macOS orC:\saplon Windows. Adjust the paths in the following steps to your local environment. - Download the two files
application.ymlandkeystore.p12to that folder. Download here You can inspect the.ymlfile for the documentation of the different configuration parameters. - Start the SAPL Node container, mounting the folder to the container path
/pdp/data:
Bash (Linux/macOS):
docker run -d --name sapl-node -e SERVER_ADDRESS=0.0.0.0 -p 8443:8443 -v ~/sapl:/pdp/data ghcr.io/heutelbeck/sapl-node:4.0.0-SNAPSHOT
PowerShell (Windows):
docker run -d --name sapl-node -e SERVER_ADDRESS=0.0.0.0 -p 8443:8443 -v C:\sapl:/pdp/data ghcr.io/heutelbeck/sapl-node:4.0.0-SNAPSHOT
The -e SERVER_ADDRESS=0.0.0.0 flag is required because the bundled configuration binds the server to localhost, which would be unreachable from outside the container. Setting it to 0.0.0.0 makes the server listen on all network interfaces.
If everything worked as expected, you should now see a line like Started SaplNodeApplication ... in the container logs.
- Send the following authorization request to the server:
Bash (Linux/macOS):
curl -v -k -H 'Authorization: Basic eHd1VWFSRDY1Rzozal9QSzcxYmp5IWhOMyp4cS54WnF2ZVUpdDVoS0xSXw==' -H 'Content-Type: application/json' -d '{"subject":"housemd","action":"use","resource":"MRT"}' https://localhost:8443/api/pdp/decide-once
PowerShell (Windows):
curl.exe -v -k -H "Authorization: Basic eHd1VWFSRDY1Rzozal9QSzcxYmp5IWhOMyp4cS54WnF2ZVUpdDVoS0xSXw==" -H "Content-Type: application/json" -d '{\"subject\":\"housemd\",\"action\":\"use\",\"resource\":\"MRT\"}' https://localhost:8443/api/pdp/decide-once
Note: On Windows, use
curl.exe(notcurl, which is a PowerShell alias forInvoke-WebRequest). In PowerShell, JSON strings require escaped double quotes (\"). All subsequent curl commands in this tutorial follow the same pattern.
The server should return {"decision":"NOT_APPLICABLE"}. As discussed in the introduction, NOT_APPLICABLE means the PDP has no policy that is applicable to the subscription. However, the reply shows that the PDP is set up properly and is answering authorization subscriptions and requests.
- Define the PDP combining algorithm. Create a file
pdp.jsonin the data folder (e.g.,~/saplorC:\sapl). Set its content to:{ "algorithm": { "votingMode": "PRIORITY_PERMIT", "defaultDecision": "DENY", "errorHandling": "ABSTAIN" }, "variables": {} }This instructs the server to evaluate policies by priority (highest priority first), return
DENYwhen no policy issues aPERMIT, and abstain from errors (treat them as not applicable). It also sets up an empty object to store environment variables in the future. - Send the same request as before to the server:
Bash:
curl -v -k -H 'Authorization: Basic eHd1VWFSRDY1Rzozal9QSzcxYmp5IWhOMyp4cS54WnF2ZVUpdDVoS0xSXw==' -H 'Content-Type: application/json' -d '{"subject":"housemd","action":"use","resource":"MRT"}' https://localhost:8443/api/pdp/decide-once
PowerShell:
curl.exe -v -k -H "Authorization: Basic eHd1VWFSRDY1Rzozal9QSzcxYmp5IWhOMyp4cS54WnF2ZVUpdDVoS0xSXw==" -H "Content-Type: application/json" -d '{\"subject\":\"housemd\",\"action\":\"use\",\"resource\":\"MRT\"}' https://localhost:8443/api/pdp/decide-once
The request should now return {"decision":"DENY"}, as expected.
- Now define a first policy. You can add arbitrary policies to the same folder where
pdp.jsonis stored. Files with SAPL policies and policy set, i.e., SAPL documents, must end with the file extension.sapl. The SAPL Node monitors the folder for changes to files and automatically loads, reloads, and unloads the respectivepdp.jsonor*.saplfiles. Create the filehousemd_mrt.sapl:
policy "Dr. House is allowed to use the MRT!"
permit subject=="housemd" & action=="use" & resource=="MRT";
If you now send the same authorization request again to the PDP, it should return {"decision":"PERMIT"}.
- So far we have just used a simple request-response pattern for answering authorization questions. Now let’s move to a simple time-based publish-subscribe scenario.
Issue the following request to the PDP. Note: the API endpoint changed from /decide-once to /decide. This new endpoint returns server-sent events and the client stays subscribed to updates:
Bash:
curl -v -k -H 'Authorization: Basic eHd1VWFSRDY1Rzozal9QSzcxYmp5IWhOMyp4cS54WnF2ZVUpdDVoS0xSXw==' -H 'Content-Type: application/json' -d '{"subject":"housemd","action":"use","resource":"MRT"}' https://localhost:8443/api/pdp/decide
PowerShell:
curl.exe -v -k -H "Authorization: Basic eHd1VWFSRDY1Rzozal9QSzcxYmp5IWhOMyp4cS54WnF2ZVUpdDVoS0xSXw==" -H "Content-Type: application/json" -d '{\"subject\":\"housemd\",\"action\":\"use\",\"resource\":\"MRT\"}' https://localhost:8443/api/pdp/decide
The server should now again return a {"decision":"PERMIT"}. However, note that the command does not immediately drop you back into your command line and curl stays connected. Keep it this way and do not interrupt the command.
-
Edit your policy file. For example, change the
subject=="housemd"tosubject=="cuddy"and save the file. Immediately, you should see{"decision":"DENY"}in the command line. Feel free to experiment with the authorization subscription in the curl command, or the policies. While you are connected, the curl command will always receive an updated decision representing the latest state of the policies. -
In the last step, you could observe how decisions change dynamically based on changes of the policies. Now, create a new policy which changes dynamically based on attributes changing. We will use a simple time-based environment attribute for this, i.e.,
<time.now>which is a one-second time ticker. Delete the original policy file (now you should get aDENYif your subscription is still going) and create a new policytime_based.sapl:policy "time demo" permit subject=="housemd"; action=="use"; resource=="MRT"; time.secondOf(<time.now>) % 10 < 5;
Note: the string in double quotes following policy is the policy name. For each policy file this name must be unique!
Now, with the same subscription as before, the decision will change from PERMIT to DENY and back every five seconds. What happens here is that <time.now> turns into a subscription to the clock. The function time.secondOf calculates the current second of the time emitted from <time.now>. Then, the policy applies a modulo 10 operator, resulting in a stream of the numbers 0 through 9. This number is compared to 5. Thus, for 0 to 4, all body conditions evaluate to true and the policy is applicable. Applicable policies emit their entitlement, which is PERMIT in this case. For the rest of the time, the policy emits NOT_APPLICABLE, which is turned into DENY by the combining algorithm configured in pdp.json.
Next Steps
You now have a working test setup. There are many experiments you can make now:
- Change the combining algorithm in the
pdp.json. - Experiment with different authorization subscriptions and policies.
- Add more conditions to the policy body.
- Start reading about the language features and experiment.
- Try to model your own business rules.
- Create more than one policy at a time.
- Inspect the log files of the Docker container. It is set up to verbose logging, and it will provide more information about how it calculated each individual decision.
The HTTP API works from any programming language that can make HTTP requests. See the HTTP Server-Sent Events API documentation for the complete API specification including streaming authorization decisions.
Integrating SAPL into Applications
Applications can integrate SAPL authorization either through an embedded PDP or by connecting to a remote SAPL server via HTTP. The embedded approach works well for single-instance applications or microservices, while the remote approach supports centralized policy management across multiple applications.
Embedded PDP for Java Applications
SAPL requires Java 21 or newer and is compatible with Java 25.
Configure a Java version in your project:
<properties>
<java.version>21</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
Add the SAPL embedded PDP dependency:
<dependency>
<groupId>io.sapl</groupId>
<artifactId>sapl-pdp-embedded</artifactId>
<version>4.0.0-SNAPSHOT</version>
</dependency>
Add the Maven Central snapshot repository:
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
For projects using multiple SAPL dependencies, use the bill of materials POM:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.sapl</groupId>
<artifactId>sapl-bom</artifactId>
<version>4.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Build a PDP using PolicyDecisionPointBuilder. For policies bundled in your application’s resources (the src/main/resources/policies folder), use withResourcesSource(). For policies on the filesystem (with live-reload on changes), use withDirectorySource():
import io.sapl.pdp.PolicyDecisionPointBuilder;
// Option A: Load policies from application resources (src/main/resources/policies)
var components = PolicyDecisionPointBuilder.withDefaults()
.withResourcesSource()
.build();
var pdp = components.pdp();
// Option B: Load policies from a filesystem directory (with live-reload)
var components = PolicyDecisionPointBuilder.withDefaults()
.withDirectorySource(Path.of("~/sapl/policies"))
.build();
var pdp = components.pdp();
Create the configuration file pdp.json in the policies directory:
{
"algorithm": {
"votingMode": "PRIORITY_PERMIT",
"defaultDecision": "DENY",
"errorHandling": "ABSTAIN"
},
"variables": {}
}
Add a policy file test_policy.sapl in the same directory:
policy "permit reading"
permit
action == "read";
subject == "willi" & resource =~ "some.+";
Request authorization decisions using the PDP. For a single blocking decision:
var subscription = AuthorizationSubscription.of("willi", "read", "something");
var decision = pdp.decideOnceBlocking(subscription);
System.out.println(decision.decision()); // PERMIT
For reactive streaming decisions that update when policies or attributes change:
pdp.decide(subscription).subscribe(decision ->
System.out.println(decision.decision())
);
When the PDP is no longer needed, release its resources:
components.dispose();
The Java API documentation covers the complete PDP interface including streaming decisions, multi-subscriptions, and Spring Security integration. Example applications demonstrating different integration patterns are available in the SAPL demos repository. Start with the Embedded PDP Demo for basic usage, or explore the Spring MVC Project and Webflux Application for framework integration.