Skip to content

Commit f5772a9

Browse files
feat: Introduce get All policies endpoint
Introducing a read only endpoint in the AS `baseURL:port/policies` Very basic (insecure) authentication added via Authorization token with a string (representing an URL, more specific a WebID) Only policies are retrieved to which the requesting party (via the Authz string) match the `odrl:assignee`
2 parents a076b70 + abc7f38 commit f5772a9

File tree

12 files changed

+295
-2
lines changed

12 files changed

+295
-2
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"script:registration": "yarn exec ts-node ./scripts/test-registration.ts",
6666
"script:uma-ucp": "yarn exec ts-node ./scripts/test-uma-ucp.ts",
6767
"script:uma-odrl": "yarn exec ts-node ./scripts/test-uma-ODRL.ts",
68+
"script:uma-odrl-policy": "yarn exec ts-node ./scripts/test-uma-ODRL-policy.ts",
6869
"script:flow": "yarn run script:public && yarn run script:private && yarn run script:uma-ucp && yarn run script:registration",
6970
"sync:list": "syncpack list-mismatches",
7071
"sync:fix": "syncpack fix-mismatches"
@@ -169,4 +170,4 @@
169170
"jsonld": "^8.3.3",
170171
"tsx": "^4.19.2"
171172
}
172-
}
173+
}

