Skip to content

Policy Endpoints #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 55 commits into
base: feat/policy-endpoint
Choose a base branch
from

Conversation

lennertdr
Copy link

@lennertdr lennertdr commented Jul 8, 2025

Draft PR

To keep up with progress

  • Added POST and GET routes to config
  • Implemented basic POST endpoint
  • Implemented GET one policy endpoint
  • Add safety checks to POST
  • DELETE endpoint
  • simple Edit/PATCH endpoint
  • rewrite/PUT endpoint
  • Unit tests

Documentation is found within docs/policy-management.md

Copy link
Contributor

@woutslabbinck woutslabbinck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work so far.
Some small changes would help.

After the changes, we can start with DELETE, and then with update (sparql update for patch and complete policy rewriting with PUT)

joachimvh and others added 17 commits July 9, 2025 07:58
This includes derived resources,
which is referenced in the demo setup,
but not used in the example script.
This can be re-added in the future should it be required.
For some reason, tsx could not exit gracefully when running these.
Either it got stuck, or exited but left the process running.
These issues did not occur with ts-node,
but the solution in this commit seemed more acceptable
than having a new dependency.
Future work could include investigating
if this is an issue with the server.
Copy link
Contributor

@woutslabbinck woutslabbinck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small remarks on the docs

Copy link
Contributor

@woutslabbinck woutslabbinck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a small amount of nit picks, but in general it looks really good!

If these are resolved, I think this PR can be merged.

The checkboxes and comments from the docs that are still unresolved by then should become issues then

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.

The PUT process:
1. Find information about the policy. If it does not exist, return with a **status code 400** to indicate that you cannot rewrite a nonexistent policy.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I read this now, I am not 100% if this is the behaviour we want.

Either we allow PUT to create a resource if it does not exist yet (following the POST method).
Or we use a 404, as we performed a GET request and did not find any resource.

By typing this out, I think creating the resource (given that the identifier in the policy and the PUT policy-id match).

I would also argue similar behaviour with PATCH if the resource does not exist.

Perhaps @joachimvh has another opinion?

2. The content type of the body gets validated. The content-type must be set to `application/sparql-query`. Any other type will result in a response with **status code 400**.
3. We use the policy information to create a store which only contains groups **(1)**, **(3)** and **(4)** as explained [above](#get-one-policy). This will serve as an isolated store, on which we can execute the update query. This implementation has its advantages:
- We do not need to validate the sparql query, since we execute it on an isolated store.
- Performing DELETE queries on rules out of your scope will simply not work, since they are not part of the isolated store.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that nothing then will happen?
I understand it as such and think that is valid behaviour.

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.

## Testing
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 in the near future.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and integrations tests of course :)

@@ -20,4 +20,7 @@ export interface UCRulesStorage {
* @returns
*/
deleteRule: (identifier: string) => Promise<void>;

// Experimental endpoint
deleteRuleFromPolicy: (ruleID: string, PolicyID: string) => Promise<void>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the implementation, it seems like it deletes the usage control rule and its reference. Could you update the documentation as such?

Also remove the fact that it is experimental. At some point it will have unit tests and thus will not be experimental.

Could you also update the updateRule definition explaining that it does not delete the reference from policies?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this an artefact of using the earliest POST method in combination with the FileStorage solution?
If so, I think this can be removed


// 2. Retrieve the Policy Body
const contentType = request.headers['content-type'];
if (!/(?:application\/sparql-update)$/i.test(contentType)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it might be useful to use this utility for parsing the content type?
https://github.com/CommunitySolidServer/CommunitySolidServer/blob/main/src/util/HeaderUtil.ts#L381

The CSS is a dependency anyway

Comment on lines +41 to +54
// Sanitization
sanitizeRule(policyStore, clientId);

// 3.1 Check that the other rules are unchanged
const newState = getPolicyInfo(policyId, policyStore, clientId);
if (newState.otherRules.length !== 0 || newState.otherPolicyRules.length !== 0)
throw new BadRequestHttpError("Update not allowed: attempted to modify rules not owned by client");

// 3.2 Check that only Policy/Rule changing quads are introduced and removed
// The only modifications we allow are policy definitions, policy rules that define owned rules and owned rules themselves
const newQuads = policyStore.getQuads(null, null, null, null);
if (newQuads.length - newState.ownedRules.length - newState.ownedPolicyRules.length - newState.policyDefinitions.length
!== initialQuads.length - ownedPolicyRules.length - ownedRules.length - policyDefinitions.length)
throw new BadRequestHttpError("Update not allowed: this query introduces quads that have nothing to do with the policy/rules you own");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these sanitisation checks the same as the ones of rewritepolicies?

If so, maybe it is better to create a util function out of it that is re-used both here and in rewrite

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants