@inrupt/solid-client
Version:
Make your web apps work with Solid Pods.
1,035 lines (979 loc) • 36.1 kB
text/typescript
/**
* 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.
*/
import { acp, rdf, solid } from "../constants";
import { isNamedNode } from "../datatypes";
import {
SolidDataset,
Thing,
ThingPersisted,
Url,
UrlString,
WebId,
} from "../interfaces";
import { internal_toIriString } from "../interfaces.internal";
import { getSourceUrl } from "../resource/resource";
import { addIri } from "../thing/add";
import { getIriAll, getUrl } from "../thing/get";
import { removeIri } from "../thing/remove";
import { setIri, setUrl } from "../thing/set";
import {
asUrl,
createThing,
getThing,
getThingAll,
removeThing,
setThing,
} from "../thing/thing";
import { WithAccessibleAcr } from "./acp";
import { internal_getAcr, internal_setAcr } from "./control.internal";
import { Policy, ResourcePolicy } from "./policy";
/**
* A Rule can be applied to a [[Policy]] to determine under what circumstances that Policy is applied to a Resource.
* @since 1.6.0
*/
export type Rule = ThingPersisted;
/**
* A ResourceRule is like a [[Rule]], but applied to a [[ResourcePolicy]] and therefore not re-used across different Resources, but only used for a single Resource and stored in that Resource's Access Control Resource.
* @since 1.6.0
*/
export type ResourceRule = ThingPersisted;
/**
* NOTE: Don't export for now (i.e. if exported, should this be `isAcpRule()` so
* as not to clash with `isAclRule()`.
*
* @param thing the [[Thing]] to check to see if it's an ACP rule or not
*/
function isRule(thing: Thing): thing is Rule {
return getIriAll(thing, rdf.type).includes(acp.Rule);
}
/**
* ```{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.
* ```
*
* Add a rule that refines the scope of a given the [[Policy]]. If an agent
* requesting access to a resource is **not** present in **any** of the "All Of" rules,
* they will not be granted access.
*
* Also see [[addAnyOfRuleUrl]] and [[addNoneOfRuleUrl]].
*
* @param policy The [[Policy]] to which the rule should be added.
* @param rule The rule to add to the policy.
* @returns A new [[Policy]] clone of the original one, with the new rule added.
* @since 1.6.0
*/
export function addAllOfRuleUrl<P extends Policy | ResourcePolicy>(
policy: P,
rule: Rule | Url | UrlString
): P {
return addIri(policy, acp.allOf, rule);
}
/**
* ```{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.
* ```
*
* Removes a rule that refines the scope of a given the [[Policy]]. If an agent
* requesting access to a resource is **not** present in **any** of the "All Of" rules,
* they will not be granted access.
* @param policy The [[Policy]] from which the rule should be removed.
* @param rule The rule to remove from the policy.
* @returns A new [[Policy]] clone of the original one, with the rule removed.
* @since 1.6.0
*/
export function removeAllOfRuleUrl<P extends Policy | ResourcePolicy>(
policy: P,
rule: Rule | Url | UrlString
): P {
return removeIri(policy, acp.allOf, rule);
}
/**
* ```{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.
* ```
*
* Overwrites the rule refining the scope of a given the [[Policy]]. If an agent
* requesting access to a resource is **not** present in **any** of the "All Of" rules,
* they will not be granted access.
* @param policy The [[Policy]] to which the rule should be added.
* @param rules The rules the policy requires.
* @returns A new [[Policy]] clone of the original one, with the "All Of" rules replaced.
* @since 1.6.0
*/
export function setAllOfRuleUrl<P extends Policy | ResourcePolicy>(
policy: P,
rule: Rule | Url | UrlString
): P {
return setIri(policy, acp.allOf, rule);
}
/**
* ```{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 "All Of" [[Rule]]s for the given [[Policy]]
* @param policy The [[policy]] from which the rules should be read.
* @returns A list of the "All Of" [[Rule]]s
* @since 1.6.0
*/
export function getAllOfRuleUrlAll<P extends Policy | ResourcePolicy>(
policy: P
): UrlString[] {
return getIriAll(policy, acp.allOf);
}
/**
* ```{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.
* ```
*
* Add a rule that extends the scope of a given the [[Policy]]. If an agent
* requesting access to a resource is present in **any** of the "Any Of" rules,
* they will be granted access.
*
* Also see [[addAllOfRuleUrl]] and [[addNoneOfRuleUrl]].
*
* @param policy The [[Policy]] to which the rule should be added.
* @param rule The rule to add to the policy.
* @returns A new [[Policy]] clone of the original one, with the new rule added.
* @since 1.6.0
*/
export function addAnyOfRuleUrl<P extends Policy | ResourcePolicy>(
policy: P,
rule: Rule | Url | UrlString
): P {
return addIri(policy, acp.anyOf, rule);
}
/**
* ```{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.
* ```
*
* Removes a rule that extends the scope of a given the [[Policy]]. If an agent
* requesting access to a resource is present in **any** of the "Any Of" rules,
* they will be granted access.
* @param policy The [[Policy]] from which the rule should be removed.
* @param rule The rule to remove from the policy.
* @returns A new [[Policy]] clone of the original one, with the rule removed.
* @since 1.6.0
*/
export function removeAnyOfRuleUrl<P extends Policy | ResourcePolicy>(
policy: P,
rule: Rule | Url | UrlString
): P {
return removeIri(policy, acp.anyOf, rule);
}
/**
* ```{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.
* ```
*
* Overwrite the rule extending the scope of a given the [[Policy]]. If an agent
* requesting access to a resource is present in **any** of the "Any Of" rules,
* they will be granted access.
* @param policy The [[Policy]] to which the rule should be added.
* @param rules The rules the policy accepts.
* @returns A new [[Policy]] clone of the original one, with the "Any Of" rules replaced.
* @since 1.6.0
*/
export function setAnyOfRuleUrl<P extends Policy | ResourcePolicy>(
policy: P,
rule: Rule | Url | UrlString
): P {
return setIri(policy, acp.anyOf, rule);
}
/**
* ```{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 "Any Of" [[Rule]]s for the given [[Policy]]
* @param policy The [[policy]] from which the rules should be read.
* @returns A list of the "Any Of" [[Rule]]s
* @since 1.6.0
*/
export function getAnyOfRuleUrlAll<P extends Policy | ResourcePolicy>(
policy: P
): UrlString[] {
return getIriAll(policy, acp.anyOf);
}
/**
* ```{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.
* ```
*
* Add a rule that restricts the scope of a given the [[Policy]]. If an agent
* requesting access to a resource is present in **any** of the forbidden rules,
* they will **not** be granted access.
*
* Also see [[addAllOfRuleUrl]] and [[addAnyOfRuleUrl]].
*
* @param policy The [[Policy]] to which the rule should be added.
* @param rule The rule to add to the policy.
* @returns A new [[Policy]] clone of the original one, with the new rule added.
* @since 1.6.0
*/
export function addNoneOfRuleUrl<P extends Policy | ResourcePolicy>(
policy: P,
rule: Rule | Url | UrlString
): P {
return addIri(policy, acp.noneOf, rule);
}
/**
* ```{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.
* ```
*
* Removes a rule that restricts the scope of a given the [[Policy]]. If an agent
* requesting access to a resource is present in **any** of the forbidden rules,
* they will **not** be granted access.
* @param policy The [[Policy]] from which the rule should be removed.
* @param rule The rule to remove from the policy.
* @returns A new [[Policy]] clone of the original one, with the rule removed.
* @since 1.6.0
*/
export function removeNoneOfRuleUrl<P extends Policy | ResourcePolicy>(
policy: P,
rule: Rule | Url | UrlString
): P {
return removeIri(policy, acp.noneOf, rule);
}
/**
* ```{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.
* ```
*
* Set the rules restrincting the scope of a given the [[Policy]]. If an agent
* requesting access to a resource is present in **any** of the "None Of" rules,
* they will not be granted access.
* @param policy The [[Policy]] to which the rule should be added.
* @param rules The rules the policy accepts.
* @returns A new [[Policy]] clone of the original one, with the "Any Of" rules replaced.
* @since 1.6.0
*/
export function setNoneOfRuleUrl<P extends Policy | ResourcePolicy>(
policy: P,
rule: Rule | Url | UrlString
): P {
return setIri(policy, acp.noneOf, rule);
}
/**
* ```{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 "None Of" [[Rule]]s for the given [[Policy]]
* @param policy The [[policy]] from which the rules should be read.
* @returns A list of the forbidden [[Rule]]s
* @since 1.6.0
*/
export function getNoneOfRuleUrlAll<P extends Policy | ResourcePolicy>(
policy: P
): UrlString[] {
return getIriAll(policy, acp.noneOf);
}
/**
* ```{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 [[Rule]].
*
* @param url URL that identifies this [[Rule]].
* @since 1.6.0
*/
export function createRule(url: Url | UrlString): Rule {
const stringUrl = internal_toIriString(url);
let ruleThing = createThing({ url: stringUrl });
ruleThing = setUrl(ruleThing, rdf.type, acp.Rule);
return ruleThing;
}
/**
* ```{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 [[ResourceRule]] for the given Resource.
*
* @param resourceWithAcr The Resource to which the new Rule is to apply.
* @param name Name that identifies this [[Rule]].
* @since 1.6.0
*/
export function createResourceRuleFor(
resourceWithAcr: WithAccessibleAcr,
name: string
): ResourceRule {
const acr = internal_getAcr(resourceWithAcr);
const url = new URL(getSourceUrl(acr));
url.hash = `#${name}`;
let ruleThing = createThing({ url: url.href });
ruleThing = setUrl(ruleThing, rdf.type, acp.Rule);
return ruleThing;
}
/**
* ```{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 [[Rule]] with the given URL from an [[SolidDataset]].
*
* @param ruleResource The Resource that contains the given [[Rule]].
* @param url URL that identifies this [[Rule]].
* @returns The requested [[Rule]], if it exists, or `null` if it does not.
* @since 1.6.0
*/
export function getRule(
ruleResource: SolidDataset,
url: Url | UrlString
): Rule | null {
const foundThing = getThing(ruleResource, url);
if (foundThing === null || getUrl(foundThing, rdf.type) !== acp.Rule) {
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 [[ResourceRule]] with the given name from an Resource's Access Control
* Resource.
*
* @param resourceWithAcr The Resource whose Access Control Resource contains the given [[ResourceRule]].
* @param name Name that identifies this [[ResourceRule]].
* @returns The requested [[ResourceRule]], if it exists, or `null` if it does not.
* @since 1.6.0
*/
export function getResourceRule(
resourceWithAcr: WithAccessibleAcr,
name: string
): ResourceRule | null {
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const url = new URL(acrUrl);
url.hash = `#${name}`;
const foundThing = getThing(acr, url.href);
if (foundThing === null || !isRule(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.
* ```
*
* Gets the [[Rule]]s from a [[SolidDataset]].
*
* @param ruleResource The Resource that contains (zero or more) [[Rule]]s.
* @returns The [[Rule]]s contained in this resource.
* @since 1.6.0
*/
export function getRuleAll(ruleResource: SolidDataset): Rule[] {
const things = getThingAll(ruleResource);
return things.filter(isRule);
}
/**
* ```{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.
* ```
*
* Gets the [[ResourceRule]]s from a Resource's Access Control Resource.
*
* @param resourceWithAcr The Resource whose Access Control Resource contains (zero or more) [[ResourceRule]]s.
* @returns The [[ResourceRule]]s contained in this Resource's Access Control Resource.
* @since 1.6.0
*/
export function getResourceRuleAll(
resourceWithAcr: WithAccessibleAcr
): ResourceRule[] {
const acr = internal_getAcr(resourceWithAcr);
const things = getThingAll(acr);
return things.filter(isRule);
}
/**
* ```{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.
* ```
*
* Removes the given [[Rule]] from the given [[SolidDataset]].
*
* @param ruleResource The Resource that contains (zero or more) [[Rule]]s.
* @returns A new SolidDataset equal to the given Rule Resource, but without the given Rule.
* @since 1.6.0
*/
export function removeRule<Dataset extends SolidDataset>(
ruleResource: Dataset,
rule: Url | UrlString | Rule
): Dataset {
return removeThing(ruleResource, rule);
}
/**
* ```{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.
* ```
*
* Removes the given [[ResourceRule]] from the given Resource's Access Control Resource.
*
* @param resourceWithAcr The Resource whose Access Control Resource contains (zero or more) [[ResourceRule]]s.
* @returns A new Resource equal to the given Resource, but without the given Rule in its ACR.
* @since 1.6.0
*/
export function removeResourceRule<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt,
rule: string | Url | UrlString | ResourceRule
): ResourceExt {
const acr = internal_getAcr(resourceWithAcr);
let ruleToRemove: UrlString;
if (typeof rule === "string") {
try {
new URL(rule);
ruleToRemove = rule;
} catch (e) {
// If the given Rule to remove is the name of the Rule,
// resolve it to its full URL — developers usually refer to either the
// Rule itself, or by its name, as they do not have access to the ACR
// directly.
const ruleUrl = new URL(getSourceUrl(acr));
ruleUrl.hash = `#${rule}`;
ruleToRemove = ruleUrl.href;
}
} else if (isNamedNode(rule)) {
ruleToRemove = internal_toIriString(rule);
} else {
ruleToRemove = asUrl(rule);
}
// Check whether the actual Rule (i.e. with the Rule type) exists:
const matchingRule = getResourceRule(
resourceWithAcr,
new URL(ruleToRemove).hash.substring(1)
);
if (matchingRule === null) {
// No such Rule exists yet, so return the Resource+ACR unchanged:
return resourceWithAcr;
}
const updatedAcr = removeThing(acr, matchingRule);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
return updatedResource;
}
/**
* ```{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 [[Rule]] into the given [[SolidDataset]], replacing previous
* instances of that Rule.
*
* @param ruleResource The Resource that contains (zero or more) [[Rule]]s.
* @returns A new SolidDataset equal to the given Rule Resource, but with the given Rule.
* @since 1.6.0
*/
export function setRule<Dataset extends SolidDataset>(
ruleResource: Dataset,
rule: Rule
): Dataset {
return setThing(ruleResource, rule);
}
/**
* ```{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 [[ResourceRule]] into the given Resource's Access Control Resource,
* replacing previous instances of that Rule.
*
* @param resourceWithAcr The Resource whose Access Control Resource contains (zero or more) [[ResourceRule]]s.
* @returns A new Resource equal to the given Resource, but with the given Rule in its ACR.
* @since 1.6.0
*/
export function setResourceRule<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt,
rule: ResourceRule
): ResourceExt {
const acr = internal_getAcr(resourceWithAcr);
const updatedAcr = setThing(acr, rule);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
return updatedResource;
}
/**
* ```{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.
* ```
*
* List all the agents a [[Rule]] applies **directly** to. This will not include agents
* that are part of a group the [[Rule]] applies to, nor will it include specific agent
* classes, such as authenticated or public agents.
*
* @param rule The rule from which agents are read.
* @returns A list of the WebIDs of agents included in the rule.
* @since 1.6.0
*/
export function getAgentAll(rule: Rule): WebId[] {
return getIriAll(rule, acp.agent).filter(
(agent: WebId) =>
agent !== acp.PublicAgent &&
agent !== acp.AuthenticatedAgent &&
agent !== acp.CreatorAgent
);
}
/**
* ```{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.
* ```
*
* Overwrite the agents the [[Rule]] applies to with the provided agents.
*
* @param rule The rule for which agents are set.
* @param agent The agent the rule should apply to.
* @returns A copy of the input rule, applying to a different set of agents.
* @since 1.6.0
*/
export function setAgent(rule: Rule, agent: WebId): Rule {
// Preserve the special agent classes authenticated and public, which we
// don't want to overwrite with this function.
const isPublic = hasPublic(rule);
const isAuthenticated = hasAuthenticated(rule);
const isCreator = hasCreator(rule);
let result = setIri(rule, acp.agent, agent);
// Restore public and authenticated
if (isPublic) {
result = setPublic(result);
}
if (isAuthenticated) {
result = setAuthenticated(result);
}
if (isCreator) {
result = setCreator(result);
}
return result;
}
/**
* ```{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.
* ```
*
* Apply the [[Rule]] to an additional agent.
*
* @param rule The [[Rule]] to be applied to an additional agent.
* @param agent The agent the [[Rule]] should apply to.
* @returns A copy of the [[Rule]], applying to an additional agent.
* @since 1.6.0
*/
export function addAgent(rule: Rule, agent: WebId): Rule {
return addIri(rule, acp.agent, agent);
}
/**
* ```{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.
* ```
*
* Prevent the [[Rule]] from applying to a given agent directly. This will not
* remove the agent from any groups the rule applies to.
*
* @param rule The [[Rule]] that should no longer apply to a given agent.
* @param agent The agent the rule should no longer apply to.
* @returns A copy of the rule, not applying to the given agent.
* @since 1.6.0
*/
export function removeAgent(rule: Rule, agent: WebId): Rule {
return removeIri(rule, acp.agent, agent);
}
/**
* ```{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.
* ```
*
* Lists all the groups a [[Rule]] applies to.
*
* @param rule The rule from which groups are read.
* @returns A list of the [[URL]]'s of groups included in the rule.
* @since 1.6.0
*/
export function getGroupAll(rule: Rule): UrlString[] {
return getIriAll(rule, acp.group);
}
/**
* ```{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.
* ```
*
* Overwrite the groups the [[Rule]] applies to with the provided groups.
*
* @param rule The rule for which groups are set.
* @param group The group the rule should apply to.
* @returns A copy of the input rule, applying to a different set of groups.
* @since 1.6.0
*/
export function setGroup(rule: Rule, group: UrlString): Rule {
return setIri(rule, acp.group, group);
}
/**
* ```{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.
* ```
*
* Apply the [[Rule]] to an additional group.
*
* @param rule The [[Rule]] to be applied to an additional group.
* @param agent The group the [[Rule]] should apply to.
* @returns A copy of the [[Rule]], applying to an additional group.
* @since 1.6.0
*/
export function addGroup(rule: Rule, group: UrlString): Rule {
return addIri(rule, acp.group, group);
}
/**
* ```{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.
* ```
*
* Prevent the [[Rule]] from applying to a given group.
*
* @param rule The [[Rule]] that should no longer apply to a given group.
* @param agent The group the rule should no longer apply to.
* @returns A copy of the rule, not applying to the given group.
* @since 1.6.0
*/
export function removeGroup(rule: Rule, group: UrlString): Rule {
return removeIri(rule, acp.group, group);
}
/**
* ```{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.
* ```
*
* Check if the rule applies to any agent.
*
* @param rule The rule checked for public access.
* @returns Whether the rule applies to any agent or not.
* @status 1.6.0
*/
export function hasPublic(rule: Rule): boolean {
return (
getIriAll(rule, acp.agent).filter((agent) => agent === acp.PublicAgent)
.length > 0
);
}
/**
* ```{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.
* ```
*
* Set a Rule to apply to any Agent.
*
* @param rule The rule being modified.
* @returns A copy of the rule, updated to apply to any agent.
* @status 1.6.0
*/
export function setPublic(rule: Rule): Rule {
// The second argument should not be part of the function signature,
// so it's not in the parameter list:
// eslint-disable-next-line prefer-rest-params
if (typeof arguments === "object" && typeof arguments[1] === "boolean") {
throw new Error(
"The function `setPublic` no longer takes a second parameter. It is now used together with `removePublic` instead."
);
}
return addIri(rule, acp.agent, acp.PublicAgent);
}
/**
* ```{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.
* ```
*
* Set a Rule to no longer apply to any Agent.
*
* @param rule The rule being modified.
* @returns A copy of the rule, updated to no longer apply to any agent.
* @status 1.6.0
*/
export function removePublic(rule: Rule): Rule {
return removeIri(rule, acp.agent, acp.PublicAgent);
}
/**
* ```{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.
* ```
*
* Check if the rule applies to any authenticated agent.
*
* @param rule The rule checked for authenticated access.
* @returns Whether the rule applies to any authenticated agent or not.
* @status 1.6.0
*/
export function hasAuthenticated(rule: Rule): boolean {
return (
getIriAll(rule, acp.agent).filter(
(agent) => agent === acp.AuthenticatedAgent
).length > 0
);
}
/**
* ```{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.
* ```
*
* Set a Rule to apply to any authenticated Agent.
*
* @param rule The rule being modified.
* @returns A copy of the rule, updated to apply to any authenticated Agent.
* @status 1.6.0
*/
export function setAuthenticated(rule: Rule): Rule {
// The second argument should not be part of the function signature,
// so it's not in the parameter list:
// eslint-disable-next-line prefer-rest-params
if (typeof arguments === "object" && typeof arguments[1] === "boolean") {
throw new Error(
"The function `setAuthenticated` no longer takes a second parameter. It is now used together with `removeAuthenticated` instead."
);
}
return addIri(rule, acp.agent, acp.AuthenticatedAgent);
}
/**
* ```{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.
* ```
*
* Set a Rule to no longer apply to any authenticated Agent.
*
* @param rule The rule being modified.
* @returns A copy of the rule, updated to apply/not apply to any authenticated agent.
* @status 1.6.0
*/
export function removeAuthenticated(rule: Rule): Rule {
return removeIri(rule, acp.agent, acp.AuthenticatedAgent);
}
/**
* ```{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.
* ```
*
* Check if the rule applies to the creator of the Resource.
*
* @param rule The rule checked for authenticated access.
* @returns Whether the rule applies to the creator of the Resource or not.
* @status 1.6.0
*/
export function hasCreator(rule: Rule): boolean {
return (
getIriAll(rule, acp.agent).filter((agent) => agent === acp.CreatorAgent)
.length > 0
);
}
/**
* ```{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.
* ```
*
* Set a Rule to apply to the creator of a Resource.
*
* @param rule The rule being modified.
* @returns A copy of the rule, updated to apply to the creator of a Resource.
* @status 1.6.0
*/
export function setCreator(rule: Rule): Rule {
// The second argument should not be part of the function signature,
// so it's not in the parameter list:
// eslint-disable-next-line prefer-rest-params
if (typeof arguments === "object" && typeof arguments[1] === "boolean") {
throw new Error(
"The function `setCreator` no longer takes a second parameter. It is now used together with `removeCreator` instead."
);
}
return addIri(rule, acp.agent, acp.CreatorAgent);
}
/**
* ```{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.
* ```
*
* Set a Rule to no longer apply to the creator of a Resource.
*
* @param rule The rule being modified.
* @returns A copy of the rule, updated to apply/not apply to the creator of a Resource.
* @status 1.6.0
*/
export function removeCreator(rule: Rule): Rule {
return removeIri(rule, acp.agent, acp.CreatorAgent);
}
/**
* ```{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.
* ```
*
* List all the clients a [[Rule]] applies **directly** to. This will not include
* specific client classes, such as public clients.
*
* @param rule The rule from which clients are read.
* @returns A list of the WebIDs of clients included in the rule.
* @since 1.6.0
*/
export function getClientAll(rule: Rule): WebId[] {
return getIriAll(rule, acp.client).filter(
(client: WebId) => client !== solid.PublicOidcClient
);
}
/**
* ```{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.
* ```
*
* Overwrite the clients the [[Rule]] applies to with the provided Client.
*
* @param rule The rule for which clients are set.
* @param client The Client the rule should apply to.
* @returns A copy of the input rule, applying to a different set of Clients.
* @since 1.6.0
*/
export function setClient(rule: Rule, client: WebId): Rule {
// Preserve the special "any client" class, which we
// don't want to overwrite with this function.
const anyClientEnabled = hasAnyClient(rule);
let result = setIri(rule, acp.client, client);
// Restore the "any client" class
if (anyClientEnabled) {
result = setAnyClient(result);
}
return result;
}
/**
* ```{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.
* ```
*
* Apply the [[Rule]] to an additional Client.
*
* @param rule The [[Rule]] to be applied to an additional Client.
* @param client The Client the [[Rule]] should apply to.
* @returns A copy of the [[Rule]], applying to an additional Client.
* @since 1.6.0
*/
export function addClient(rule: Rule, client: WebId): Rule {
return addIri(rule, acp.client, client);
}
/**
* ```{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.
* ```
*
* Prevent the [[Rule]] from applying to a given Client directly.
*
* @param rule The [[Rule]] that should no longer apply to a given Client.
* @param client The Client the rule should no longer apply to.
* @returns A copy of the rule, not applying to the given Client.
* @since 1.6.0
*/
export function removeClient(rule: Rule, client: WebId): Rule {
return removeIri(rule, acp.client, client);
}
/**
* ```{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.
* ```
*
* Check if the rule applies to any client, i.e. all the applications
* regardless of their identifier.
*
* @param rule The rule checked for authenticated access.
* @returns Whether the rule applies to public clients.
* @since 1.6.0
*/
export function hasAnyClient(rule: Rule): boolean {
return (
getIriAll(rule, acp.client).filter(
(client) => client === solid.PublicOidcClient
).length > 0
);
}
/**
* ```{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.
* ```
*
* Make the [[Rule]] apply to any client application.
*
* @param rule The rule for which clients are set.
* @returns A copy of the rule, updated to apply to any client
* @since 1.6.0
*/
export function setAnyClient(rule: Rule): Rule {
return addIri(rule, acp.client, solid.PublicOidcClient);
}
/**
* ```{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.
* ```
*
* Make the [[Rule]] no longer apply to any client application.
*
* @param rule The rule for which clients are set.
* @returns A copy of the rule, updated to no longer apply to any client
* @since 1.6.0
*/
export function removeAnyClient(rule: Rule): Rule {
return removeIri(rule, acp.client, solid.PublicOidcClient);
}
/**
* Gets a human-readable representation of the given [[Rule]] 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 rule The Rule to get a human-readable representation of.
* @since 1.6.0
*/
export function ruleAsMarkdown(rule: Rule): string {
let markdown = `## Rule: ${asUrl(rule)}\n\n`;
let targetEnumeration = "";
if (hasPublic(rule)) {
targetEnumeration += "- Everyone\n";
}
if (hasAuthenticated(rule)) {
targetEnumeration += "- All authenticated agents\n";
}
if (hasCreator(rule)) {
targetEnumeration += "- The creator of this resource\n";
}
if (hasAnyClient(rule)) {
targetEnumeration += "- Users of any client application\n";
}
const targetAgents = getAgentAll(rule);
if (targetAgents.length > 0) {
targetEnumeration += "- The following agents:\n - ";
targetEnumeration += targetAgents.join("\n - ") + "\n";
}
const targetGroups = getGroupAll(rule);
if (targetGroups.length > 0) {
targetEnumeration += "- Members of the following groups:\n - ";
targetEnumeration += targetGroups.join("\n - ") + "\n";
}
const targetClients = getClientAll(rule);
if (targetClients.length > 0) {
targetEnumeration += "- Users of the following client applications:\n - ";
targetEnumeration += targetClients.join("\n - ") + "\n";
}
markdown +=
targetEnumeration.length > 0
? "This rule applies to:\n" + targetEnumeration
: "<empty>\n";
return markdown;
}