@inrupt/solid-client
Version:
Make your web apps work with Solid Pods.
500 lines (497 loc) • 20.9 kB
JavaScript
import { acp, rdf } from '../constants.mjs';
import { isNamedNode } from '../datatypes.mjs';
import { internal_toIriString } from '../interfaces.internal.mjs';
import { getSourceUrl } from '../resource/resource.mjs';
import { addIri } from '../thing/add.mjs';
import { getIriAll } from '../thing/get.mjs';
import { removeAll } from '../thing/remove.mjs';
import { setUrl } from '../thing/set.mjs';
import { createThing, getThing, getThingAll, isThingLocal, removeThing, setThing, asUrl } from '../thing/thing.mjs';
import { getPolicyUrlAll, getAcrPolicyUrlAll, removePolicyUrl, removeAcrPolicyUrl, addPolicyUrl, addAcrPolicyUrl } from './control.mjs';
import { internal_getAcr, internal_setAcr } from './control.internal.mjs';
import { getAllOfRuleUrlAll, getAnyOfRuleUrlAll, getNoneOfRuleUrlAll } from './rule.mjs';
/**
* Copyright 2020 Inrupt Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @param thing the [[Thing]] to check to see if it's an ACP Policy or not
*/
function isPolicy(thing) {
return getIriAll(thing, rdf.type).includes(acp.Policy);
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Initialise a new, empty [[Policy]].
*
* @param url URL that identifies this Policy.
* @since 1.6.0
*/
function createPolicy(url) {
const stringUrl = internal_toIriString(url);
let policyThing = createThing({ url: stringUrl });
policyThing = setUrl(policyThing, rdf.type, acp.Policy);
return policyThing;
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Get the [[Policy]] with the given URL from an [[SolidDataset]].
*
* @param policyResource The Resource that contains the given Policy.
* @param url URL that identifies this Policy.
* @returns The requested Policy, if it exists, or `null` if it does not.
* @since 1.6.0
*/
function getPolicy(policyResource, url) {
const foundThing = getThing(policyResource, url);
if (foundThing === null || !isPolicy(foundThing)) {
return null;
}
return foundThing;
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Get all [[Policy]]'s in a given [[SolidDataset]].
*
* @param policyResource The Resource that contains Access Policies.
* @since 1.6.0
*/
function getPolicyAll(policyResource) {
const foundThings = getThingAll(policyResource);
const foundPolicies = foundThings.filter((thing) => !isThingLocal(thing) && isPolicy(thing));
return foundPolicies;
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Remove the given [[Policy]] from the given [[SolidDataset]].
*
* @param policyResource The Resource that contains Access Policies.
* @param policy The Policy to remove from the resource.
* @since 1.6.0
*/
function removePolicy(policyResource, policy) {
return removeThing(policyResource, policy);
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Insert the given [[Policy]] into the given [[SolidDataset]], replacing previous instances of that Policy.
*
* @param policyResource The Resource that contains Access Policies.
* @param policy The Policy to insert into the Resource.
* @returns A new dataset equal to the given resource, but with the given Policy.
* @since 1.6.0
*/
function setPolicy(policyResource, policy) {
return setThing(policyResource, policy);
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Given a [[Policy]] and a set of [[AccessModes]], return a new Policy based on the given
* Policy, but with the given Access Modes allowed on it.
*
* @param policy The Policy on which to set the modes to allow.
* @param modes Modes to allow for this Policy.
* @since 1.6.0
*/
function setAllowModes(policy, modes) {
let newPolicy = removeAll(policy, acp.allow);
if (modes.read === true) {
newPolicy = addIri(newPolicy, acp.allow, acp.Read);
}
if (modes.append === true) {
newPolicy = addIri(newPolicy, acp.allow, acp.Append);
}
if (modes.write === true) {
newPolicy = addIri(newPolicy, acp.allow, acp.Write);
}
return newPolicy;
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Given a [[Policy]], return which [[AccessModes]] it allows.
*
* @param policy The Policy for which you want to know the Access Modes it allows.
* @since 1.6.0
*/
function getAllowModes(policy) {
const allowedModes = getIriAll(policy, acp.allow);
return {
read: allowedModes.includes(acp.Read),
append: allowedModes.includes(acp.Append),
write: allowedModes.includes(acp.Write),
};
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Given a [[Policy]] and a set of [[AccessModes]], return a new Policy based on the given
* Policy, but with the given Access Modes disallowed on it.
*
* @param policy The Policy on which to set the modes to disallow.
* @param modes Modes to disallow for this Policy.
* @since 1.6.0
*/
function setDenyModes(policy, modes) {
let newPolicy = removeAll(policy, acp.deny);
if (modes.read === true) {
newPolicy = addIri(newPolicy, acp.deny, acp.Read);
}
if (modes.append === true) {
newPolicy = addIri(newPolicy, acp.deny, acp.Append);
}
if (modes.write === true) {
newPolicy = addIri(newPolicy, acp.deny, acp.Write);
}
return newPolicy;
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Given a [[Policy]], return which [[AccessModes]] it disallows.
*
* @param policy The Policy on which you want to know the Access Modes it disallows.
* @since 1.6.0
*/
function getDenyModes(policy) {
const deniedModes = getIriAll(policy, acp.deny);
return {
read: deniedModes.includes(acp.Read),
append: deniedModes.includes(acp.Append),
write: deniedModes.includes(acp.Write),
};
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Initialise a new, empty [[ResourcePolicy]] for the given Resource.
*
* @param resourceWithAcr The Resource to which the Policy is to apply.
* @param name The name that identifies this Policy.
* @since 1.6.0
*/
function createResourcePolicyFor(resourceWithAcr, name) {
const acr = internal_getAcr(resourceWithAcr);
const url = new URL(getSourceUrl(acr));
url.hash = `#${name}`;
let policyThing = createThing({ url: url.href });
policyThing = setUrl(policyThing, rdf.type, acp.Policy);
return policyThing;
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Get the [[ResourcePolicy]] with the given name that applies to a Resource
* from its Access Control Resource.
*
* @param resourceWithAcr The Resource whose ACR contains the given Policy.
* @param name The name that identifies this Policy.
* @returns The requested Policy, if it exists and applies to the given Resource, or `null` if it does not.
* @since 1.6.0
*/
function getResourcePolicy(resourceWithAcr, name) {
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const url = new URL(acrUrl);
url.hash = `#${name}`;
const foundThing = getThing(acr, url.href);
if (!getPolicyUrlAll(resourceWithAcr).includes(url.href) ||
foundThing === null ||
!isPolicy(foundThing)) {
return null;
}
return foundThing;
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Get the [[ResourcePolicy]] with the given name that applies to a Resource's
* Access Control Resource from that Access Control Resource.
*
* @param resourceWithAcr The Resource whose ACR contains the given Policy.
* @param name The name that identifies this Policy.
* @returns The requested Policy, if it exists and applies to the Resource's ACR, or `null` if it does not.
* @since 1.6.0
*/
function getResourceAcrPolicy(resourceWithAcr, name) {
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const url = new URL(acrUrl);
url.hash = `#${name}`;
const foundThing = getThing(acr, url.href);
if (!getAcrPolicyUrlAll(resourceWithAcr).includes(url.href) ||
foundThing === null ||
!isPolicy(foundThing)) {
return null;
}
return foundThing;
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Get all [[ResourcePolicy]]'s that apply to a Resource in its Access Control
* Resource.
*
* @param resourceWithAcr The Resource whose Access Control Resource contains Access Policies applying to it.
* @since 1.6.0
*/
function getResourcePolicyAll(resourceWithAcr) {
const acr = internal_getAcr(resourceWithAcr);
const policyUrls = getPolicyUrlAll(resourceWithAcr);
const foundThings = policyUrls.map((policyUrl) => getThing(acr, policyUrl));
const foundPolicies = foundThings.filter((thing) => thing !== null && isPolicy(thing));
return foundPolicies;
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Get all [[ResourcePolicy]]'s that apply to a given Resource's Access Control
* Resource from that Access Control Resource.
*
* @param resourceWithAcr The Resource whose Access Control Resource contains Access Policies.
* @since 1.6.0
*/
function getResourceAcrPolicyAll(resourceWithAcr) {
const acr = internal_getAcr(resourceWithAcr);
const policyUrls = getAcrPolicyUrlAll(resourceWithAcr);
const foundThings = policyUrls.map((policyUrl) => getThing(acr, policyUrl));
const foundPolicies = foundThings.filter((thing) => thing !== null && isPolicy(thing));
return foundPolicies;
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Remove the given [[ResourcePolicy]] from the given Resource's Access Control
* Resource.
*
* @param resourceWithAcr The Resource whose Access Control Resource contains Access Policies.
* @param policy The Policy to remove from the Resource's Access Control Resource.
* @since 1.6.0
*/
function removeResourcePolicy(resourceWithAcr, policy) {
const acr = internal_getAcr(resourceWithAcr);
let policyToRemove = policy;
if (typeof policyToRemove === "string") {
try {
new URL(policyToRemove);
}
catch (e) {
// If the given Policy to remove is the name of the Policy,
// resolve it to its full URL — developers usually refer to either the
// Policy itself, or by its name, as they do not have access to the ACR
// directly.
const policyUrl = new URL(getSourceUrl(acr));
policyUrl.hash = `#${policy}`;
policyToRemove = policyUrl.href;
}
}
let policyUrlString;
if (typeof policyToRemove === "string") {
policyUrlString = policyToRemove;
}
else if (isNamedNode(policyToRemove)) {
policyUrlString = internal_toIriString(policyToRemove);
}
else {
policyUrlString = asUrl(policyToRemove, getSourceUrl(acr));
}
// Check whether the actual Policy (i.e. with the Policy type) exists:
const matchingRule = getResourcePolicy(resourceWithAcr, new URL(policyUrlString).hash.substring(1));
if (matchingRule === null) {
// No such Policy exists yet, so return the Resource+ACR unchanged:
return resourceWithAcr;
}
const updatedAcr = removeThing(acr, policyToRemove);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
return removePolicyUrl(updatedResource, policyUrlString);
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Remove the given [[ResourcePolicy]] that applies to a given Resource's Access
* Control Resource from that Access Control Resource.
*
* @param resourceWithAcr The Resource whose Access Control Resource contains Access Policies.
* @param policy The ACR Policy to remove from the Resource's Access Control Resource.
* @since 1.6.0
*/
function removeResourceAcrPolicy(resourceWithAcr, policy) {
const acr = internal_getAcr(resourceWithAcr);
let policyToRemove = policy;
if (typeof policyToRemove === "string") {
try {
new URL(policyToRemove);
}
catch (e) {
// If the given Policy to remove is the name of the Policy,
// resolve it to its full URL — developers usually refer to either the
// Policy itself, or by its name, as they do not have access to the ACR
// directly.
const policyUrl = new URL(getSourceUrl(acr));
policyUrl.hash = `#${policy}`;
policyToRemove = policyUrl.href;
}
}
let policyUrlString;
if (typeof policyToRemove === "string") {
policyUrlString = policyToRemove;
}
else if (isNamedNode(policyToRemove)) {
policyUrlString = internal_toIriString(policyToRemove);
}
else {
policyUrlString = asUrl(policyToRemove, getSourceUrl(acr));
}
// Check whether the actual Policy (i.e. with the Policy type) exists:
const matchingRule = getResourceAcrPolicy(resourceWithAcr, new URL(policyUrlString).hash.substring(1));
if (matchingRule === null) {
// No such Policy exists yet, so return the Resource+ACR unchanged:
return resourceWithAcr;
}
const updatedAcr = removeThing(acr, policyToRemove);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
return removeAcrPolicyUrl(updatedResource, policyUrlString);
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Insert the given [[ResourcePolicy]] into the given Resource's Acccess Control
* Resource, replacing previous instances of that Policy.
*
* @param resourceWithAcr The Resource whose Access Control Resource contains Access Policies.
* @param policy The Policy to insert into the Resource's Access Control Resource.
* @returns A new Resource equal to the given Resource, but with the given Policy in its Access Control Resource.
* @since 1.6.0
*/
function setResourcePolicy(resourceWithAcr, policy) {
const acr = internal_getAcr(resourceWithAcr);
const updatedAcr = setThing(acr, policy);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
const policyUrl = asUrl(policy, getSourceUrl(acr));
return addPolicyUrl(updatedResource, policyUrl);
}
/**
* ```{note} There is no Access Control Policies specification yet. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Insert the given [[ResourcePolicy]] into the given Resource's Acccess Control
* Resource, replacing previous instances of that Policy, to apply to the Access
* Control Resource itself.
*
* @param resourceWithAcr The Resource whose Access Control Resource contains Access Policies.
* @param policy The Policy to insert into the Resource's Access Control Resource.
* @returns A new Resource equal to the given Resource, but with the given Policy in its Access Control Resource, applying to that Access Control Resource.
* @since 1.6.0
*/
function setResourceAcrPolicy(resourceWithAcr, policy) {
const acr = internal_getAcr(resourceWithAcr);
const updatedAcr = setThing(acr, policy);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
const policyUrl = asUrl(policy, getSourceUrl(acr));
return addAcrPolicyUrl(updatedResource, policyUrl);
}
/**
* Gets a human-readable representation of the given [[Policy]] to aid debugging.
*
* Note that changes to the exact format of the return value are not considered a breaking change;
* it is intended to aid in debugging, not as a serialisation method that can be reliably parsed.
*
* @param policy The Policy to get a human-readable representation of.
* @since 1.6.0
*/
function policyAsMarkdown(policy) {
function getStatus(allow, deny) {
if (deny) {
return "denied";
}
if (allow) {
return "allowed";
}
return "unspecified";
}
const allowModes = getAllowModes(policy);
const denyModes = getDenyModes(policy);
let markdown = `## Policy: ${asUrl(policy)}\n\n`;
markdown += `- Read: ${getStatus(allowModes.read, denyModes.read)}\n`;
markdown += `- Append: ${getStatus(allowModes.append, denyModes.append)}\n`;
markdown += `- Write: ${getStatus(allowModes.write, denyModes.write)}\n`;
const allOfRules = getAllOfRuleUrlAll(policy);
const anyOfRules = getAnyOfRuleUrlAll(policy);
const noneOfRules = getNoneOfRuleUrlAll(policy);
if (allOfRules.length === 0 &&
anyOfRules.length === 0 &&
noneOfRules.length === 0) {
markdown += "\n<no rules specified yet>\n";
}
if (allOfRules.length > 0) {
markdown += "\nAll of these rules should match:\n";
markdown += "- " + allOfRules.join("\n- ") + "\n";
}
if (anyOfRules.length > 0) {
markdown += "\nAt least one of these rules should match:\n";
markdown += "- " + anyOfRules.join("\n- ") + "\n";
}
if (noneOfRules.length > 0) {
markdown += "\nNone of these rules should match:\n";
markdown += "- " + noneOfRules.join("\n- ") + "\n";
}
return markdown;
}
export { createPolicy, createResourcePolicyFor, getAllowModes, getDenyModes, getPolicy, getPolicyAll, getResourceAcrPolicy, getResourceAcrPolicyAll, getResourcePolicy, getResourcePolicyAll, policyAsMarkdown, removePolicy, removeResourceAcrPolicy, removeResourcePolicy, setAllowModes, setDenyModes, setPolicy, setResourceAcrPolicy, setResourcePolicy };