Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 15 additions & 19 deletions controller/src/classes/utils/OdrlAccessRequestService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ export class ODRLAccessRequestService {
`${this.authorizationServerURL}/requests`, {
method: 'POST',
headers: {
'authorization': `WebID ${encodeURIComponent(requestingParty)}`,
'content-type': 'text/turtle'
}, body: await this.accessRequestToTtl({
'authorization': `WebID ${encodeURIComponent(requestingParty)}`
}, body: await this.accessRequestToJson({
uid: uuid(),
target: resourceURL,
action: action,
Expand All @@ -38,19 +37,11 @@ export class ODRLAccessRequestService {
if (response.status !== 201) throw new Error('failed to create access request');
}

private accessRequestToTtl = async (accessRequest: AccessRequest): Promise<string> => `
@prefix ex: <http://example.org/> .
@prefix sotw: <https://w3id.org/force/sotw#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:${accessRequest.uid} a sotw:EvaluationRequest ;
dcterms:issued "${new Date().toISOString()}"^^xsd:datetime ;
sotw:requestedTarget <${accessRequest.target}> ;
sotw:requestedAction odrl:${accessRequest.action} ;
sotw:requestingParty <${accessRequest.requestingParty}> ;
ex:requestStatus ex:${accessRequest.status} .
private accessRequestToJson = async (accessRequest: AccessRequest): Promise<string> => `
{
"resource_id": "${accessRequest.target}",
"resource_scopes": [ "http://www.w3.org/ns/odrl/2/${accessRequest.action}" ]
}
`;

/**
Expand Down Expand Up @@ -134,9 +125,14 @@ export class ODRLAccessRequestService {
return results;
}


/**
* Retrieves last part of URI.
* @param val - URI
*/
private readonly cleanValue = (val?: string): string => {
Comment thread
woutslabbinck marked this conversation as resolved.
if (!val) return '';
const match = val.match(/^http:\/\/.*\/(.*)$/);
const match = val.match(/([^/#]+)$/);
return (match ? match[1] : val).toLowerCase();
}

Expand All @@ -151,7 +147,7 @@ export class ODRLAccessRequestService {
sotw:requestedTarget ?target ;
sotw:requestedAction ?action ;
sotw:requestingParty <${requestingPartyID}> ;
ex:requestStatus ?status .
sotw:requestStatus ?status .
}
`;

Expand All @@ -169,7 +165,7 @@ export class ODRLAccessRequestService {
sotw:requestedTarget ?target ;
sotw:requestedAction ?action ;
sotw:requestingParty ?requestingParty ;
ex:requestStatus ?status .
sotw:requestStatus ?status .
}
`;
}
157 changes: 157 additions & 0 deletions documentation/access-requests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Seeding Loama and UMA

These commands automate the creation of local policies and access requests between two test accounts (Alice and Bob).

## Environment configuration

* **Identity Provider:** `http://localhost:3000`
* **Test Accounts:** `bob@example.org`, `alice@example.org`
* **Password:** `abc123`
* **UMA Version:** Repository `SolidLabResearch/user-managed-access` at commit `9b08f27`

## Installation

### Loama setup

```sh
git clone git@github.com:SolidLabResearch/loama.git
cd loama
git checkout feat/odrl
nvm use 20
npm install
npm run build
```

### UMA setup

```sh
git clone git@github.com:SolidLabResearch/user-managed-access.git
cd user-managed-access
nvm use 22
yarn install
yarn build
```

## Service startup

### Loama application
1. Start the backend services by following the initialization script in the `SolidLabResearch/user-managed-access` README.
2. Launch the development server:
```sh
npm run dev
```

### UMA server
```sh
yarn start
```

## Mock policy injection

If no policy matches the target resource, the user interface hides the access request. Run these commands to insert initial policies.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also elaborate quickly for each policy what they do.
I got confused while following the approval flow: why does alice also have append access?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is append since there has to be atleast one policy present and validating read-access after the request is simpler than update


### Windows (PowerShell)


Give Alice append access to Bob's readme, *(This is append since Alice requests read-permissions later in the demo)*
```powershell
curl.exe --location 'http://localhost:4000/uma/policies' --header 'Authorization: WebID http%3A%2F%2Flocalhost%3A3000%2Fbob%2Fprofile%2Fcard%23me' --header 'Content-Type: text/plain' --data-raw '@prefix ex: <http://example.org/>. @prefix odrl: <http://www.w3.org/ns/odrl/2/> . @prefix dct: <http://purl.org/dc/terms/>. ex:policy a odrl:Agreement ; odrl:uid ex:policy ; odrl:permission ex:permission .ex:permission a odrl:Permission ; odrl:action odrl:append ; odrl:target <http://localhost:3000/bob/README> ; odrl:assignee <http://localhost:3000/alice/profile/card#me> ; odrl:assigner <http://localhost:3000/bob/profile/card#me> .'
```

Give bob read acces to Alice's readme
```powershell
curl.exe --location 'http://localhost:4000/uma/policies' --header 'Authorization: WebID http%3A%2F%2Flocalhost%3A3000%2Falice%2Fprofile%2Fcard%23me' --header 'Content-Type: text/turtle' --data-raw '@prefix ex: <http://example.org/>. @prefix odrl: <http://www.w3.org/ns/odrl/2/> . @prefix dct: <http://purl.org/dc/terms/>. ex:policy1 a odrl:Agreement ; odrl:uid ex:policy1 ; odrl:permission ex:permission1 . ex:permission1 a odrl:Permission ; odrl:action odrl:read ; odrl:target <http://localhost:3000/alice/README> ; odrl:assignee <http://localhost:3000/bob/profile/card#me> ; odrl:assigner <http://localhost:3000/alice/profile/card#me> .'
```

### Unix (Bash/Zsh)

Give Alice append access to Bob's readme, *(This is append since Alice requests read-permissions later in the demo)*
```sh
curl --location 'http://localhost:4000/uma/policies' --header 'Authorization: WebID http%3A%2F%2Flocalhost%3A3000%2Fbob%2Fprofile%2Fcard%23me' --header 'Content-Type: text/plain' --data-raw '@prefix ex: <http://example.org/>. @prefix odrl: <http://www.w3.org/ns/odrl/2/> . @prefix dct: <http://purl.org/dc/terms/>. ex:policy a odrl:Agreement ; odrl:uid ex:policy ; odrl:permission ex:permission .ex:permission a odrl:Permission ; odrl:action odrl:append ; odrl:target <http://localhost:3000/bob/README> ; odrl:assignee <http://localhost:3000/alice/profile/card#me> ; odrl:assigner <http://localhost:3000/bob/profile/card#me> .'
```

Give bob read acces to Alice's readme
```sh
curl --location 'http://localhost:4000/uma/policies' --header 'Authorization: WebID http%3A%2F%2Flocalhost%3A3000%2Falice%2Fprofile%2Fcard%23me' --header 'Content-Type: text/turtle' --data-raw '@prefix ex: <http://example.org/>. @prefix odrl: <http://www.w3.org/ns/odrl/2/> . @prefix dct: <http://purl.org/dc/terms/>. ex:policy1 a odrl:Agreement ; odrl:uid ex:policy1 ; odrl:permission ex:permission1 . ex:permission1 a odrl:Permission ; odrl:action odrl:read ; odrl:target <http://localhost:3000/alice/README> ; odrl:assignee <http://localhost:3000/bob/profile/card#me> ; odrl:assigner <http://localhost:3000/alice/profile/card#me> .'
```

## Verification workflows

### Session setup
1. Open `http://localhost:5173/` in your standard browser window, use IDP `http://localhost:3000` and authenticate as Bob (`bob@example.org`, `abc123`).
2. Open a private/incognito browser window and authenticate as Alice (`alice@example.org`, `abc123`).

### Testing the denial flow
1. From Alice's session, navigate to the **Request Access** tab and request access to Bob's resource `http://localhost:3000/bob/README`.
2. In Bob's session, navigate to the **Grant Access** tab.
3. Click **Deny**.
4. Confirm that the request is removed from Bob's pending list.

### Testing the approval flow
1. From Alice's session, submit another access request to Bob's resource `http://localhost:3000/bob/README`.
2. In Bob's session under the **Grant Access** tab, click **Accept**.
3. Verify that the request appears in Alice's **Accepted** tab.
4. Verify that the request appears in Bob's **Accepted** tab.

## Known bugs and limitations

* **UI State Sync:** When you update a policy on a selected resource, the interface does not visually refresh until you manually deselect and reselect that resource.

## Direct API validation

### Fallback access request generation (PowerShell)
If the UI buttons fail to create an access request, force execution via the API:

```powershell
curl.exe --% --location "http://localhost:4000/uma/requests" --header "Authorization: WebID http%3A%2F%2Flocalhost%3A3000%2Falice%2Fprofile%2Fcard%23me" --header "Content-Type: application/json" --data "{\"resource_id\": \"http://localhost:3000/bob/README\", \"resource_scopes\": [\"http://www.w3.org/ns/odrl/2/read\", \"http://www.w3.org/ns/odrl/2/write\"]}"
```
```powershell
curl.exe --% --location "http://localhost:4000/uma/requests" --header "Authorization: WebID http%3A%2F%2Flocalhost%3A3000%2Fbob%2Fprofile%2Fcard%23me" --header "Content-Type: application/json" --data "{\"resource_id\": \"http://localhost:3000/alice/README\",\"resource_scopes\": [ \"http://www.w3.org/ns/odrl/2/read\" ]}"
```

### Request checking
You can evaluate state machine accuracy after an approval using the ``trustflows-client``. *Note we used v0.1.0-alpha.6*

```
npm install trustflows-client@0.1.0-alpha.6
```

```ts
import { getDefaultAuth, configureDefaultAuth } from "trustflows-client";

async function runClientCredentialsFlow() {
configureDefaultAuth({
persistTokens: false,
});

const auth = getDefaultAuth();

try {
console.log("Authenticating via Client Credentials...");

await auth.loginClientCredentials(
"http://localhost:3000/alice/profile/card#me", // Your WebID
"alice@example.org", // Account email
"abc123" // Account password
);

console.log("Authentication successful.");

const authFetch = auth.createAuthFetch();

const targetUrl = "http://localhost:3000/bob/README";
console.log(`Fetching: ${targetUrl}`);

const response = await authFetch(targetUrl);
const data = await response.text();

console.log("Response status:", response.status);
console.log("Data payload:", data);

} catch (error) {
console.error("Execution failed:", error);
}
}

runClientCredentialsFlow();
```
Loading