@inrupt/solid-client
Version:
Make your web apps work with Solid Pods.
296 lines (293 loc) • 15.2 kB
JavaScript
import { acl } from '../constants.mjs';
import { getSolidDataset, saveSolidDatasetAt } from '../resource/solidDataset.mjs';
import { setThing } from '../thing/thing.mjs';
import { dataset } from '../rdfjs.mjs';
import { removeAll } from '../thing/remove.mjs';
import { setIri } from '../thing/set.mjs';
import { getSourceUrl, internal_defaultFetchOptions, getResourceInfo } from '../resource/resource.mjs';
import { getFile } from '../resource/file.mjs';
import { internal_cloneResource } from '../resource/resource.internal.mjs';
import { internal_fetchAcl, internal_setAcl, internal_getAclRules, internal_getDefaultAclRulesForResource } from './acl.internal.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.
*/
/**
* ```{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.
* ```
*
* Verify whether a given SolidDataset was fetched together with its Access Control List.
*
* @param dataset A [[SolidDataset]] that may have its ACLs attached.
* @returns True if `dataset` was fetched together with its ACLs.
*/
function hasAcl(dataset) {
const potentialAcl = dataset;
return typeof potentialAcl.internal_acl === "object";
}
/**
* ```{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.
* ```
*
* Verifies whether the given Resource has a resource ACL (Access Control List) attached.
*
* The [[hasResourceAcl]] function checks that:
* - a given Resource has a resource ACL attached, and
* - the user calling [[hasResourceAcl]] has Control access to the Resource.
*
* To retrieve a Resource with its ACLs, see [[getSolidDatasetWithAcl]].
*
* @param resource A Resource that might have an ACL attached.
* @returns `true` if the Resource has a resource ACL attached that is accessible by the user.
*/
function hasResourceAcl(resource) {
return (resource.internal_acl.resourceAcl !== null &&
getSourceUrl(resource) ===
resource.internal_acl.resourceAcl.internal_accessTo &&
resource.internal_resourceInfo.aclUrl ===
getSourceUrl(resource.internal_acl.resourceAcl));
}
/**
* Experimental: fetch a SolidDataset and its associated Access Control List.
*
* This is an experimental function that fetches both a Resource, the linked ACL Resource (if
* available), and the ACL that applies to it if the linked ACL Resource is not available. This can
* result in many HTTP requests being executed, in lieu of the Solid spec mandating servers to
* provide this info in a single request. Therefore, and because this function is still
* experimental, prefer [[getSolidDataset]] instead.
*
* If the Resource does not advertise the ACL Resource (because the authenticated user does not have
* access to it), the `acl` property in the returned value will be null. `acl.resourceAcl` will be
* undefined if the Resource's linked ACL Resource could not be fetched (because it does not exist),
* and `acl.fallbackAcl` will be null if the applicable Container's ACL is not accessible to the
* authenticated user.
*
* @param url URL of the SolidDataset to fetch.
* @param options Optional parameter `options.fetch`: An alternative `fetch` function to make the HTTP request, compatible with the browser-native [fetch API](https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/fetch#parameters).
* @returns A SolidDataset and the ACLs that apply to it, if available to the authenticated user.
*/
async function getSolidDatasetWithAcl(url, options = internal_defaultFetchOptions) {
const solidDataset = await getSolidDataset(url, options);
const acl = await internal_fetchAcl(solidDataset, options);
return internal_setAcl(solidDataset, acl);
}
/**
* ```{note} This function is still experimental and subject to change, even in a non-major release.
* ```
*
* Retrieves a file, its resource ACL (Access Control List) if available,
* and its fallback ACL from a URL and returns them as a blob.
*
* If the user calling the function does not have access to the file's resource ACL,
* [[hasAccessibleAcl]] on the returned blob returns false.
* If the user has access to the file's resource ACL but the resource ACL does not exist,
* [[getResourceAcl]] on the returned blob returns null.
* If the fallback ACL is inaccessible by the user,
* [[getFallbackAcl]] on the returned blob returns null.
*
* ```{tip}
* To retrieve the ACLs, the function results in multiple HTTP requests rather than a single
* request as mandated by the Solid spec. As such, prefer [[getFile]] instead if you do not need the ACL.
* ```
*
* @param url The URL of the fetched file
* @param options Fetching options: a custom fetcher and/or headers.
* @returns A file and its ACLs, if available to the authenticated user, as a blob.
* @since 0.2.0
*/
async function getFileWithAcl(input, options = internal_defaultFetchOptions) {
const file = await getFile(input, options);
const acl = await internal_fetchAcl(file, options);
return internal_setAcl(file, acl);
}
/**
* Experimental: fetch a Resource's metadata and its associated Access Control List.
*
* This is an experimental function that fetches both a Resource's metadata, the linked ACL Resource (if
* available), and the ACL that applies to it if the linked ACL Resource is not available (if accessible). This can
* result in many HTTP requests being executed, in lieu of the Solid spec mandating servers to
* provide this info in a single request.
*
* If the Resource's linked ACL Resource could not be fetched (because it does not exist, or because
* the authenticated user does not have access to it), `acl.resourceAcl` will be `null`. If the
* applicable Container's ACL is not accessible to the authenticated user, `acl.fallbackAcl` will be
* `null`.
*
* @param url URL of the SolidDataset to fetch.
* @param options Optional parameter `options.fetch`: An alternative `fetch` function to make the HTTP request, compatible with the browser-native [fetch API](https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/fetch#parameters).
* @returns A Resource's metadata and the ACLs that apply to the Resource, if available to the authenticated user.
*/
async function getResourceInfoWithAcl(url, options = internal_defaultFetchOptions) {
const resourceInfo = await getResourceInfo(url, options);
const acl = await internal_fetchAcl(resourceInfo, options);
return internal_setAcl(resourceInfo, acl);
}
function getResourceAcl(resource) {
if (!hasResourceAcl(resource)) {
return null;
}
return resource.internal_acl.resourceAcl;
}
/**
* ```{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.
* ```
*
* Verifies whether the given Resource has a fallback ACL (Access Control List) attached.
*
* A fallback ACL for a Resource is inherited from the Resource's parent Container
* (or another of its ancestor Containers) and applies if the Resource does
* not have its own resource ACL.
*
* The [[hasFallbackAcl]] function checks that:
* - a given Resource has a fallback ACL attached, and
* - the user calling [[hasFallbackAcl]] has Control access to the Container
* from which the Resource inherits its ACL.
*
* To retrieve a Resource with its ACLs, see [[getSolidDatasetWithAcl]].
*
* @param resource A [[SolidDataset]] that might have a fallback ACL attached.
*
* @returns `true` if the Resource has a fallback ACL attached that is accessible to the user.
*/
function hasFallbackAcl(resource) {
return resource.internal_acl.fallbackAcl !== null;
}
function getFallbackAcl(dataset) {
if (!hasFallbackAcl(dataset)) {
return null;
}
return dataset.internal_acl.fallbackAcl;
}
/**
* ```{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.
* ```
*
* Creates an empty resource ACL (Access Control List) for a given Resource.
*
* @param targetResource A Resource that does not have its own ACL yet (see [[hasResourceAcl]]).
* @returns An empty resource ACL for the given Resource.
*/
function createAcl(targetResource) {
const emptyResourceAcl = Object.assign(dataset(), {
internal_accessTo: getSourceUrl(targetResource),
internal_resourceInfo: {
sourceIri: targetResource.internal_resourceInfo.aclUrl,
isRawData: false,
linkedResources: {},
},
});
return emptyResourceAcl;
}
/**
* ```{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.
* ```
*
* Creates a resource ACL (Access Control List), initialised from the fallback ACL
* inherited from the given Resource's Container (or another of its ancestor Containers).
* That is, the new ACL has the same rules/entries as the fallback ACL that currently
* applies to the Resource.
*
* @param resource A Resource without its own resource ACL (see [[hasResourceAcl]]) but with an accessible fallback ACL (see [[hasFallbackAcl]]).
* @returns A resource ACL initialised with the rules/entries from the Resource's fallback ACL.
*/
function createAclFromFallbackAcl(resource) {
const emptyResourceAcl = createAcl(resource);
const fallbackAclRules = internal_getAclRules(resource.internal_acl.fallbackAcl);
const defaultAclRules = internal_getDefaultAclRulesForResource(fallbackAclRules, resource.internal_acl.fallbackAcl.internal_accessTo);
const newAclRules = defaultAclRules.map((rule) => {
rule = removeAll(rule, acl.default);
rule = removeAll(rule, acl.defaultForNew);
rule = setIri(rule, acl.accessTo, getSourceUrl(resource));
rule = setIri(rule, acl.default, getSourceUrl(resource));
return rule;
});
// Iterate over every ACL Rule we want to import, inserting them into `emptyResourceAcl` one by one:
const initialisedResourceAcl = newAclRules.reduce(setThing, emptyResourceAcl);
return initialisedResourceAcl;
}
/**
* ```{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.
* ```
*
* Saves the resource ACL for a Resource.
*
* @param resource The Resource to which the given resource ACL applies.
* @param resourceAcl An [[AclDataset]] whose ACL Rules will apply to `resource`.
* @param options Optional parameter `options.fetch`: An alternative `fetch` function to make the HTTP request, compatible with the browser-native [fetch API](https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/fetch#parameters).
*/
async function saveAclFor(resource, resourceAcl, options = internal_defaultFetchOptions) {
if (!hasAccessibleAcl(resource)) {
throw new Error(`Could not determine the location of the ACL for the Resource at [${getSourceUrl(resource)}]; possibly the current user does not have Control access to that Resource. Try calling \`hasAccessibleAcl()\` before calling \`saveAclFor()\`.`);
}
const savedDataset = await saveSolidDatasetAt(resource.internal_resourceInfo.aclUrl, resourceAcl, options);
const savedAclDataset = Object.assign(savedDataset, {
internal_accessTo: getSourceUrl(resource),
});
return savedAclDataset;
}
/**
* ```{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.
* ```
*
* Removes the resource ACL (Access Control List) from a Resource.
*
* Once the resource ACL is removed from the Resource, the Resource relies on the
* fallback ACL inherited from the Resource's parent Container (or another of its ancestor Containers).
*
* @param resource The Resource for which you want to delete the ACL.
* @param options Optional parameter `options.fetch`: An alternative `fetch` function to make the HTTP request, compatible with the browser-native [fetch API](https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/fetch#parameters).
*/
async function deleteAclFor(resource, options = internal_defaultFetchOptions) {
const config = Object.assign(Object.assign({}, internal_defaultFetchOptions), options);
const response = await config.fetch(resource.internal_resourceInfo.aclUrl, {
method: "DELETE",
});
if (!response.ok) {
throw new Error(`Deleting the ACL of the Resource at [${getSourceUrl(resource)}] failed: [${response.status}] [${response.statusText}].`);
}
const storedResource = Object.assign(internal_cloneResource(resource), {
acl: {
resourceAcl: null,
},
});
return storedResource;
}
/**
* Given a [[SolidDataset]], verify whether its Access Control List is accessible to the current user.
*
* This should generally only be true for SolidDatasets fetched by
* [[getSolidDatasetWithAcl]].
*
* Please note that the Web Access Control specification is not yet finalised, and hence, this
* function is still experimental and can change in a non-major release.
*
* @param dataset A [[SolidDataset]].
* @returns Whether the given `dataset` has a an ACL that is accessible to the current user.
*/
function hasAccessibleAcl(dataset) {
return typeof dataset.internal_resourceInfo.aclUrl === "string";
}
export { createAcl, createAclFromFallbackAcl, deleteAclFor, getFallbackAcl, getFileWithAcl, getResourceAcl, getResourceInfoWithAcl, getSolidDatasetWithAcl, hasAccessibleAcl, hasAcl, hasFallbackAcl, hasResourceAcl, saveAclFor };