packages/ucp/src/util/Vocabularies.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ export const ODRL = createVocabulary(
114114
'Agreement',
115115
'Offer',
116116
'Permission',
117+
'Prohibition',
118+
'Duty',
117119
'Request',
118120
'action',
119121
'target',
@@ -122,6 +124,8 @@ export const ODRL = createVocabulary(
122124
'constraint',
123125
'operator',
124126
'permission',
127+
'prohibition',
128+
'duty',
125129
'dateTime',
126130
'purpose',
127131
'leftOperand',
@@ -137,4 +141,4 @@ export const XSD = createVocabulary(
137141
'duration',
138142
'integer',
139143
'string',
140-
);
144+
);

packages/uma/config/default.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"sai-uma:config/routes/tokens.json",
1717
"sai-uma:config/routes/log.json",
1818
"sai-uma:config/routes/vc.json",
19+
"sai-uma:config/routes/policies.json",
1920
"sai-uma:config/routes/contract.json",
2021
"sai-uma:config/tickets/storage/default.json",
2122
"sai-uma:config/tickets/strategy/immediate-authorizer.json",
@@ -97,6 +98,7 @@
9798
{ "@id": "urn:uma:default:UmaConfigRoute" },
9899
{ "@id": "urn:uma:default:JwksRoute" },
99100
{ "@id": "urn:uma:default:TokenRoute" },
101+
{ "@id": "urn:uma:default:PolicyRoute" },
100102
{ "@id": "urn:uma:default:PermissionRegistrationRoute" },
101103
{ "@id": "urn:uma:default:ResourceRegistrationRoute" },
102104
{ "@id": "urn:uma:default:ResourceRegistrationOpsRoute" },
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"@context": [
3+
"https://linkedsoftwaredependencies.org/bundles/npm/@solidlab/uma/^0.0.0/components/context.jsonld"
4+
],
5+
"@graph": [
6+
{
7+
"@id": "urn:uma:default:PolicyRoute",
8+
"@type": "HttpHandlerRoute",
9+
"methods": [
10+
"GET"
11+
],
12+
"handler": {
13+
"@type": "PolicyRequestHandler",
14+
"storage": {
15+
"@id": "urn:uma:default:RulesStorage"
16+
}
17+
},
18+
"path": "/uma/policies"
19+
}
20+
]
21+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
############################################
2+
# File for Policy endpoint Testing Purposes
3+
# this will serve to test some edge cases
4+
############################################
5+
6+
7+
@prefix ex: <http://example.org/>.
8+
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
9+
10+
ex:usagePolicy1 a odrl:Agreement .
11+
ex:usagePolicy1 odrl:permission ex:permission1 .
12+
ex:usagePolicy1 odrl:permission ex:permission1b .
13+
ex:permission1 a odrl:Permission .
14+
ex:permission1 odrl:action odrl:modify .
15+
ex:permission1 odrl:target <http://localhost:3000/alice/other/resource.txt> .
16+
ex:permission1 odrl:assignee <https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me> .
17+
ex:permission1 odrl:assigner <https://pod.a.com/profile/card#me> .
18+
ex:permission1b a odrl:Permission .
19+
ex:permission1b odrl:action odrl:modify .
20+
ex:permission1b odrl:target <http://localhost:3000/alice/other/resource.txt> .
21+
ex:permission1b odrl:assignee <https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me> .
22+
ex:permission1b odrl:assigner <https://pod.b.com/profile/card#me> .
23+
24+
ex:usagePolicy1a a odrl:Agreement .
25+
ex:usagePolicy1a odrl:permission ex:permission1a .
26+
ex:permission1a a odrl:Permission .
27+
ex:permission1a odrl:action odrl:create .
28+
ex:permission1a odrl:target <http://localhost:3000/alice/other/resource.txt> .
29+
ex:permission1a odrl:assignee <https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me> .
30+
ex:permission1a odrl:assigner <https://pod.example.com/profile/card#me> .
31+
32+
ex:usagePolicy2 a odrl:Agreement .
33+
ex:usagePolicy2 odrl:permission ex:permission2a .
34+
ex:permission2 a odrl:Permission .
35+
ex:permission2 odrl:action odrl:modify .
36+
ex:permission2 odrl:target <http://localhost:3000/alice/other/> .
37+
ex:permission2 odrl:assignee <https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me> .
38+
ex:permission2 odrl:assigner <https://pod.woutslabbinck.com/profile/card#me> .
39+
40+
ex:usagePolicy2a a odrl:Agreement .
41+
ex:usagePolicy2a odrl:permission ex:permission2 .
42+
ex:permission2a a odrl:Permission .
43+
ex:permission2a odrl:action odrl:create .
44+
ex:permission2a odrl:target <http://localhost:3000/alice/other/> .
45+
ex:permission2a odrl:assignee <https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me> .
46+
ex:permission2a odrl:assigner <https://pod.example.com/profile/card#me> .
47+
48+
49+
ex:usagePolicy3 a odrl:Agreement .
50+
ex:usagePolicy3 odrl:permission ex:permission3 .
51+
ex:permission3 a odrl:Permission .
52+
ex:permission3 odrl:action odrl:read .
53+
ex:permission3 odrl:target <http://localhost:3000/alice/other/resource.txt> .
54+
ex:permission3 odrl:assignee <https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me> .
55+
ex:permission3 odrl:assigner <https://pod.woutslabbinck.com/profile/card#me> .

packages/uma/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export * from './routes/Config';
4141
export * from './routes/Log';
4242
export * from './routes/VC';
4343
export * from './routes/Contract';
44+
export * from './routes/Policy';
4445

4546
// Tickets
4647
export * from './ticketing/Ticket';
@@ -73,3 +74,5 @@ export * from './util/ConvertUtil';
7374
export * from './util/HttpMessageSignatures';
7475
export * from './util/Result';
7576
export * from './util/ReType';
77+
export * from './util/routeSpecific/policies/GetPolicies';
78+
export * from './util/routeSpecific/policies/PolicyUtil';

packages/uma/src/routes/Policy.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { BadRequestHttpError, getLoggerFor, MethodNotAllowedHttpError } from "@solid/community-server";
2+
import { UCRulesStorage } from "@solidlab/ucp";
3+
import { HttpHandlerContext, HttpHandlerResponse, HttpHandler, HttpHandlerRequest } from "../util/http/models/HttpHandler";
4+
import { getPolicies as getPolicies } from "../util/routeSpecific/policies/GetPolicies";
5+
6+
/**
7+
* Endpoint to handle policies, this implementation gives all policies that have the
8+
* client as assigner.
9+
*/
10+
export class PolicyRequestHandler extends HttpHandler {
11+
12+
protected readonly logger = getLoggerFor(this);
13+
14+
constructor(
15+
private readonly storage: UCRulesStorage
16+
) {
17+
super();
18+
}
19+
20+
/**
21+
* This function takes the GET-request with `Authorization: webID` and extracts the webID
22+
* (To be altered with actual Solid-OIDC)
23+
*
24+
* @param request the request with the client 'id' as body
25+
* @returns the client id
26+
*/
27+
protected getCredentials(request: HttpHandlerRequest): string {
28+
const header = request.headers['authorization'];
29+
if (typeof header !== 'string') {
30+
throw new BadRequestHttpError('Missing Authorization header');
31+
}
32+
return header;
33+
}
34+
35+
/**
36+
* Handle every /uma/policies request.
37+
*/
38+
public async handle({ request }: HttpHandlerContext): Promise<HttpHandlerResponse<any>> {
39+
this.logger.info(`Received policy request`);
40+
41+
// Extract client from request
42+
const client = this.getCredentials(request);
43+
const store = await this.storage.getStore();
44+
45+
switch (request.method) {
46+
case 'GET': return getPolicies(request, store, client);
47+
// TODO: add other endpoints
48+
default: throw new MethodNotAllowedHttpError();
49+
}
50+
}
51+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//TODO

packages/uma/src/util/routeSpecific/policies/EditPolicies.ts

Whitespace-only changes.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { HttpHandlerRequest, HttpHandlerResponse } from "../../http/models/HttpHandler";
2+
import { Quad, Store, Writer } from "n3";
3+
import { odrlAssigner, relations, namedNode } from "./PolicyUtil";
4+
import { MethodNotAllowedHttpError } from "@solid/community-server";
5+
6+
/**
7+
* Handling of the GET /uma/policies endpoint
8+
*
9+
* @param request will give all policies when no <id> is fixed in the URL, otherwise it will give the required policy (if allowed)
10+
*/
11+
export async function getPolicies(request: HttpHandlerRequest, store: Store, clientId: string): Promise<HttpHandlerResponse<any>> {
12+
if (request.url.pathname === '/uma/policies')
13+
return getAllPolicies(store, clientId);
14+
15+
// If asked for a policy, validate the policy ID
16+
const args = request.url.pathname.split('/');
17+
if (args.length === 4 && isPolicy(args[-1]))
18+
return getOnePolicy(args[-1], store, clientId);
19+
20+
throw new MethodNotAllowedHttpError();
21+
}
22+
23+
/**
24+
* Function to determine the validity of the <id> of the GET /uma/policies/<id> endpoint (not implemented yet)
25+
*
26+
* @param policyId
27+
* @returns the validity of policyId
28+
*/
29+
function isPolicy(policyId: string): boolean {
30+
// TODO
31+
return true;
32+
}
33+
34+
/**
35+
* Function to implement the GET /uma/policies/<id> endpoint, it retrieves all information about a certain
36+
* policy if available. Yet to be implemented.
37+
*/
38+
function getOnePolicy(policyId: string, store: Store, clientId: string): Promise<HttpHandlerResponse<any>> {
39+
// TODO
40+
return getAllPolicies(store, clientId);
41+
}
42+
43+
44+
/**
45+
* Get all policy information relevant to the client in the request.
46+
* This iplementation searches for all subjects in relation with the policy with depth 1, a deeper algorithm is required.
47+
*
48+
* @param param0 a request with the clients webID as authorization header.
49+
* @returns all policy information (depth 1) relevant to the client
50+
*/
51+
function getAllPolicies(store: Store, clientId: string): Promise<HttpHandlerResponse<any>> {
52+
53+
// Keep track of all the matching policies
54+
const policyDetails: Set<Quad> = new Set();
55+
56+
for (const relation of relations) {
57+
// Collect every quad that matches with the relation (one of Permission, Prohibition or Duty)
58+
const matchingRules = store.getQuads(null, relation, null, null);
59+
60+
// Every quad will represent a policy in relation with a rule
61+
for (const quad of matchingRules) {
62+
// Extract the policy and rule out the quad
63+
const policy = quad.subject;
64+
const rule = quad.object;
65+
66+
// Only go on if the rule is assigned by the client
67+
if (store.getQuads(rule, odrlAssigner, namedNode(clientId), null).length > 0) {
68+
69+
// Because an ODRL policy may only have one assigner, we can now add all policy and rule information
70+
store.getQuads(policy, null, null, null).forEach(quad => policyDetails.add(quad));
71+
store.getQuads(rule, null, null, null).forEach(quad => policyDetails.add(quad));
72+
}
73+
}
74+
}
75+
76+
77+
// Serialize as Turtle
78+
const writer = new Writer({ format: 'Turtle' });
79+
writer.addQuads(Array.from(policyDetails));
80+
81+
return new Promise<HttpHandlerResponse<any>>((resolve, reject) => {
82+
writer.end((error, result) => {
83+
if (error) {
84+
reject(error);
85+
} else {
86+
resolve({
87+
status: 200,
88+
headers: { 'content-type': 'text/turtle' },
89+
body: result
90+
});
91+
}
92+
});
93+
});
94+
}

0 commit comments

Comments
 (0)