Skip to content
This repository was archived by the owner on May 25, 2021. It is now read-only.

Commit

Permalink
Merge pull request #108 from inrupt/feature/notifications-acl-integra…
Browse files Browse the repository at this point in the history
…tion

Integration of ACL library
  • Loading branch information
james-martin-jd authored Jul 24, 2019
2 parents d9a9e22 + e629f6c commit 52cd6a8
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 131 deletions.
4 changes: 2 additions & 2 deletions src/demo/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ const App = () => {
const createAcl = async () => {
if (webId) {
const uri = new URL(webId);
const documentURI = `${uri.origin}/public/test`;
const documentURI = `${uri.origin}/public/container`;
const { MODES } = AccessControlList;
const permissions = [{ modes: [MODES.CONTROL], agents: [webId] }];
const aclInstance = new AccessControlList(webId, documentURI);
await aclInstance.createACLFile(permissions);
await aclInstance.createACL(permissions);
}
};

Expand Down
51 changes: 28 additions & 23 deletions src/lib/classes/access-control-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Permissions = {
modes: Array<String>
};

class AccessControlList {
export default class AccessControlList {
constructor(owner, documentUri) {
this.owner = owner;
this.documentUri = documentUri;
Expand Down Expand Up @@ -105,18 +105,21 @@ class AccessControlList {
};

/**
* @function createACLFile Creates a file or container with a specific set of acls. Assigns READ, WRITE and CONTROL permissions to the owner by default
* @function createACL Creates a file or container with a specific set of acls. Assigns READ, WRITE and CONTROL permissions to the owner by default
* @param {Array<Permissions> | null} permissions Array of permissions to be added in the acl file
*/
createACLFile = async (permissions = null) => {
await this.createSolidFile(this.documentUri);
if (permissions) {
const permissionList = [
{ agents: this.owner, modes: [PERMISSIONS.READ, PERMISSIONS.WRITE, PERMISSIONS.CONTROL] },
...permissions
];
const body = this.createPermissionsTurtle(permissionList);
await this.createSolidFile(this.aclUri, { body });
createACL = async (permissions = null) => {
try {
if (permissions) {
const permissionList = [
{ agents: this.owner, modes: [PERMISSIONS.READ, PERMISSIONS.WRITE, PERMISSIONS.CONTROL] },
...permissions
];
const body = this.createPermissionsTurtle(permissionList);
return await this.createSolidResource(this.aclUri, { body });
}
} catch (error) {
throw error;
}
};

Expand All @@ -125,7 +128,7 @@ class AccessControlList {
* @param {String} url Url where the solid file has to be created
* @param {Object} options Options to add as part of the native fetch options object
*/
createSolidFile = async (url: String, options: Object = {}) =>
createSolidResource = async (url: String, options: Object = {}) =>
solid.fetch(url, {
method: 'PUT',
headers: {
Expand Down Expand Up @@ -306,19 +309,21 @@ class AccessControlList {
* @param {Array<Permissions> | null | String} permissionss An array of permissions to be removed
*/
removePermissions = async permissions => {
const aclPermissions = await this.getPermissions();
for await (const permission of permissions) {
const { modes, agents } = permission;
const modeExists = aclPermissions.filter(per => this.isSameMode(per.modes, modes));
if (modeExists.length > 0) {
const mode = modeExists[0];
const agentsExists = mode.agents.filter(agent => agents.includes(agent));
for await (const agent of agentsExists) {
await this.removePermissionsFromMode(mode, agent);
try {
const aclPermissions = await this.getPermissions();
for await (const permission of permissions) {
const { modes, agents } = permission;
const modeExists = aclPermissions.filter(per => this.isSameMode(per.modes, modes));
if (modeExists.length > 0) {
const mode = modeExists[0];
const agentsExists = mode.agents.filter(agent => agents.includes(agent));
for await (const agent of agentsExists) {
await this.removePermissionsFromMode(mode, agent);
}
}
}
} catch (e) {
throw e;
}
};
}

export default AccessControlList;
128 changes: 23 additions & 105 deletions src/lib/classes/notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import N3 from 'n3';
import solidLDflex from '@solid/query-ldflex';
import { solidResponse, SolidError, getBasicPod } from '@utils';
import defaultShape from '../shapes/notification.json';
import AccessControlList from './access-control-list';

const PREFIXES = {
terms: 'https://www.w3.org/ns/solid/terms#',
Expand Down Expand Up @@ -115,15 +116,16 @@ export class Notification {
* @param owner
* @returns {Promise<*>}
*/

createInbox = async (inboxPath, appPath, settingFileName = 'settings.ttl') => {
try {
const newInboxPath = inboxPath.endsWith('/') ? inboxPath : `${inboxPath}/`;
const newAppPath = appPath.endsWith('/') ? appPath : `${appPath}/`;
/**
* Check if inbox already exists or not in the target path.
* @type {*|boolean}
*/
const hasInbox = await this.hasInbox(inboxPath);
const appSettingPat = `${appPath}${settingFileName}`;
const hasInbox = await this.hasInbox(newInboxPath);
const appSettingPath = `${newAppPath}${settingFileName}`;
/**
* if container exist will return message without changes.
*/
Expand All @@ -132,119 +134,35 @@ export class Notification {
if (!this.owner) throw new SolidError('Owner is undefined', 'Inbox', 500);

/**
* Start to build ACL file to add access to owner and users to inbox container
* To know more about ACL please go to: https://github.com/solid/web-access-control-spec
*/
const termFactory = N3.DataFactory;
const { namedNode } = termFactory;
const writer = new N3.Writer({
prefixes: {
ns: PREFIXES.ns,
foaf: PREFIXES.foaf,
acl: PREFIXES.acl
},
format: 'text/turtle'
});

/**
* Add Quad type Authorization
*/
writer.addQuad(namedNode('#owner'), namedNode('ns:type'), namedNode('acl:Authorization'));
/**
* Add agent permission to owner
*/
writer.addQuad(namedNode('#owner'), namedNode('acl:agent'), namedNode(this.owner));
/**
* Add access reference to the container folder
* Create inbox reference to be discovered in the pod into settings.ttl
*/
writer.addQuad(namedNode('#owner'), namedNode('acl:accessTo'), namedNode('./'));
writer.addQuad(namedNode('#owner'), namedNode('acl:defaultForNew'), namedNode('./'));
const settingsResult = await this.settingsTurtle(appSettingPath, newInboxPath);

/**
* Add roles to owner Read, Write and Control
* Check if settings reference was created it if not we will try one time more.
*/
writer.addQuad(
namedNode('#owner'),
namedNode('acl:mode'),
namedNode('acl:Read, acl:Write, acl:Control')
);
if (!settingsResult.ok) await this.settingsTurtle(appSettingPath, newInboxPath);

/**
* Add permissions to public users
* Create inbox container
*/
writer.addQuad(namedNode('#public'), namedNode('ns:type'), namedNode('acl:Authorization'));

writer.addQuad(
namedNode('#public'),
namedNode('acl:agentClass'),
namedNode('http://xmlns.com/foaf/0.1/Agent')
);
/**
* Add access reference to the container folder
*/
writer.addQuad(namedNode('#public'), namedNode('acl:accessTo'), namedNode('./'));
const resultInbox = await solid.fetch(`${newInboxPath}.dummy`, {
method: 'PUT',
headers: {
'Content-Type': 'text/turtle'
}
});

writer.addQuad(namedNode('#public'), namedNode('acl:defaultForNew'), namedNode('./'));
/**
* Add roles to public Append
* If create inbox fail we return an error message
*/
writer.addQuad(namedNode('#public'), namedNode('acl:mode'), namedNode('acl:Append'));

await writer.end(async (error, result) => {
if (error) {
throw error;
}
if (!resultInbox.ok)
throw new SolidError('Error when tried to create an inbox', 'Error', resultInbox.status);

/**
* Create inbox container
*/
const resultInbox = await solid.fetch(`${inboxPath}.dummy`, {
method: 'PUT',
headers: {
'Content-Type': 'text/turtle'
}
});

/**
* If create inbox fail we return an error message
*/
if (!resultInbox.ok)
throw new SolidError(
result.message || 'Error when tried to create an inbox',
'Error',
resultInbox.status
);

await solid.fetch(`${inboxPath}.dummy`, { method: 'DELETE' });

/**
* Create inbox reference to be discovered in the pod into settings.ttl
*/
const settingsResult = await this.settingsTurtle(appSettingPat, inboxPath);

/**
* Check if settings reference was created it if not we will try one time more.
*/
if (!settingsResult.ok) await this.settingsTurtle(appSettingPat, inboxPath);

/**
* Create a default ACL for inbox container
*/
const resultAcl = await solid.fetch(`${inboxPath}.acl`, {
method: 'PUT',
body: result,
headers: {
'Content-Type': 'text/turtle'
}
});

if (!resultAcl.ok)
throw new SolidError(
result.message || 'An error when tried to assign permissions',
'Error',
resultAcl.status
);
});
await solid.fetch(`${newInboxPath}.dummy`, { method: 'DELETE' });
const permissions = [{ agents: null, modes: [AccessControlList.MODES.APPEND] }];
const aclContainer = new AccessControlList(this.owner, newInboxPath);
await aclContainer.createACL(permissions);

return solidResponse(200, 'Inbox was created');
} catch (error) {
Expand Down
5 changes: 4 additions & 1 deletion src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {

import { useNotification } from '@hooks';

import { AccessControlList } from '@classes';

export {
ProviderLogin,
PrivateRoute,
Expand All @@ -36,5 +38,6 @@ export {
useLatestUpdate,
ShexForm,
ShexFormBuilder,
useNotification
useNotification,
AccessControlList
};

0 comments on commit 52cd6a8

Please sign in to comment.