@inrupt/solid-client
Version:
Make your web apps work with Solid Pods.
601 lines (561 loc) • 23.5 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 } from "../constants";
import {
hasServerResourceInfo,
SolidDataset,
Thing,
ThingPersisted,
Url,
UrlString,
WithResourceInfo,
WithServerResourceInfo,
} from "../interfaces";
import { getSourceUrl } from "../resource/resource";
import { addIri } from "../thing/add";
import { getIriAll } from "../thing/get";
import { removeAll, removeIri } from "../thing/remove";
import { setIri } from "../thing/set";
import {
createThing,
getThing,
getThingAll,
removeThing,
setThing,
} from "../thing/thing";
import { WithAccessibleAcr } from "./acp";
import {
internal_addMemberPolicyUrl,
internal_addPolicyUrl,
internal_getAcr,
internal_getControlAll,
internal_getInitialisedControl,
internal_getMemberPolicyUrlAll,
internal_getPolicyUrlAll,
internal_removeMemberPolicyUrl,
internal_removeMemberPolicyUrlAll,
internal_removePolicyUrl,
internal_removePolicyUrlAll,
internal_setAcr,
internal_setControl,
} from "./control.internal";
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Given a Resource, check whether it is governed by Access Policies.
* (Specifically, a Resource that is governed by Access Policies will refer to exactly one Access
* Control Resource, and expose that to users who are allowed to see or modify access to the given
* Resource.)
*
* @param resource Resource which may or may not be governed by Access Policies.
* @returns True if the Resource refers to an Access Control Resource and is hence governed by Access Policies, or false if it does not.
* @since 1.6.0
*/
export function hasLinkedAcr<Resource extends WithServerResourceInfo>(
resource: Resource
): resource is WithLinkedAcr<Resource> {
return (
hasServerResourceInfo(resource) &&
Array.isArray(
resource.internal_resourceInfo.linkedResources[acp.accessControl]
) &&
resource.internal_resourceInfo.linkedResources[acp.accessControl].length ===
1
);
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* An Access Control Resource, containing [[Control]]s specifying which [[Policy]]'s
* apply to the Resource this Access Control Resource is linked to.
* @since 1.6.0
*/
export type AccessControlResource = SolidDataset &
WithResourceInfo & { accessTo: UrlString };
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* A Control, usually contained in an [[AccessControlResource]]. It describes which
* [[Policy]]'s apply to a Resource.
* @since 1.6.0
*/
export type Control = Thing;
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* If this type applies to a Resource, it is governed by an Access Control Resource,
* and thus not the Web Access Control spec.
* It does not indicate that this Access Control Resource will also be accessible to the current
* user.
* @since 1.6.0
*/
export type WithLinkedAcr<
Resource extends WithServerResourceInfo = WithServerResourceInfo
> = Resource & {
internal_resourceInfo: {
linkedResources: {
[acp.accessControl]: [string];
};
};
};
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Add a [[Policy]] to an Access Control Resource such that that [[Policy]] applies to the Access
* Control Resource itself, rather than the Resource it governs.
*
* @param resourceWithAcr The Resource with an Access Control Resource to which the ACR Policy should be added.
* @param policyUrl URL of the Policy that should apply to the given Access Control Resource.
* @returns A Resource with a new Access Control Resource equal to the original ACR, but with the given ACR Policy added to it.
* @since 1.6.0
*/
export function addAcrPolicyUrl<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt,
policyUrl: Url | UrlString | ThingPersisted
): ResourceExt {
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
let acrThing = getThing(acr, acrUrl) ?? createThing({ url: acrUrl });
acrThing = addIri(acrThing, acp.access, policyUrl);
const updatedAcr = setThing(acr, acrThing);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
return updatedResource;
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Add a [[Policy]] to a Resource's Access Control Resource such that that
* Policy applies to the Access Control Resources of child Resources.
*
* @param resourceWithAcr The Resource with an Access Control Resource to which the ACR Policy should be added.
* @param policyUrl URL of the Policy that should apply to the given Access Control Resources of children of the Resource.
* @returns A Resource with a new Access Control Resource equal to the original ACR, but with the given ACR Policy added to it.
* @since 1.6.0
*/
export function addMemberAcrPolicyUrl<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt,
policyUrl: Url | UrlString | ThingPersisted
): ResourceExt {
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
let acrThing = getThing(acr, acrUrl) ?? createThing({ url: acrUrl });
acrThing = addIri(acrThing, acp.accessMembers, policyUrl);
const updatedAcr = setThing(acr, acrThing);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
return updatedResource;
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Get the URLs of the Access Policies that apply to an Access Control Resource itself, rather than
* to the Resource it governs.
*
* @param resourceWithAcr The Resource with the Access Control Resource of which to get the URLs of the Policies that govern access to it.
* @returns URLs of the Policies that govern access to the given Access Control Resource.
* @since 1.6.0
*/
export function getAcrPolicyUrlAll<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt
): UrlString[] {
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const acrThing = getThing(acr, acrUrl);
if (acrThing === null) {
return [];
}
return getIriAll(acrThing, acp.access);
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Get the URLs of the Access Policies that apply to the Access Control Resources of the Resource's
* children.
*
* @param resourceWithAcr The Resource with the Access Control Resource of which to get the URLs of the Policies that govern access to its children.
* @returns URLs of the Policies that govern access to the Access Control Resources of the given Resource's children.
* @since 1.6.0
*/
export function getMemberAcrPolicyUrlAll<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt
): UrlString[] {
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const acrThing = getThing(acr, acrUrl);
if (acrThing === null) {
return [];
}
return getIriAll(acrThing, acp.accessMembers);
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Stop the URL of a given [[Policy]] from applying to an Access Control Resource itself.
*
* @param resourceWithAcr The Resource with the Access Control Resource to which the given URL of a Policy should no longer apply.
* @param policyUrl The URL of the Policy that should no longer apply.
* @returns A Resource with a new Access Control Resource equal to the original ACR, but with the given ACR Policy removed from it.
* @since 1.6.0
*/
export function removeAcrPolicyUrl<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt,
policyUrl: Url | UrlString | ThingPersisted
): ResourceExt {
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const acrThing = getThing(acr, acrUrl);
if (acrThing === null) {
return resourceWithAcr;
}
const updatedAcrThing = removeIri(acrThing, acp.access, policyUrl);
const updatedAcr = setThing(acr, updatedAcrThing);
return internal_setAcr(resourceWithAcr, updatedAcr);
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Stop the URL of a given [[Policy]] from applying to the Access Control Resources of the
* Resource's children.
*
* @param resourceWithAcr The Resource with the Access Control Resource to whose children's ACRs the given URL of a Policy should no longer apply.
* @param policyUrl The URL of the Policy that should no longer apply.
* @returns A Resource with a new Access Control Resource equal to the original ACR, but with the given member ACR Policy removed from it.
* @since 1.6.0
*/
export function removeMemberAcrPolicyUrl<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt,
policyUrl: Url | UrlString | ThingPersisted
): ResourceExt {
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const acrThing = getThing(acr, acrUrl);
if (acrThing === null) {
return resourceWithAcr;
}
const updatedAcrThing = removeIri(acrThing, acp.accessMembers, policyUrl);
const updatedAcr = setThing(acr, updatedAcrThing);
return internal_setAcr(resourceWithAcr, updatedAcr);
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Stop all URL of Access Policies from applying to an Access Control Resource itself.
*
* @param resourceWithAcr The Resource with the Access Control Resource to which no more Policies should apply.
* @returns A Resource with a new Access Control Resource equal to the original ACR, but without any Policy applying to it.
* @since 1.6.0
*/
export function removeAcrPolicyUrlAll<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt
): ResourceExt {
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const acrThing = getThing(acr, acrUrl);
if (acrThing === null) {
return resourceWithAcr;
}
const updatedAcrThing = removeAll(acrThing, acp.access);
const updatedAcr = setThing(acr, updatedAcrThing);
return internal_setAcr(resourceWithAcr, updatedAcr);
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Stop all URL of Access Policies from applying to the Access Control Resources of the Resource's
* children.
*
* @param resourceWithAcr The Resource with the Access Control Resource that should no longer apply Policies to its children's ACRs.
* @returns A Resource with a new Access Control Resource equal to the original ACR, but without any Policy applying to its children's ACRs.
* @since 1.6.0
*/
export function removeMemberAcrPolicyUrlAll<
ResourceExt extends WithAccessibleAcr
>(resourceWithAcr: ResourceExt): ResourceExt {
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const acrThing = getThing(acr, acrUrl);
if (acrThing === null) {
return resourceWithAcr;
}
const updatedAcrThing = removeAll(acrThing, acp.accessMembers);
const updatedAcr = setThing(acr, updatedAcrThing);
return internal_setAcr(resourceWithAcr, updatedAcr);
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Add a [[Policy]] to an Access Control Resource such that that [[Policy]] applies to that Resource.
*
* @param resourceWithAcr The Resource to which the ACR Policy should be added.
* @param policyUrl URL of the Policy that should apply to the given Resource.
* @returns A Resource with a new Access Control Resource equal to the original ACR, but with the given Policy added to it.
* @since 1.6.0
*/
export function addPolicyUrl<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt,
policyUrl: Url | UrlString
): ResourceExt {
const control = internal_getInitialisedControl(resourceWithAcr);
const updatedControl = internal_addPolicyUrl(control, policyUrl);
const updatedResource = internal_setControl(resourceWithAcr, updatedControl);
return updatedResource;
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Add a [[Policy]] to a Resource's Access Control Resource such that that
* Policy applies to that Resource's children.
*
* @param resourceWithAcr The Resource to whose Access Control Resource the Policy should be added.
* @param policyUrl URL of the Policy that should apply to the given Resource's children.
* @returns A new Resource equal to the given Resource, but with the given Member Policy added to its Access Control Resource.
* @since 1.6.0
*/
export function addMemberPolicyUrl<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt,
policyUrl: Url | UrlString
): ResourceExt {
const control = internal_getInitialisedControl(resourceWithAcr);
const updatedControl = internal_addMemberPolicyUrl(control, policyUrl);
const updatedResource = internal_setControl(resourceWithAcr, updatedControl);
return updatedResource;
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Get the URLs of the Access Policies that apply to a Resource.
*
* @param resourceWithAcr The Resource with the Access Control Resource of which to get the URLs of the Policies that govern access to it.
* @returns URLs of the Policies that govern access to the given Resource.
* @since 1.6.0
*/
export function getPolicyUrlAll<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt
): UrlString[] {
const controls = internal_getControlAll(resourceWithAcr);
const policyUrlsByControl = controls.map((control) =>
internal_getPolicyUrlAll(control)
);
const uniquePolicyUrls = new Set<UrlString>();
policyUrlsByControl.forEach((policyUrls) => {
policyUrls.forEach((url) => uniquePolicyUrls.add(url));
});
return Array.from(uniquePolicyUrls);
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Get the URLs of the Access Policies that apply to a Resource's children.
*
* @param resourceWithAcr The Resource with the Access Control Resource of which to get the URLs of the Policies that govern access to its children.
* @returns URLs of the Policies that govern access to the given Resource's children.
* @since 1.6.0
*/
export function getMemberPolicyUrlAll<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt
): UrlString[] {
const controls = internal_getControlAll(resourceWithAcr);
const memberPolicyUrlsByControl = controls.map((control) =>
internal_getMemberPolicyUrlAll(control)
);
const uniquePolicyUrls = new Set<UrlString>();
memberPolicyUrlsByControl.forEach((policyUrls) => {
policyUrls.forEach((url) => uniquePolicyUrls.add(url));
});
return Array.from(uniquePolicyUrls);
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Stop the URL of a given [[Policy]] from applying to a Resource.
*
* @param resourceWithAcr The Resource, with its Access Control Resource, to which the given URL of a Policy should no longer apply.
* @param policyUrl The URL of the Policy that should no longer apply.
* @returns A Resource with a new Access Control Resource equal to the original ACR, but with the given Policy removed from it.
* @since 1.6.0
*/
export function removePolicyUrl<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt,
policyUrl: Url | UrlString | ThingPersisted
): ResourceExt {
const controls = internal_getControlAll(resourceWithAcr);
const updatedControls = controls.map((control) =>
internal_removePolicyUrl(control, policyUrl)
);
const updatedResource = updatedControls.reduce(
internal_setControl,
resourceWithAcr
);
return updatedResource;
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Stop the URL of a given [[Policy]] from applying to the Resource's children.
*
* @param resourceWithAcr The Resource with the Access Control Resource to whose children the given URL of a Policy should no longer apply.
* @param policyUrl The URL of the Policy that should no longer apply.
* @returns A Resource with a new Access Control Resource equal to the original ACR, but with the given Member Policy removed from it.
* @since 1.6.0
*/
export function removeMemberPolicyUrl<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt,
policyUrl: Url | UrlString | ThingPersisted
): ResourceExt {
const controls = internal_getControlAll(resourceWithAcr);
const updatedControls = controls.map((control) =>
internal_removeMemberPolicyUrl(control, policyUrl)
);
const updatedResource = updatedControls.reduce(
internal_setControl,
resourceWithAcr
);
return updatedResource;
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Stop all URL of Access Policies from applying to a Resource.
*
* @param resourceWithAcr The Resource, with its Access Control Resource, to which no more Policies should apply.
* @returns A Resource with a new Access Control Resource equal to the original ACR, but without any Policy applying to the Resource.
* @since 1.6.0
*/
export function removePolicyUrlAll<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt
): ResourceExt {
const controls = internal_getControlAll(resourceWithAcr);
const updatedControls = controls.map((control) =>
internal_removePolicyUrlAll(control)
);
const updatedResource = updatedControls.reduce(
internal_setControl,
resourceWithAcr
);
return updatedResource;
}
/**
* ```{note} The Web Access Control specification is not yet finalised. As such, this
* function is still experimental and subject to change, even in a non-major release.
* ```
*
* Stop all URL of Access Policies from applying to the Resource's children.
*
* @param resourceWithAcr The Resource with the Access Control Resource that should no longer apply Policies to its children.
* @returns A Resource with a new Access Control Resource equal to the original ACR, but without any Policy applying to the Resource's children.
* @since 1.6.0
*/
export function removeMemberPolicyUrlAll<ResourceExt extends WithAccessibleAcr>(
resourceWithAcr: ResourceExt
): ResourceExt {
const controls = internal_getControlAll(resourceWithAcr);
const updatedControls = controls.map((control) =>
internal_removeMemberPolicyUrlAll(control)
);
const updatedResource = updatedControls.reduce(
internal_setControl,
resourceWithAcr
);
return updatedResource;
}
/**
* Gets a human-readable representation of the given [[Control]] 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 resourceWithAcr The Resource with an attached Access Control Resource of which you want to get a human-readable representation.
* @since 1.6.0
*/
export function acrAsMarkdown(
resourceWithAcr: WithResourceInfo & WithAccessibleAcr
): string {
let markdown = `# Access controls for ${getSourceUrl(resourceWithAcr)}\n`;
const policyUrls = getPolicyUrlAll(resourceWithAcr);
const memberPolicyUrls = getMemberPolicyUrlAll(resourceWithAcr);
const acrPolicyUrls = getAcrPolicyUrlAll(resourceWithAcr);
const memberAcrPolicyUrls = getMemberAcrPolicyUrlAll(resourceWithAcr);
if (
policyUrls.length === 0 &&
memberPolicyUrls.length === 0 &&
acrPolicyUrls.length === 0 &&
memberAcrPolicyUrls.length === 0
) {
markdown += "\n<no policies specified yet>\n";
}
if (policyUrls.length > 0) {
markdown += "\nThe following policies apply to this resource:\n- ";
markdown += policyUrls.join("\n- ") + "\n";
}
if (acrPolicyUrls.length > 0) {
markdown +=
"\nThe following policies apply to the access control resource for this resource:\n- ";
markdown += acrPolicyUrls.join("\n- ") + "\n";
}
if (memberPolicyUrls.length > 0) {
markdown +=
"\nThe following policies apply to the children of this resource:\n- ";
markdown += memberPolicyUrls.join("\n- ") + "\n";
}
if (memberAcrPolicyUrls.length > 0) {
markdown +=
"\nThe following policies apply to the access control resources for children of this resource:\n- ";
markdown += memberAcrPolicyUrls.join("\n- ") + "\n";
}
return markdown;
}