Skip to content

Commit 04e11d8

Browse files
committed
docs: Update policy management documentation with known issues
1 parent e2f6d15 commit 04e11d8

3 files changed

Lines changed: 86 additions & 85 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ You can then execute the following flows:
3030
- `yarn script:collection`: `POST`, `GET` and `DELETE` some text to/from `/alice/public/resource.txt` to test the correct creation and deletion of resource registrations on the UMA server.
3131
An AssetCollection policy is used to create `/alice/public/`.
3232
More information on the collection implementation can be found in [documentation/collections.md](documentation/collections.md).
33+
- `yarn script:uma-odrl-policy`: Tests all policy management APIs.
34+
- `yarn script:uma-odrl-end2end`: Tests the access request management APIs
3335

3436
`yarn script:flow` runs all flows in sequence.
3537

@@ -39,6 +41,9 @@ the above scripts are the best way to learn about how everything works.
3941
A more extensive getting started guide can be found
4042
in [documentation/getting-started.md](documentation/getting-started.md).
4143

44+
More information on policy management can be found in
45+
[documentation/policy-management](documentation/policy-management.md).
46+
4247
## Demonstration
4348

4449
Instead of running `yarn start`, you can run `yarn start:demo` to start the server with an alternative configuration.

documentation/policy-management.md

Lines changed: 69 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Policy Management
22

3-
In this document we describe the *policy adminstration endpoint*.
4-
It contains the methods to describe how to create, read, update and delete policies.
3+
The *policy management API* allows users to configure the policies that govern access to their data.
4+
First we cover the available operations, after which we cover the known issues and limitations of the API.
55

6-
## Supported endpoints
6+
## API
77

8-
The current implementation supports the following requests:
8+
The current implementation supports the following requests on the UMA server:
99

