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

Integration of ACL library #108

Merged
merged 2 commits into from
Jul 24, 2019
Merged
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
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
};