This is a simple demonstration application for the OPA Spring Boot SDK. The application presents a simple REST based object store implemented using Spring Boot.
The before
folder contains a version of the demo app without any API authorization, and the after
folder shows the same application with API authorization added using the OPA Spring Boot SDK.
Both applications can be launched via ./gradlew run
. The "after" version expects to find OPA running on http://localhost:8181
, which can be changed by setting the OPA_URL
environment variable. A sample policy can be found in after/policy
. You can launch an OPA instance with this policy using opa run -s --bundle ./after/policy
.
This shell session shows an example of interacting with the pre-integration version of the demo app using curl
. You need to run ./gradlew run
in the before
folder for this to work.
Create sample objects. Since there is no authorization yet, we can set the tenant and user to arbitrary values, though the API will error if we omit them.
$ curl -LSs -X PUT -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: a" --data '{"foo": "bar"}' http://localhost:8080/object/obj1
$ curl -LSs -X PUT -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: a" --data '{"bar": "baz"}' http://localhost:8080/object/obj2
$ curl -LSs -X PUT -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: b" --data '{"spam": "ham"}' http://localhost:8080/object/obj3
Check that the b
tenant contains obj3
, and the a
tenant contains obj1
and obj2
.
$ curl -LSs -X GET -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: b" http://localhost:8080/object | jq
[
"obj3"
]
curl -LSs -X GET -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: a" http://localhost:8080/object | jq
[
"obj2",
"obj1"
]
Read the objects back out. Notice that if we try to read obj3
from tenant a
, we get a not found, since we created that object in the b
tenant.
$ curl -LSs -X GET -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: a" http://localhost:8080/object/obj1 | jq
{
"foo": "bar"
}
$ curl -LSs -X GET -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: a" http://localhost:8080/object/obj2 | jq
{
"bar": "baz"
}
$ curl -LSs -X GET -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: a" http://localhost:8080/object/obj3 | jq
{
"timestamp": "2024-07-23T21:07:49.824+00:00",
"status": 404,
"error": "Not Found",
"path": "/object/obj3"
}
$ curl -LSs -X GET -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: b" http://localhost:8080/object/obj3 | jq
{
"spam": "ham"
}
Modify obj1
in place and read the value back out, to ensure that the new value has actually been stored.
$ curl -X PUT -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: a" --data '{"new": "val"}' http://localhost:8080/object/obj1
$ curl -LSs -X GET -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: a" http://localhost:8080/object/obj1 | jq
{
"new": "val"
}
Delete obj1
and ensure it is not found if we try to access it again.
curl -LSs -X DELETE -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: a" http://localhost:8080/object/obj1
curl -LSs -X GET -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: a" http://localhost:8080/object/obj1 | jq
{
"timestamp": "2024-07-23T21:09:30.449+00:00",
"status": 404,
"error": "Not Found",
"path": "/object/obj1"
}
This shell session shows an example of interacting with the post-integration version of the demo app using curl
. You need to run opa run -s --bundle ./policy
in the after
folder, then in a separate terminal run ./gradlew run
in the after
folder for this to work. If you want to see the HTTP status codes from the API server, just add the -v
argument to the curl commands.
Since alice
has the admin
role in the acmecorp
tenant, she should be able to perform any action. We demonstrate this by creating an object, verifying it shows up in the object list, deleting it, and finally verifying it is no longer present.
$ curl -LSs -X PUT -H "Content-Type: application/json" -H "Demo-User: alice" -H "Demo-Tenant: acmecorp" --data '{"foo": "bar"}' http://localhost:8080/object/obj1
$ curl -LSs -X GET -H "Demo-User: alice" -H "Demo-Tenant: acmecorp" http://localhost:8080/object | jq
[
"obj1"
]
$ curl -LSs -X DELETE -H "Demo-User: alice" -H "Demo-Tenant: acmecorp" http://localhost:8080/object/obj1
$ curl -LSs -X GET -H "Demo-User: alice" -H "Demo-Tenant: acmecorp" http://localhost:8080/object | jq
[]
bob
should be able to make changes, but not read the content of the objects or list them due to only having the writer
role. We verify this by having bob
create an object. When bob
tries to get the object listing or read obj2
, it fails, but alice
is still able to perform those actions.
$ curl -LSs -X PUT -H "Content-Type: application/json" -H "Demo-User: bob" -H "Demo-Tenant: acmecorp" --data '{"spam": "ham"}' http://localhost:8080/object/obj2
$ curl -LSs -X GET -H "Demo-User: bob" -H "Demo-Tenant: acmecorp" http://localhost:8080/object | jq
$ curl -LSs -X GET -H "Demo-User: alice" -H "Demo-Tenant: acmecorp" http://localhost:8080/object | jq
[
"obj2"
]
$ curl -LSs -X GET -H "Demo-User: bob" -H "Demo-Tenant: acmecorp" http://localhost:8080/object/obj2 | jq
$ curl -LSs -X GET -H "Demo-User: alice" -H "Demo-Tenant: acmecorp" http://localhost:8080/object/obj2 | jq
{
"spam": "ham"
}
eve
should be able to read objects, but not modify them due to only having the reader
role. We check this by having eve
read the existing obj2
object, then try to create an obj3
object and verifying that it does not exist after we did so.
curl -LSs -X GET -H "Demo-User: eve" -H "Demo-Tenant: acmecorp" http://localhost:8080/object/obj2 | jq
{
"spam": "ham"
}
curl -LSs -X PUT -H "Content-Type: application/json" -H "Demo-User: eve" -H "Demo-Tenant: acmecorp" --data '{"bar": "baz"}' http://localhost:8080/object/obj3
curl -LSs -X GET -H "Demo-User: eve" -H "Demo-Tenant: acmecorp" http://localhost:8080/object/obj3 | jq
{
"timestamp": "2024-07-24T19:36:58.088+00:00",
"status": 404,
"error": "Not Found",
"path": "/object/obj3"
}
Objects prefixed with legal_
or accounting_
require the user to have a corresponding role. alfred
has the reader
, writer
, and accounting
roles, so he should be able to create and modify any type of object except those prefixed with legal_
. We can verify this by creating and reading an accounting_
object, and then attempting to create a legal_
object and observing that the request fails.
$ curl -LSs -X PUT -H "Content-Type: application/json" -H "Demo-User: alfred" -H "Demo-Tenant: acmecorp" --data '{"spam": "ham"}' http://localhost:8080/object/accounting_sheet123
$ curl -LSs -X GET -H "Demo-User: alfred" -H "Demo-Tenant: acmecorp" http://localhost:8080/object/accounting_sheet123 | jq
{
"spam": "ham"
}
$ curl -LSs -X PUT -H "Content-Type: application/json" -H "Demo-User: alfred" -H "Demo-Tenant: acmecorp" --data '{"spam": "ham"}' http://localhost:8080/object/legal_case123 | jq
{
"timestamp": "2024-07-25T20:59:58.491+00:00",
"status": 403,
"error": "Forbidden",
"path": "/object/legal_case123"
}