1010
- [`GET`](#reading-policies) to both `uma/policies` and `uma/policies/<encodedPolicyID>`
1111
- [`POST`](#creating-policies) to `uma/policies`
@@ -15,13 +15,15 @@ The current implementation supports the following requests:
1515

1616
These requests comply with some restrictions:
1717

18-
- When the URL contains a policy ID, it must be [URI encoded](#uri-encoding-decision).
19-
- The request must have its `'Authorization'` header set to the clients webID. More on that [later](#authorizationauthentication-decisions).
18+
- When the URL contains a policy ID, it must be URI encoded.
19+
- The request must have its `Authorization` header set to the owners WebID.
20+
More on that [later](#authentication).
2021

2122
### Creating policies
2223

2324
Create a policy/multiple policies through a POST request to `/uma/policies`.
24-
Apart from its Authorization header, the `'Content-Type'` must be set to the RDF serialization format in which the body is written.
25+
Apart from its Authorization header, the `'Content-Type'` must be set
26+
to the RDF serialization format in which the body is written.
2527
The accepted formats are those accepted by the [N3 Parser](https://github.com/rdfjs/N3.js/?tab=readme-ov-file#parsing), represented by the following content types:
2628

2729
- `text/turtle`
@@ -30,7 +32,9 @@ The accepted formats are those accepted by the [N3 Parser](https://github.com/rd
3032
- `application/n-quads`
3133
- `text/n3`
3234

33-
The body is expected to represent a valid ODRL policy, although some [sanitization](#sanitization-decisions) is applied to ensure minimal validity. It is possible to POST multiple policies at once, but they have to remain in scope of the client.
35+
The body is expected to represent a valid ODRL policy,
36+
although some [sanitization](#sanitization-decisions) is applied to ensure minimal validity.
37+
It is possible to POST multiple policies at once, but they have to remain in scope of the owner.
3438
Upon success, the server responds with **status code 201**.
3539
Bad requests, possibly due to an improper policy definition, will respond with **status code 400 or 409**.
3640
When the policy has been validated, but adding it to the storage fails, the response will have **status code 500**.
@@ -61,22 +65,24 @@ ex:permission a odrl:Permission ;
6165

6266
To read policies, two endpoints are implemented:
6367

64-
- GET `/uma/policies`: get policy information you are authorized to see, for every policy.
65-
- GET `/uma/policies/<encodedPolicyID>`: get policy information you are authorized to see, for the policy with the specified [URI encoded](#uri-encoding-decision) ID.
68+
- GET `/uma/policies`: return all policies the provided credentials have assigned.
69+
- GET `/uma/policies/<encodedPolicyID>`: return the rules of the policy with the given (encoded) ID,
70+
if the provided credentials are allowed to see them.
6671

67-
These endpoints returen both the policies (and related rules) where the user is identified as the assigner and assignee.
68-
Applications should be aware of this and should make sure the distinction is made where necessary.
72+
One policy can contain rules of multiple assigners,
73+
only the rules where the assigner matches the request credentials will be returned.
6974

7075
#### GET one policy
7176

72-
An example request to get policy `http://example.org/policy` for the client with webID `https://pod.example.com/profile/card#me` looks like this:
77+
An example request to get policy `http://example.org/policy`
78+
with WebID `https://pod.example.com/profile/card#me` looks like this:
7379

7480
```curl
7581
curl --location 'http://localhost:4000/uma/policies/http%3A%2F%2Fexample.org%2Fpolicy' \
7682
--header 'Authorization: https://pod.example.com/profile/card#me'
7783
```
7884

79-
If the client has viable information about this policy, the server would respond with the information about the policy:
85+
Since the credentials match the assigner, the server responds with the information about the policy:
8086

8187
```ttl
8288
<http://example.org/policy> a <http://www.w3.org/ns/odrl/2/Agreement>;
@@ -99,27 +105,34 @@ curl --location 'http://localhost:4000/uma/policies' \
99105

100106
### Updating policies
101107

102-
Updating a policy can be done through a PUT or a PATCH request to `/uma/policies/<encodedPolicyID>`, each with different semantics.
108+
Updating a policy can be done through a PUT or a PATCH request to `/uma/policies/<encodedPolicyID>`,
109+
each with different semantics.
103110

104111
#### PUT
105112

106-
A PUT completely replaces the policy within the scope of the client.
107-
The PUT works as a combination of DELETE and POST. It requires a body with the same content type as the [POST request](#creating-policies). This body will be interpreted as the requested policy with some rules.
113+
A PUT completely replaces all rules of a policy the client is the assigner of.
114+
The PUT works as a combination of DELETE and POST.
115+
It requires a body with the same content type as the [POST request](#creating-policies).
116+
This body will be interpreted as the requested policy with some rules.
108117

109118
The PUT process:
110119

111-
1. Find information about the policy. If it does not exist, return with a **status code 404** to indicate that you cannot rewrite a nonexistent policy.
120+
1. Find information about the policy.
121+
If it does not exist, return with a **status code 404** to indicate that you cannot rewrite a nonexistent policy.
112122

113-
2. Parse and validate the body, with the same procedure used in the POST endpoint. First, we perform the basic sanitization checks. Upon success, extra checks are performed to see if the new definition stays within the scope of the client:
123+
2. Parse and validate the body, with the same procedure used in the POST endpoint.
124+
First, we perform the basic sanitization checks.
125+
Upon success, extra checks are performed to see if the new definition stays within the scope of the client:
114126
- Check that the newly defined policy does not define other policies
115127
- Check that the new policy does not contain any rules that do not belong to the client
116128
- Check that no unrelated quads to the policy and its rules are added.
117129

118130
Failed checks will result in a response with **status code 400** and a dedicated message.
119-
3. Delete the old policy, but keep a copy for a possible rollback. The deletion uses the procedure used in the [DELETE](#deleting-policies) endpoint.
131+
3. Delete the old policy, but keep a copy for a possible rollback.
132+
The deletion uses the procedure used in the [DELETE](#deleting-policies) endpoint.
120133

121134
4. Add the new policy. On success, the server will respond with **status code 204** .
122-
Upon failure, the server will respond with a 5xx error status.
135+
Upon failure, the server will respond with a 5xx error status.
123136

124137
##### Example PUT Request
125138

@@ -143,21 +156,20 @@ ex:permission a odrl:Permission ;
143156
odrl:assigner <https://pod.example.com/profile/card#me> .'
144157
```
145158

146-
This example updates the target of this policy. It is important to explicitly include `-X PUT`, as curl will otherwise default to a POST request, which is invalid for this endpoint.
147-
148159
#### PATCH
149160

150161
A PATCH request will update the policy and its related rules using a SPARQL update query.
151162
The `content-type` header must be set to `application/sparql-update`.
152163

153164
The policy will be isolated from the store before executing the query, to make sure no other quads are affected.
154-
In addition, the user's credentials are checked to make sure they are the resource owner for the resource targeted in the policy.
155165
After this, the query can be executed.
156-
To make sure the policy remains a valid policy, the policy is isolated and checked again before inserting the modified store back in the store.
166+
To make sure the policy remains a valid policy, the policy is isolated and checked again
167+
before inserting the modified store back in the store.
157168

158169
##### Example PATCH request
159170

160-
The example below illustrates how policies can be changed using a PATCH request. We notice that the content type has changed to `application/sparql-query`.
171+
The example below illustrates how policies can be changed using a PATCH request.
172+
Notice that the content type has changed to `application/sparql-query`.
161173

162174
```curl
163175
curl -X PATCH --location 'http://localhost:4000/uma/policies/http%3A%2F%2Fexample.org%2Fpolicy' \
@@ -188,65 +200,49 @@ curl -X DELETE --location 'http://localhost:4000/uma/policies/http%3A%2F%2Fexamp
188200
--header 'Authorization: https://pod.example.com/profile/card#me'
189201
```
190202

191-
## Implementation details
203+
## Known issues and limitations
192204

193-
### Authorization/Authentication decisions
205+
### Authentication
194206

195-
The current implementation has insufficient authentication restrictions. Currently, the only requirement is that the 'Authorization' header is to be set to the webID of the "logged on" client. Proper procedures to authenticate this client are still to be implemented.
207+
Current authentication is done by setting the `Authorization` header to a WebID.
208+
There is no verification so any WebID can be entered.
209+
In the future we want to support OIDC tokens for authentication.
196210

197-
### Sanitization decisions
211+
### Sanitization
198212

199-
Some endpoints allow new policies to be created, or existing policies to be modified. This introduces the possibility of invalid syntactic or semantic policies, hence a sanitization strategy is required. In the current implementation, only POST, PUT and PATCH could introduce such problems. We provided the following basic checks:
213+
New policies and policy updates are sanitized with the following checks:
200214

201-
- Every defined rule must have a unique ID.
215+
- Every defined rule must have a unique ID (`odrl:uid`).
202216
- Every rule must have exactly one assigner.
203-
- Every assigner must match the authenticated client.
204-
205-
Sanitization Limitations
206-
207-
- There are currently no checks to verify whether a client is sufficiently authorized to create or modify a policy/rule for a specific target.
208-
- A client should not be in able to alter rights about a target it does not have access to.
209-
- There are plenty of other sanitization checks to be considered.
210-
211-
### URI encoding decision
212-
213-
Some operations require the client to specify a policy ID in the URL. Since policy ID's might contain reserved characters (e.g. `/`, `:`, ...), we have chosen to encode them with the builtin [`encodeURIComponent()` function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent). Using this method, reserved characters will be converted to their respective UTF-8 encodings.
214-
215-
## Testing
216-
217-
The current implementation is tested only by the script in `scripts\test-uma-ODRL-policy.ts`. This script tests every implemented endpoint with a designated flow. Since the script initiates with an empty storage, and there is no endpoint or other way to seed it, the first requests must test the POST endpoint. These tests are designed to ensure that the storage is filled. After the POST tests, the access endpoints can be tested. Every endpoint gets tested in this script, which makes sure that the added data is removed. The current testing will be replaced with proper unit tests and integration tests in the near future.
218-
219-
## TODO
220-
221-
- The current [sanitization limitations](#sanitization-decisions) are to be considered.
222-
- Implement Unit Tests
223-
- ...
224-
225-
### Solved Problems
226-
227-
#### DELETE fix
228-
229-
##### Problem
230-
231-
When you have a policy with multiple rules that have different assigners, DELETE on every rule of one assigner will succesfully delete the rule itself, but not the definition of the rule within the policy. This is due to the fact that you can currently only DELETE based on the ID of the rule/policy you want to delete, and you cannot delete the entire policy since other assigners depend on it. Currently, the only problem with this is filling space, since the quads defining deleted rules will not be returned in GET requests.
232-
233-
##### Fix
217+
- The assigner must match the authenticated WebID.
234218

235-
We created a new RulesStorage function, made specifically to fix our problem entirely. The function is implemented to delete the rule AND its definition in the policy. This solution is still a bit experimental.
219+
The sanitization check is quite limited and will not prevent all invalid policies.
220+
On the other hand, it is not able to handle some triples that are valid,
221+
such as collection definitions.
236222

237-
#### PATCH fix
223+
### Ownership
238224

239-
PATCH used to contain a safety hazard. When client A has a certain policy/rule, or even just a certain quad, this could be discovered by an intrusive client B. Client B could simply PATCH an INSERT of a random quad that does NOT belong to its own rules/policies, which can have one of three outcomes:
225+
There is no ownership check to make sure users can only write policies they own.
226+
This will require changes to the resource server as currently it does not inform the UMA server of ownership.
240227

241-
1. The PATCH resolves in an error saying that you cannot change rules that do not belong to you. This means that the quad belongs to some other client, since it has been detected as a quad owned by someone else.
228+
### PATCH limitations
242229

243-
2. The PATCH resolves in an error saying that you cannot change rules that belong to nobody. This means that the quad is not affiliated with any client.
230+
PATCH only works on simple policies without constraints.
231+
There is a known issue where nested triples, such as constraints, can get lost when modifying a policy.
244232

245-
3. The PATCH completes with code 200. Since the inserted quad does NOT belong to you, there must be another client that owns the quad. In this way, any policy can be discovered (exhaustively).
246-
An extra constraint, disabling clients to PATCH policies it has no rules in, would still enable the client to exploit policies that it has rules in.
233+
### PUT identifier validation
247234

248-
This problem was solved by splitting the policy into the parts where the client has access to, and the parts where it does not. By executing the query only on the parts that the client has access to, it would be easier to analyse the resulting store of the query. If this store has rules that the client does not have access to, they must have been added by the client and the operation gets cancelled. This method is also protected from deleting rules out of our reach.
235+
The identifier is not validated correctly when doing a PUT request.
236+
This means that the identifier of the policy you are PUTting does not need to match the identifier in the URL.
237+
The policy that gets modified is based on the identifier found in the policy.
249238

250-
#### POST checks
239+
### Policies with multiple assigners
251240

252-
It is now impossible to POST an already existing policy or already existing rules. This means that a policy can only be POSTED once. If a client wishes to be a part of a policy, it has to do it through a PUT request. If a client is already part of the policy, it can PATCH modifications.
241+
It is possible to have a policy with several rules,
242+
which have different assigners.
243+
Some known issues there:
244+
- When GETting such a policy, you will only receive the identifiers of all linked rules,
245+
even those you are not the assigner of.
246+
You will not get the contents of those rules though.
247+
- When DELETEing such a policy, all rules will be deleted,
248+
even those you are not the assigner of.

packages/uma/src/util/routeSpecific/get.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import {queryEngine} from './index';
33

44
/**
55
* Run a query against a store and collect the matching subgraphs.
6-
*
6+
*
77
* For each set of variable bindings, this function extracts all triples
88
* where the bound terms appear as subjects, then groups them into a result store.
9-
*
9+
*
1010
* @param store the source store to query
1111
* @param query the query string to execute
1212
* @param vars list of variable names that must be present in the result
@@ -50,10 +50,10 @@ const executeGet = async (
5050

5151
/**
5252
* Build a query to retrieve a single policy and its permissions
53-
* by matching both the policy ID and the client’s role as assigner or assignee.
54-
*
53+
* by matching both the policy ID and the client’s role as assigner.
54+
*
5555
* @param policyID identifier of the policy
56-
* @param resourceOwner identifier of the client (assigner or assignee)
56+
* @param resourceOwner identifier of the assigner
5757
* @returns a query string
5858
*/
5959
const buildPolicyRetrievalQuery = (policyID: string, resourceOwner: string) => `
@@ -76,7 +76,7 @@ const buildPolicyRetrievalQuery = (policyID: string, resourceOwner: string) => `
7676

7777
/**
7878
* Retrieve a single policy and its permissions.
79-
*
79+
*
8080
* @param store the source store
8181
* @param policyID identifier of the policy
8282
* @param resourceOwner identifier of the client (assigner or assignee)
@@ -88,7 +88,7 @@ export const getPolicy = (store: Store, policyID: string, resourceOwner: string)
8888
/**
8989
* Build a query to retrieve all policies for a given client.
9090
* A client may act as assigner or assignee.
91-
*
91+
*
9292
* @param resourceOwner identifier of the client
9393
* @returns a query string
9494
*/
@@ -110,7 +110,7 @@ const buildPoliciesRetrievalQuery = (resourceOwner: string) => `
110110

111111
/**
112112
* Retrieve all policies for a given client.
113-
*
113+
*
114114
* @param store the source store
115115
* @param resourceOwner identifier of the client
116116
* @returns a store containing all policies and their permissions
@@ -126,7 +126,7 @@ export const getPolicies = (store: Store, resourceOwner: string) =>
126126
* Build a query to retrieve a single request,
127127
* provided that the client is either the requesting party
128128
* or the assigner of a policy targeting the same resource.
129-
*
129+
*
130130
* @param requestID identifier of the request
131131
* @param requestingPartyOrResourceOwner identifier of the client
132132
* @returns a query string
@@ -154,7 +154,7 @@ const buildAccessRequestRetrievalQuery = (requestID: string, requestingPartyOrRe
154154
/**
155155
* Retrieve a single request by ID,
156156
* if the client is the requesting party or assigner of the target.
157-
*
157+
*
158158
* @param store the source store
159159
* @param accessRequestID identifier of the request
160160
* @param requestingPartyOrResourceOwner identifier of the client
@@ -166,7 +166,7 @@ export const getAccessRequest = (store: Store, accessRequestID: string, requesti
166166
/**
167167
* Build a query to retrieve all requests for a client,
168168
* either as requesting party or as assigner of the requested target.
169-
*
169+
*
170170
* @param requestingPartyOrResourceOwner identifier of the client
171171
* @returns a query string
172172
*/
@@ -193,7 +193,7 @@ const buildAccessRequestsRetrievalQuery = (requestingPartyOrResourceOwner: strin
193193
/**
194194
* Retrieve all requests for a client,
195195
* either as requesting party or as assigner of the requested targets.
196-
*
196+
*
197197
* @param store the source store
198198
* @param requestingPartyOrResourceOwner identifier of the client
199199
* @returns a store containing the requests

0 commit comments

Comments
 (0)