ownacl
Version:
A library to manage access in solid pods.
198 lines (179 loc) • 5.4 kB
JavaScript
const rdf = require("rdflib");
const ns = require("solid-namespace")(rdf);
const url = require("url");
function makeEntity(entity, options = {}) {
if (typeof entity !== "object") {
throwError(
"The specified entity needs to be an object. Received: " + typeof entity
);
}
if (!entity.name) {
throwError("The specified entity doesn't have a name property");
}
const agentUrl = url.parse(entity.name);
if (
(agentUrl.protocol !== "https:" && agentUrl.protocol !== "http:") ||
!agentUrl.host ||
!agentUrl.path
) {
throwError(
"Please specify a valid url in the name property of the specified entity. Received: " +
entity.name
);
}
entity.type = makeType(entity, options);
entity.access = makeAccess(entity.access);
return entity;
}
function getDefaultAclLocation(resource) {
const aclUrlObject = url.parse(resource);
aclUrlObject.pathname =
(aclUrlObject.pathname.endsWith("/")
? aclUrlObject.pathname.substr(
0,
aclUrlObject.pathname
.substr(0, aclUrlObject.pathname.length - 1)
.lastIndexOf("/") + 1
)
: aclUrlObject.pathname.substr(
0,
aclUrlObject.pathname
.substr(0, aclUrlObject.pathname.lastIndexOf("/"))
.lastIndexOf("/") + 1
)) + ".acl";
return url.format(aclUrlObject);
}
function makeType(entity, options) {
const agentUrl = url.parse(entity.name);
const validTypes = ["Origin", "AgentGroup", "Agent"];
if (validTypes.lastIndexOf(entity.type) === -1) {
entity.type = "Agent";
}
if (options.agentGroup) {
entity.type = "AgentGroup";
} else if (options.origin) {
entity.type = "Origin";
}
if (entity.type === "Agent") {
if (agentUrl.path !== "/profile/card") {
throwError(
"Please specify a valid webId in the name property of the specified entity. Received: " +
entity.name
);
}
}
return entity.type;
}
function makeAccess(access) {
if (typeof access === "string") access = [access];
const accessModes = {
Read: "http://www.w3.org/ns/auth/acl#Read",
Write: "http://www.w3.org/ns/auth/acl#Write",
Control: "http://www.w3.org/ns/auth/acl#Control",
Append: "http://www.w3.org/ns/auth/acl#Append",
};
return access.map((accessMode) => {
if (Object.keys(accessModes).lastIndexOf(accessMode) !== -1)
return accessModes[accessMode];
if (Object.values(accessModes).lastIndexOf(accessMode) !== -1)
return accessMode;
throwError(
"The specified access is invalid. Received: " +
accessMode +
"\nValid types include: " +
Object.keys(accessModes)
);
});
}
function getIdentifier(entity, accessees, resource) {
let identifier;
accessees = accessees.filter((accessee) => {
return JSON.stringify(accessee.access) === JSON.stringify(entity.access);
});
if (accessees.length > 0) {
identifier = entity.identifier
? resource + "#" + entity.identifier.split("#")[1]
: resource + "#" + accessees[0].identifier.split("#")[1];
} else {
identifier =
resource +
"#" +
entity.access
.map((access) => access.split("#")[1])
.sort()
.join("");
}
return identifier;
}
function getExistingAccess(store, entity, options = {}) {
const agentPredicate = getAgentPredicate(entity);
if (store.any(null, agentPredicate, rdf.sym(entity.name))) {
const currIdentifiers = store
.statementsMatching(null, agentPredicate, rdf.sym(entity.name))
.map((statement) => {
return statement.subject.value;
});
if (currIdentifiers.length > 1) {
let triples = [];
currIdentifiers.forEach((identifier) => {
if (options.force || identifier !== entity.identifier)
triples = triples.concat(
getExistingAccessTriples(store, entity, identifier)
);
});
return triples;
} else {
if (options.force || currIdentifiers[0] !== entity.identifier)
return getExistingAccessTriples(store, entity, currIdentifiers[0]);
}
}
return [];
}
function getExistingAccessTriples(store, entity, identifier) {
const agentPredicate = getAgentPredicate(entity);
const agentsOfIdentifier = getAgentsOfIdentifier(store, identifier);
if (
JSON.stringify(agentsOfIdentifier) ===
JSON.stringify(
store.statementsMatching(
rdf.sym(identifier),
agentPredicate,
rdf.sym(entity.name)
)
)
) {
return store.statementsMatching(rdf.sym(identifier));
} else {
return store.statementsMatching(
rdf.sym(identifier),
agentPredicate,
rdf.sym(entity.name)
);
}
}
function getAgentsOfIdentifier(store, identifier) {
return store
.statementsMatching(rdf.sym(identifier), ns.acl("agent"))
.concat(
store.statementsMatching(rdf.sym(identifier), ns.acl("agentClass")),
store.statementsMatching(rdf.sym(identifier), ns.acl("origin"))
);
}
function getAgentPredicate(entity) {
return entity.type !== "Origin"
? entity.type === "Agent"
? ns.acl("agent")
: ns.acl("agentClass")
: ns.acl("origin");
}
function throwError(error) {
throw new Error(error);
}
module.exports = {
makeEntity,
getDefaultAclLocation,
getIdentifier,
getExistingAccess,
getAgentPredicate,
throwError,
};