@inrupt/solid-client
Version:
Make your web apps work with Solid Pods.
1,435 lines (1,253 loc) • 67.2 kB
text/typescript
// Copyright 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 { describe, it, expect } from "@jest/globals";
import type { AccessControlResource, WithLinkedAcr } from "./control";
import {
addAcrPolicyUrl,
addMemberAcrPolicyUrl,
addMemberPolicyUrl,
addPolicyUrl,
getAcrPolicyUrlAll,
getMemberAcrPolicyUrlAll,
getMemberPolicyUrlAll,
getPolicyUrlAll,
hasLinkedAcr,
removeAcrPolicyUrl,
removeAcrPolicyUrlAll,
removeMemberAcrPolicyUrl,
removeMemberAcrPolicyUrlAll,
removeMemberPolicyUrl,
removeMemberPolicyUrlAll,
removePolicyUrl,
removePolicyUrlAll,
} from "./control";
import {
internal_createControl,
internal_getAcr,
internal_getControl,
internal_getControlAll,
internal_setAcr,
internal_setControl,
} from "./control.internal";
import { acp, rdf } from "../constants";
import type { WithServerResourceInfo } from "../interfaces";
import { getIri, getUrl, getUrlAll } from "../thing/get";
import {
asIri,
asUrl,
createThing,
getThing,
getThingAll,
removeThing,
setThing,
} from "../thing/thing";
import { addMockAcrTo, mockAcrFor } from "./mock";
import { setIri, setUrl } from "../thing/set";
import { addUrl } from "../thing/add";
import { mockSolidDatasetFrom } from "../resource/mock";
import type { WithAccessibleAcl } from "../acl/acl";
import { getSourceUrl } from "../resource/resource";
describe("hasLinkedAcr", () => {
it("returns true if a Resource exposes a URL to an Access Control Resource", () => {
const withLinkedAcr: WithLinkedAcr = {
internal_resourceInfo: {
isRawData: false,
sourceIri: "https://some.pod/resource",
linkedResources: {
[acp.accessControl]: ["https://some.pod/access-control-resource"],
},
},
};
expect(hasLinkedAcr(withLinkedAcr)).toBe(true);
});
it("returns false if a Resource is governed by Web-Access-Control", () => {
const withLinkedAcr: WithAccessibleAcl = {
internal_resourceInfo: {
isRawData: false,
sourceIri: "https://some.pod/resource",
linkedResources: {
acl: ["https://some.pod/access-control-resource"],
},
aclUrl: "https://some.pod/access-control-resource",
},
};
expect(hasLinkedAcr(withLinkedAcr)).toBe(false);
});
it("returns false if a Resource does not expose anything Access Control-related", () => {
const withLinkedAcr: WithServerResourceInfo = {
internal_resourceInfo: {
isRawData: false,
sourceIri: "https://some.pod/resource",
linkedResources: {},
},
};
expect(hasLinkedAcr(withLinkedAcr)).toBe(false);
});
});
describe("createControl", () => {
it("sets the type of the new Access Control to acp:AccessControl", () => {
const newControl = internal_createControl();
expect(getIri(newControl, rdf.type)).toBe(acp.AccessControl);
});
});
describe("getControl", () => {
it("returns the Access Control if found", () => {
const controlUrl =
"https://some.pod/access-control-resource.ttl#access-control";
const control = setUrl(
createThing({ url: controlUrl }),
rdf.type,
acp.AccessControl,
);
const accessControlResource = setThing(
mockAcrFor("https://some.pod/resource"),
control,
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://arbitrary.pod/resource"),
accessControlResource,
);
const foundControl = internal_getControl(resourceWithAcr, controlUrl);
expect(foundControl).toEqual(control);
});
it("returns null if the specified Thing is not an Access Control", () => {
const controlUrl =
"https://some.pod/access-control-resource.ttl#access-control";
const control = createThing({ url: controlUrl });
const accessControlResource = setThing(
mockAcrFor("https://some.pod/resource"),
control,
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://arbitrary.pod/resource"),
accessControlResource,
);
const foundControl = internal_getControl(resourceWithAcr, controlUrl);
expect(foundControl).toBeNull();
});
it("returns null if the Access Control could not be found", () => {
const controlUrl =
"https://some.pod/access-control-resource.ttl#access-control";
const control = createThing({ url: controlUrl });
const accessControlResource = setThing(
mockAcrFor("https://some.pod/resource"),
control,
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://arbitrary.pod/resource"),
accessControlResource,
);
const foundControl = internal_getControl(
resourceWithAcr,
"https://some-other.pod/access-control-resource.ttl#access-control",
);
expect(foundControl).toBeNull();
});
it("throws an error if the given Resource does not have an Access Control Resource", () => {
const controlUrl =
"https://some.pod/access-control-resource.ttl#access-control";
const withoutAcr = mockSolidDatasetFrom("https://some.pod/resource");
expect(() => internal_getControl(withoutAcr as any, controlUrl)).toThrow(
"An Access Control Resource for [https://some.pod/resource] is not available. This could be because the current user is not allowed to see it, or because their Pod Server does not support Access Control Resources.",
);
});
});
describe("getControlAll", () => {
it("returns all included Access Controls", () => {
const control = setUrl(createThing(), rdf.type, acp.AccessControl);
const accessControlResource = setThing(
mockAcrFor("https://some.pod/resource"),
control,
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://arbitrary.pod/resource"),
accessControlResource,
);
const foundControls = internal_getControlAll(resourceWithAcr);
expect(foundControls).toHaveLength(1);
});
it("returns an Access Control if linked to with the acp:accessControl predicate even if not explicitly typed", () => {
const controlUrl =
"https://some.pod/access-control-resource.ttl#access-control";
const acr = mockAcrFor("https://some.pod/resource");
const acrThing = setIri(
createThing({ url: getSourceUrl(acr) }),
acp.accessControl,
controlUrl,
);
const accessControlResource = setThing(
mockAcrFor("https://some.pod/resource"),
acrThing,
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://arbitrary.pod/resource"),
accessControlResource,
);
const foundControl = internal_getControlAll(resourceWithAcr);
expect(foundControl).toHaveLength(1);
expect(foundControl.map(asIri)).toContain(controlUrl);
});
it("returns both explicitly and implicitly typed Access Control", () => {
const implicitControlIri =
"https://some.pod/access-control-resource.ttl#implicit-access-control";
const acr = mockAcrFor("https://some.pod/resource");
const acrThing = setIri(
createThing({ url: getSourceUrl(acr) }),
acp.accessControl,
implicitControlIri,
);
let accessControlResource = setThing(
mockAcrFor("https://some.pod/resource"),
acrThing,
);
const explicitControlIri =
"https://some.pod/access-control-resource.ttl#explicit-access-control";
const explicitControl = setUrl(
createThing({ url: explicitControlIri }),
rdf.type,
acp.AccessControl,
);
accessControlResource = setThing(accessControlResource, explicitControl);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://arbitrary.pod/resource"),
accessControlResource,
);
const foundControl = internal_getControlAll(resourceWithAcr);
expect(foundControl).toHaveLength(2);
expect(foundControl.map(asIri)).toContain(implicitControlIri);
expect(foundControl.map(asIri)).toContain(explicitControlIri);
});
it("ignores Things that are not Access Controls", () => {
const control = setUrl(createThing(), rdf.type, acp.AccessControl);
const notAControl = setUrl(
createThing(),
rdf.type,
"https://some.vocab/not-access-control",
);
let accessControlResource = mockAcrFor("https://some.pod/resource");
accessControlResource = setThing(accessControlResource, control);
accessControlResource = setThing(accessControlResource, notAControl);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://arbitrary.pod/resource"),
accessControlResource,
);
const foundControls = internal_getControlAll(resourceWithAcr);
expect(foundControls).toHaveLength(1);
});
it("returns an empty array if no Access Controls could be found", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://arbitrary.pod/resource"),
accessControlResource,
);
const foundControl = internal_getControlAll(resourceWithAcr);
expect(foundControl).toEqual([]);
});
it("throws an error if the given Resource does not have an Access Control Resource", () => {
const withoutAcr = mockSolidDatasetFrom("https://some.pod/resource");
expect(() => internal_getControlAll(withoutAcr as any)).toThrow(
"An Access Control Resource for [https://some.pod/resource] is not available. This could be because the current user is not allowed to see it, or because their Pod Server does not support Access Control Resources.",
);
});
it("throws an error if the given Resource's ACR could not be fetched", () => {
const withoutAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
null as unknown as AccessControlResource,
);
expect(() => internal_getControlAll(withoutAcr as any)).toThrow(
"An Access Control Resource for [https://some.pod/resource] is not available. This could be because the current user is not allowed to see it, or because their Pod Server does not support Access Control Resources.",
);
});
});
describe("setControl", () => {
it("adds the given Access Control to the given Access Control Resource", () => {
const controlUrl =
"https://some.pod/access-control-resource.ttl#access-control";
const control = setUrl(
createThing({ url: controlUrl }),
rdf.type,
acp.AccessControl,
);
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://arbitrary.pod/resource"),
accessControlResource,
);
const newWithAccessControlResource = internal_setControl(
resourceWithAcr,
control,
);
expect(
getThing(newWithAccessControlResource.internal_acp.acr, controlUrl),
).toEqual(control);
});
it("throws an error if the given Resource does not have an Access Control Resource", () => {
const accessUrl =
"https://some.pod/access-control-resource.ttl#access-control";
const control = setUrl(
createThing({ url: accessUrl }),
rdf.type,
acp.AccessControl,
);
const withoutAcr = mockSolidDatasetFrom("https://some.pod/resource");
expect(() => internal_setControl(withoutAcr as any, control)).toThrow(
"An Access Control Resource for [https://some.pod/resource] is not available. This could be because the current user is not allowed to see it, or because their Pod Server does not support Access Control Resources.",
);
});
});
describe("addAcrPolicyUrl", () => {
it("adds the given URL as a Policy for the given ACR", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const updatedResourceWithAcr = addAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.access)).toBe(
"https://some.pod/policy-resource#policy",
);
});
it("does not remove existing Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.access,
"https://some.pod/policy-resource#other-policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = addAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrlAll(controls[0], acp.access)).toContain(
"https://some.pod/policy-resource#other-policy",
);
expect(getUrlAll(controls[0], acp.access)).toContain(
"https://some.pod/policy-resource#policy",
);
});
it("does not modify the input ACR", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const inputControlsBefore = getThingAll(accessControlResource);
addAcrPolicyUrl(resourceWithAcr, "https://some.pod/policy-resource#policy");
const inputControlsAfter = getThingAll(accessControlResource);
expect(inputControlsAfter).toEqual(inputControlsBefore);
});
it("creates the acr subject", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const inputControlsBefore = getThingAll(accessControlResource);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const updatedAcr = removeThing(acr, acrUrl);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
addAcrPolicyUrl(updatedResource, "https://some.pod/policy-resource#policy");
const inputControlsAfter = getThingAll(accessControlResource);
expect(inputControlsAfter).toEqual(inputControlsBefore);
});
});
describe("addMemberAcrPolicyUrl", () => {
it("adds the given URL as a Policy for the given ACR's children's ACRs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const updatedResourceWithAcr = addMemberAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.accessMembers)).toBe(
"https://some.pod/policy-resource#policy",
);
});
it("does not remove existing Policy URLs", () => {
let accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.accessMembers,
"https://some.pod/policy-resource#other-policy",
);
accessControlResource = setThing(accessControlResource, existingControl);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const updatedResourceWithAcr = addMemberAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrlAll(controls[0], acp.accessMembers)).toContain(
"https://some.pod/policy-resource#other-policy",
);
expect(getUrlAll(controls[0], acp.accessMembers)).toContain(
"https://some.pod/policy-resource#policy",
);
});
it("does not modify the input ACR", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const inputControlsBefore = getThingAll(accessControlResource);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
addMemberAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const inputControlsAfter = getThingAll(accessControlResource);
expect(inputControlsAfter).toEqual(inputControlsBefore);
});
it("creates the acr subject", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const inputControlsBefore = getThingAll(accessControlResource);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const updatedAcr = removeThing(acr, acrUrl);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
addMemberAcrPolicyUrl(
updatedResource,
"https://some.pod/policy-resource#policy",
);
const inputControlsAfter = getThingAll(accessControlResource);
expect(inputControlsAfter).toEqual(inputControlsBefore);
});
});
describe("getAcrPolicyUrlAll", () => {
it("returns an empty array if no Policy URLs are defined for the ACR", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const policyUrls = getAcrPolicyUrlAll(resourceWithAcr);
expect(policyUrls).toEqual([]);
});
it("returns all applicable Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.access,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const policyUrls = getAcrPolicyUrlAll(resourceWithAcr);
expect(policyUrls).toEqual(["https://some.pod/policy-resource#policy"]);
});
it("does not return Member Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const policyUrls = getAcrPolicyUrlAll(resourceWithAcr);
expect(policyUrls).toEqual([]);
});
it("does not return policies if acr does not have an anchor node", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const updatedAcr = removeThing(acr, acrUrl);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
expect(getAcrPolicyUrlAll(updatedResource)).toHaveLength(0);
});
});
describe("getMemberAcrPolicyUrlAll", () => {
it("returns an empty array if no Policy URLs are defined for the ACR's children's ACRs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const policyUrls = getMemberAcrPolicyUrlAll(resourceWithAcr);
expect(policyUrls).toEqual([]);
});
it("returns all applicable Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const policyUrls = getMemberAcrPolicyUrlAll(resourceWithAcr);
expect(policyUrls).toEqual(["https://some.pod/policy-resource#policy"]);
});
it("does not return own Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.access,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const policyUrls = getMemberAcrPolicyUrlAll(resourceWithAcr);
expect(policyUrls).toEqual([]);
});
it("does not return policies if acr does not have an anchor node", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const updatedAcr = removeThing(acr, acrUrl);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
expect(getMemberAcrPolicyUrlAll(updatedResource)).toHaveLength(0);
});
});
describe("removeAcrPolicyUrl", () => {
it("removes the given URL as a Policy from the given ACR", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.access,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = removeAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(getUrlAll(controls[0], acp.access)).toHaveLength(0);
});
it("returns the input unchanged if there was nothing to remove", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const updatedResourceWithAcr = removeAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
expect(updatedResourceWithAcr).toEqual(resourceWithAcr);
});
it("does not remove existing mismatching Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.access,
"https://some.pod/policy-resource#policy",
);
existingControl = addUrl(
existingControl,
acp.access,
"https://some.pod/policy-resource#other-policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = removeAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.access)).toBe(
"https://some.pod/policy-resource#other-policy",
);
});
it("does not remove Member Control Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.access,
"https://some.pod/policy-resource#policy",
);
existingControl = addUrl(
existingControl,
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = removeAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.accessMembers)).toBe(
"https://some.pod/policy-resource#policy",
);
});
it("does not modify the input ACR", () => {
let accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.access,
"https://some.pod/policy-resource#policy",
);
accessControlResource = setThing(accessControlResource, existingControl);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
removeAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(accessControlResource);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.access)).toBe(
"https://some.pod/policy-resource#policy",
);
});
it("returns the resource unchanged if acr does not have an anchor node", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const updatedAcr = removeThing(acr, acrUrl);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
expect(
removeAcrPolicyUrl(
updatedResource,
"https://some.pod/policy-resource#policy",
),
).toEqual(updatedResource);
});
});
describe("removeMemberAcrPolicyUrl", () => {
it("removes the given URL as a Policy from the given ACR", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = removeMemberAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(getUrlAll(controls[0], acp.accessMembers)).toHaveLength(0);
});
it("returns the input unchanged if there was nothing to remove", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const updatedResourceWithAcr = removeMemberAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
expect(updatedResourceWithAcr).toEqual(resourceWithAcr);
});
it("does not remove existing mismatching Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
existingControl = addUrl(
existingControl,
acp.accessMembers,
"https://some.pod/policy-resource#other-policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = removeMemberAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.accessMembers)).toBe(
"https://some.pod/policy-resource#other-policy",
);
});
it("does not remove own Control Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
existingControl = addUrl(
existingControl,
acp.access,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = removeMemberAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.access)).toBe(
"https://some.pod/policy-resource#policy",
);
});
it("does not modify the input ACR", () => {
let accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
accessControlResource = setThing(accessControlResource, existingControl);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
removeMemberAcrPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(accessControlResource);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.accessMembers)).toBe(
"https://some.pod/policy-resource#policy",
);
});
it("returns the resource unchanged if acr does not have an anchor node", () => {
let accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
accessControlResource = setThing(accessControlResource, existingControl);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const updatedAcr = removeThing(acr, acrUrl);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
expect(
removeMemberAcrPolicyUrl(
updatedResource,
"https://some.pod/policy-resource#policy",
),
).toEqual(updatedResource);
});
});
describe("removeAcrPolicyUrlAll", () => {
it("removes all URLs that served as its Policy from the given ACR", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.access,
"https://some.pod/policy-resource#policy",
);
existingControl = addUrl(
existingControl,
acp.access,
"https://some.pod/policy-resource#other-policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = removeAcrPolicyUrlAll(resourceWithAcr);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(getUrlAll(controls[0], acp.access)).toHaveLength(0);
});
it("returns the input unchanged if there was nothing to remove", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const updatedResourceWithAcr = removeAcrPolicyUrlAll(resourceWithAcr);
expect(updatedResourceWithAcr).toEqual(resourceWithAcr);
});
it("does not remove Member Control Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.access,
"https://some.pod/policy-resource#policy",
);
existingControl = addUrl(
existingControl,
acp.accessMembers,
"https://some.pod/policy-resource#other-policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = removeAcrPolicyUrlAll(resourceWithAcr);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.accessMembers)).toBe(
"https://some.pod/policy-resource#other-policy",
);
});
it("does not modify the input ACR", () => {
let accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.access,
"https://some.pod/policy-resource#policy",
);
accessControlResource = setThing(accessControlResource, existingControl);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
removeAcrPolicyUrlAll(resourceWithAcr);
const controls = getThingAll(accessControlResource);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.access)).toBe(
"https://some.pod/policy-resource#policy",
);
});
it("returns the resource unchanged if acr does not have an anchor node", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const updatedAcr = removeThing(acr, acrUrl);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
expect(removeAcrPolicyUrlAll(updatedResource)).toEqual(updatedResource);
});
});
describe("removeMemberAcrPolicyUrlAll", () => {
it("removes all URLs that served as its Policy from the given ACR", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
existingControl = addUrl(
existingControl,
acp.accessMembers,
"https://some.pod/policy-resource#other-policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = removeMemberAcrPolicyUrlAll(resourceWithAcr);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(getUrlAll(controls[0], acp.accessMembers)).toHaveLength(0);
});
it("returns the input unchanged if there was nothing to remove", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const updatedResourceWithAcr = removeMemberAcrPolicyUrlAll(resourceWithAcr);
expect(updatedResourceWithAcr).toEqual(resourceWithAcr);
});
it("does not remove own Control Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
existingControl = addUrl(
existingControl,
acp.access,
"https://some.pod/policy-resource#other-policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = removeMemberAcrPolicyUrlAll(resourceWithAcr);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.access)).toBe(
"https://some.pod/policy-resource#other-policy",
);
});
it("does not modify the input ACR", () => {
let accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(
existingControl,
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
accessControlResource = setThing(accessControlResource, existingControl);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
removeMemberAcrPolicyUrlAll(resourceWithAcr);
const controls = getThingAll(accessControlResource);
expect(controls).toHaveLength(1);
expect(getUrl(controls[0], acp.accessMembers)).toBe(
"https://some.pod/policy-resource#policy",
);
});
it("returns the resource unchanged if acr does not have an anchor node", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const existingControl = addUrl(
createThing({ url: getSourceUrl(accessControlResource) }),
acp.accessMembers,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const acr = internal_getAcr(resourceWithAcr);
const acrUrl = getSourceUrl(acr);
const updatedAcr = removeThing(acr, acrUrl);
const updatedResource = internal_setAcr(resourceWithAcr, updatedAcr);
expect(removeMemberAcrPolicyUrlAll(updatedResource)).toEqual(
updatedResource,
);
});
});
describe("addPolicyUrl", () => {
it("adds the given URL as a Policy for the given Resource", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const oldControls = getThingAll(resourceWithAcr.internal_acp.acr);
const updatedResourceWithAcr = addPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const updatedControls = getThingAll(
updatedResourceWithAcr.internal_acp.acr,
);
const difference = updatedControls.filter((control) => {
return !oldControls.some((oldControl) => {
return asUrl(control) === asUrl(oldControl);
});
})[0];
expect(updatedControls).toHaveLength(oldControls.length + 1);
expect(getUrl(difference, acp.apply)).toBe(
"https://some.pod/policy-resource#policy",
);
});
it("does not remove existing Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(existingControl, rdf.type, acp.AccessControl);
existingControl = addUrl(
existingControl,
acp.apply,
"https://some.pod/policy-resource#other-policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = addPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrlAll(controls[0], acp.apply)).toContain(
"https://some.pod/policy-resource#other-policy",
);
expect(getUrlAll(controls[0], acp.apply)).toContain(
"https://some.pod/policy-resource#policy",
);
});
it("does not modify the input ACR", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const inputControlsBefore = getThingAll(accessControlResource);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
addPolicyUrl(resourceWithAcr, "https://some.pod/policy-resource#policy");
const inputControlsAfter = getThingAll(accessControlResource);
expect(inputControlsAfter).toEqual(inputControlsBefore);
});
});
describe("addMemberPolicyUrl", () => {
it("adds the given URL as a Policy for the given Resource's children", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const oldControls = getThingAll(resourceWithAcr.internal_acp.acr);
const updatedResourceWithAcr = addMemberPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const updatedControls = getThingAll(
updatedResourceWithAcr.internal_acp.acr,
);
const difference = updatedControls.filter((control) => {
return !oldControls.some((oldControl) => {
return asUrl(control) === asUrl(oldControl);
});
})[0];
expect(updatedControls).toHaveLength(oldControls.length + 1);
expect(getUrl(difference, acp.applyMembers)).toBe(
"https://some.pod/policy-resource#policy",
);
});
it("does not remove existing Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(existingControl, rdf.type, acp.AccessControl);
existingControl = addUrl(
existingControl,
acp.applyMembers,
"https://some.pod/policy-resource#other-policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const updatedResourceWithAcr = addMemberPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const controls = getThingAll(updatedResourceWithAcr.internal_acp.acr);
expect(controls).toHaveLength(1);
expect(getUrlAll(controls[0], acp.applyMembers)).toContain(
"https://some.pod/policy-resource#other-policy",
);
expect(getUrlAll(controls[0], acp.applyMembers)).toContain(
"https://some.pod/policy-resource#policy",
);
});
it("does not modify the input ACR", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const inputControlsBefore = getThingAll(accessControlResource);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
addMemberPolicyUrl(
resourceWithAcr,
"https://some.pod/policy-resource#policy",
);
const inputControlsAfter = getThingAll(accessControlResource);
expect(inputControlsAfter).toEqual(inputControlsBefore);
});
});
describe("getPolicyUrlAll", () => {
it("returns an empty array if no Policy URLs are defined for the Resource", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const policyUrls = getPolicyUrlAll(resourceWithAcr);
expect(policyUrls).toEqual([]);
});
it("returns all applicable Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(existingControl, rdf.type, acp.AccessControl);
existingControl = addUrl(
existingControl,
acp.apply,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const policyUrls = getPolicyUrlAll(resourceWithAcr);
expect(policyUrls).toEqual(["https://some.pod/policy-resource#policy"]);
});
it("does not return Member Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(existingControl, rdf.type, acp.AccessControl);
existingControl = addUrl(
existingControl,
acp.applyMembers,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const policyUrls = getPolicyUrlAll(resourceWithAcr);
expect(policyUrls).toEqual([]);
});
});
describe("getMemberPolicyUrlAll", () => {
it("returns an empty array if no Policy URLs are defined for the Resource's children", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
accessControlResource,
);
const policyUrls = getMemberPolicyUrlAll(resourceWithAcr);
expect(policyUrls).toEqual([]);
});
it("returns all applicable Policy URLs", () => {
const accessControlResource = mockAcrFor("https://some.pod/resource");
let existingControl = createThing({
url: getSourceUrl(accessControlResource),
});
existingControl = addUrl(existingControl, rdf.type, acp.AccessControl);
existingControl = addUrl(
existingControl,
acp.applyMembers,
"https://some.pod/policy-resource#policy",
);
const resourceWithAcr = addMockAcrTo(
mockSolidDatasetFrom("https://some.pod/resource"),
setThing(accessControlResource, existingControl),
);
const policyUrls = getMemberPolicyUrlAll