UNPKG

@inrupt/solid-client

Version:

Make your web apps work with Solid Pods.

1,435 lines (1,253 loc) • 67.2 kB
// 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