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
63 changes: 24 additions & 39 deletions controller/src/classes/OdrlController.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { getDefaultSession } from "@inrupt/solid-client-authn-browser";
import { BaseSubject, Index, IndexItem, Permission, ResourcePermissions, Resources } from "../types";
import { IAccessRequest, IController, IInboxConstructor, IStore, IStoreConstructor, SubjectConfig, SubjectConfigs, SubjectKey, SubjectType } from "../types/modules";
import { type AccessRequest as AccessRequestObject } from "../types/modules";
import { type AccessRequest as AccessRequestObject, Policy, Rule, RuleUpdate } from "../types/modules";
import { AccessRequest } from "./accessRequests/AccessRequest";
import { ODRLAccessRequest } from "./accessRequests/OdrlAccessRequest";
import { Mutex } from "./utils/Mutex";
import { ODRLAccessRequestService } from "./utils/OdrlAccessRequestService";
import { ODRLPolicyService } from "./utils/OdrlPolicyService";
import { PolicyInterpreter } from "./utils/PolicyInterpreter";

/**
* Controller which makes it calls to the backend AS through ODRL requests.
Expand Down Expand Up @@ -78,52 +80,34 @@ export class ODRLController<T extends Record<keyof T, BaseSubject<keyof T & stri
} as IndexItem<T[K]>
}

async addPermission<K extends SubjectKey<T>>(resourceUrl: string, addedPermission: Permission, subject: SubjectType<T, K>) {
const release = await this.acquire();
try {

// 1. Create a new permission for the subject
await this.getSubjectConfig(subject).manager.createPermissions(resourceUrl, subject, [addedPermission])

// 2. Let the manager add the permission, return the updated version
const webId = getDefaultSession().info.webId!;
const permissions = await this.getSubjectConfig(subject).manager.getTargetPermissionsForUser(webId, subject.selector?.url ?? "", resourceUrl);

return permissions;
} catch (e) {
throw e;
} finally {
release();
}
}

async removeSubject<K extends SubjectKey<T>>(resourceUrl: string, subject: SubjectType<T, K>) {
const subjectConfig = this.getSubjectConfig(subject);
const item = await this.getItem(resourceUrl, subject);

await subjectConfig.manager.deletePermissions(resourceUrl, subject, item?.permissions ?? []);
}

async removePermission<K extends SubjectKey<T>>(resourceUrl: string, removedPermission: Permission, subject: SubjectType<T, K>) {
const release = await this.acquire()
try {

// 1. Delete a permission for the subject
await this.getSubjectConfig(subject).manager.deletePermissions(resourceUrl, subject, [removedPermission]);

// 2. Let the manager delete the permission, return the updated version
const webId = getDefaultSession().info.webId!;
const permissions = await this.getSubjectConfig(subject).manager.getTargetPermissionsForUser(webId, subject.selector!.url, resourceUrl);

return permissions
async updatePolicy(updates: RuleUpdate[]): Promise<void> {
console.log("updatePolicy");
const webId = getDefaultSession().info.webId!;
const service = new ODRLPolicyService(this.authorizationServerURL);
// Applied sequentially so a later update in the batch can safely depend on
// an earlier one having landed (e.g. edit right after add of the same rule).
for (const update of updates) {
await service.applyRuleUpdate(webId, update);
}
}

} catch (error) {
return []
} finally {
release();
async getResourcePolicies(resourceUrl: string): Promise<Policy[]> {
const webId = getDefaultSession().info.webId;
if (!webId) {
throw new Error("User not logged in");
}
const store = await new ODRLPolicyService(this.authorizationServerURL).fetchPolicies(webId);
return new PolicyInterpreter().storeToPolicies(store, resourceUrl);
}


async enablePermissions<K extends SubjectKey<T>>(resource: string, subject: SubjectType<T, K>) {
// won't fix
}
Expand Down Expand Up @@ -156,9 +140,10 @@ export class ODRLController<T extends Record<keyof T, BaseSubject<keyof T & stri
}

// ! added for access requests
async requestAccess(permission: { action: string; resource: string; }): Promise<void> {
async requestAccess(permission: { accessRequest: AccessRequestObject}): Promise<void> {
const webid = getDefaultSession().info.webId!;
await new ODRLAccessRequestService(this.authorizationServerURL).requestAccess(permission.resource, webid, permission.action);
permission.accessRequest.requestingParty = webid;
await new ODRLAccessRequestService(this.authorizationServerURL).requestAccess(permission.accessRequest);
}

async handleAccessRequest(requestId: string, status: 'accepted' | 'denied'): Promise<void> {
Expand All @@ -172,4 +157,4 @@ export class ODRLController<T extends Record<keyof T, BaseSubject<keyof T & stri
}> {
return new ODRLAccessRequestService(this.authorizationServerURL).retrieveAccessRequests(getDefaultSession().info.webId!);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export abstract class ODRLPermissionManager<T extends Record<keyof T, BaseSubjec


public async getRemotePermissions<K extends SubjectKey<T>>(resourceUrl: string): Promise<SubjectPermissions<T[K]>[]> {
console.log("getRemotePermission");
// Extract our webID
const session = getDefaultSession();
const webId = session.info.webId;
Expand All @@ -108,10 +109,14 @@ export abstract class ODRLPermissionManager<T extends Record<keyof T, BaseSubjec
// Retrieve our policies
const store = await new ODRLPolicyService(this.authorizationServerURL).fetchPolicies(webId);

console.log(store);

// Get detailed info about the target
const interpreter = new PolicyInterpreter();
const target: TargetSubjects = interpreter.permissionsForOneResource(resourceUrl, store);


console.log(target);

if (target) {
const subjectPermissions: SubjectPermissions<T[K]>[] = [];
Expand Down Expand Up @@ -178,7 +183,8 @@ export abstract class ODRLPermissionManager<T extends Record<keyof T, BaseSubjec
permissionsPerSubject: perms
})
}

console.log("resourcePermissions");
console.log(resourcePermissions);
return resourcePermissions;
}

Expand Down
71 changes: 42 additions & 29 deletions controller/src/classes/utils/OdrlAccessRequestService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AccessRequest } from "@/types/modules";
import { Constraint, AccessRequest } from "@/types/modules";
import { QueryEngine } from "@comunica/query-sparql";
import { Parser, Store } from "n3";
import { Parser, Store, Writer } from "n3";
import { v4 as uuid } from 'uuid';

export class ODRLAccessRequestService {
Expand All @@ -18,40 +18,47 @@ export class ODRLAccessRequestService {
* @param requestingParty - user credentials of the requesting party
* @param action - the action the user wants to perform on the resource
*/
public requestAccess = async (resourceURL: string, requestingParty: string, action: string): Promise<void> => {
public requestAccess = async (accessRequest: AccessRequest): Promise<void> => {
const response = await fetch(
`${this.authorizationServerURL}/requests`, {
method: 'POST',
headers: {
'authorization': `WebID ${encodeURIComponent(requestingParty)}`,
'content-type': 'text/turtle'
}, body: await this.accessRequestToTtl({
uid: uuid(),
target: resourceURL,
action: action,
requestingParty: requestingParty,
status: 'requested'
})
'authorization': `WebID ${encodeURIComponent(accessRequest.requestingParty)}`
}, body: await this.accessRequestToJson(accessRequest)
}
);

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> => {
const payload: any = {
resource_id: accessRequest.target,
resource_scopes: [
accessRequest.action.startsWith('http')
? accessRequest.action
: `http://www.w3.org/ns/odrl/2/${accessRequest.action}`
]
};

const constraintsList: any[] = [];

if (accessRequest.constraint && accessRequest.constraint.length > 0) {
accessRequest.constraint.forEach(con => {
constraintsList.push([
con.LeftOperand,
con.Operand,
con.RightOperand
]);
});
}

if (constraintsList.length > 0) {
payload.constraints = constraintsList;
}

return JSON.stringify(payload, null, 12);
};

/**
* Place a PATCH request to update an access request to an UMA backend
Expand Down Expand Up @@ -126,6 +133,7 @@ export class ODRLAccessRequestService {
uid: binding.get('uid')?.value!,
target: binding.get('target')?.value!,
action: this.cleanValue(binding.get('action')?.value),
constraint: [], //ToDO
requestingParty: binding.get('requestingParty')?.value!,
status: this.cleanValue(binding.get('status')?.value),
})
Expand All @@ -134,9 +142,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 +164,7 @@ export class ODRLAccessRequestService {
sotw:requestedTarget ?target ;
sotw:requestedAction ?action ;
sotw:requestingParty <${requestingPartyID}> ;
ex:requestStatus ?status .
sotw:requestStatus ?status .
}
`;

Expand All @@ -169,7 +182,7 @@ export class ODRLAccessRequestService {
sotw:requestedTarget ?target ;
sotw:requestedAction ?action ;
sotw:requestingParty ?requestingParty ;
ex:requestStatus ?status .
sotw:requestStatus ?status .
}
`;
}
25 changes: 23 additions & 2 deletions controller/src/classes/utils/OdrlPolicyService.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { getDefaultSession } from "@inrupt/solid-client-authn-browser";
import { Permission } from "../../types";
import { RuleUpdate } from "../../types/modules";
import { ODRL, PolicyParser } from "./PolicyParser";
import { DataFactory } from "n3";
const { namedNode } = DataFactory;

const RULE_TYPE_TERM: Record<string, string> = {
permission: 'Permission',
prohibition: 'Prohibition',
duty: 'Duty'
};

export const UMA_URL = (authorizationServerURL: string, encodedId: string = "") =>
`${authorizationServerURL}/policies${encodedId}`;

Expand All @@ -25,7 +32,6 @@ export class ODRLPolicyService {
}

public async fetchPolicies(webId: string) {

// Get all our policies
const response = await fetch(UMA_URL(this.authorizationServerURL), {
headers: {
Expand All @@ -34,6 +40,7 @@ export class ODRLPolicyService {
}
});


// Extract the target Ids
const turtleText = await response.text();

Expand Down Expand Up @@ -147,6 +154,20 @@ WHERE {}`)
}
}

/**
* Applies a single rule-level change (add, edit, or remove) to a policy.
* This is the generalized replacement for the old addPermission/removePermission
* flow: instead of editing a flattened subject/permission pair, it edits the
* actual ODRL rule node identified by rule.id.
*
* NOTE: the delete-then-insert body for 'edit' sends two update operations in
* one PATCH request. This follows standard SPARQL 1.1 Update syntax but has not
* been verified against your authorization server, confirm it accepts this shape.
*/
public async applyRuleUpdate(webId: string, update: RuleUpdate): Promise<void> {
console.log("applyRuleUpate");
}

/**
* Funcion that searches every owned rule by the logged on client, finds the target
* of an assigner and deletes the actions on it
Expand Down Expand Up @@ -241,4 +262,4 @@ DELETE {
}
}
}
}
}
Loading