UNPKG

@inrupt/solid-client

Version:
1,451 lines (1,271 loc) • 82 kB
/** * Copyright 2020 Inrupt Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the * Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import { describe, it, expect } from "@jest/globals"; import { DataFactory } from "n3"; import { AccessControlResource, acrAsMarkdown, addAcrPolicyUrl, addMemberAcrPolicyUrl, addMemberPolicyUrl, addPolicyUrl, getAcrPolicyUrlAll, getMemberAcrPolicyUrlAll, getMemberPolicyUrlAll, getPolicyUrlAll, hasLinkedAcr, removeAcrPolicyUrl, removeAcrPolicyUrlAll, removeMemberAcrPolicyUrl, removeMemberAcrPolicyUrlAll, removeMemberPolicyUrl, removeMemberPolicyUrlAll, removePolicyUrl, removePolicyUrlAll, WithLinkedAcr, } from "./control"; import { internal_createControl, internal_getControl, internal_getControlAll, internal_setControl, internal_addMemberPolicyUrl, internal_addPolicyUrl, internal_getMemberPolicyUrlAll, internal_getPolicyUrlAll, internal_removeMemberPolicyUrl, internal_removeMemberPolicyUrlAll, internal_removePolicyUrl, internal_removePolicyUrlAll, } from "./control.internal"; import { acp, rdf } from "../constants"; import { WithServerResourceInfo } from "../interfaces"; import { getIri, getIriAll } from "../thing/get"; import { createThing, getThing, setThing } from "../thing/thing"; import { addMockAcrTo, mockAcrFor } from "./mock"; import { setIri, setUrl } from "../thing/set"; import { addIri } from "../thing/add"; import { mockSolidDatasetFrom } from "../resource/mock"; import { WithAccessibleAcl } from "../acl/acl"; import { getSourceUrl } from "../resource/resource"; import { removeControl } from "./v1"; 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).toEqual([control]); }); 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).toEqual([control]); }); 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("removeControl", () => { it("removes the given Access Control from 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 = setThing( mockAcrFor("https://some.pod/resource"), control ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://arbitrary.pod/resource"), accessControlResource ); const newWithAccessControlResource = removeControl( resourceWithAcr, control ); expect( getThing(newWithAccessControlResource.internal_acp.acr, controlUrl) ).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 control = setUrl( createThing({ url: controlUrl }), rdf.type, acp.AccessControl ); const withoutAcr = mockSolidDatasetFrom("https://some.pod/resource"); expect(() => removeControl(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 acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(1); expect(acrQuads[0].predicate.value).toBe(acp.access); expect(acrQuads[0].object.value).toBe( "https://some.pod/policy-resource#policy" ); }); it("does not remove existing Policy URLs", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#other-policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = addAcrPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(2); expect(acrQuads[0].predicate.value).toBe(acp.access); expect(acrQuads[0].object.value).toBe( "https://some.pod/policy-resource#other-policy" ); expect(acrQuads[1].predicate.value).toBe(acp.access); expect(acrQuads[1].object.value).toBe( "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 ); addAcrPolicyUrl(resourceWithAcr, "https://some.pod/policy-resource#policy"); expect(accessControlResource.size).toBe(0); }); }); 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 acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(1); expect(acrQuads[0].predicate.value).toBe(acp.accessMembers); expect(acrQuads[0].object.value).toBe( "https://some.pod/policy-resource#policy" ); }); it("does not remove existing Policy URLs", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#other-policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = addMemberAcrPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(2); expect(acrQuads[0].predicate.value).toBe(acp.accessMembers); expect(acrQuads[0].object.value).toBe( "https://some.pod/policy-resource#other-policy" ); expect(acrQuads[1].predicate.value).toBe(acp.accessMembers); expect(acrQuads[1].object.value).toBe( "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 ); addMemberAcrPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); expect(accessControlResource.size).toBe(0); }); }); 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const policyUrls = getAcrPolicyUrlAll(resourceWithAcr); expect(policyUrls).toEqual([]); }); }); 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const policyUrls = getMemberAcrPolicyUrlAll(resourceWithAcr); expect(policyUrls).toEqual([]); }); }); describe("removeAcrPolicyUrl", () => { it("removes the given URL as a Policy from the given ACR", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removeAcrPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toEqual([]); }); 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#other-policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removeAcrPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(1); expect(acrQuads[0].predicate.value).toBe(acp.access); expect(acrQuads[0].object.value).toBe( "https://some.pod/policy-resource#other-policy" ); }); it("does not remove Member Control Policy URLs", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removeAcrPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(1); expect(acrQuads[0].predicate.value).toBe(acp.accessMembers); expect(acrQuads[0].object.value).toBe( "https://some.pod/policy-resource#policy" ); }); it("does not modify the input ACR", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); removeAcrPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const oldAcrQuads = Array.from(accessControlResource); expect(oldAcrQuads).toHaveLength(1); expect(oldAcrQuads[0].predicate.value).toBe(acp.access); expect(oldAcrQuads[0].object.value).toBe( "https://some.pod/policy-resource#policy" ); }); }); describe("removeMemberAcrPolicyUrl", () => { it("removes the given URL as a Policy from the given ACR", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removeMemberAcrPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toEqual([]); }); 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#other-policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removeMemberAcrPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(1); expect(acrQuads[0].predicate.value).toBe(acp.accessMembers); expect(acrQuads[0].object.value).toBe( "https://some.pod/policy-resource#other-policy" ); }); it("does not remove own Control Policy URLs", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removeMemberAcrPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(1); expect(acrQuads[0].predicate.value).toBe(acp.access); expect(acrQuads[0].object.value).toBe( "https://some.pod/policy-resource#policy" ); }); it("does not modify the input ACR", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); removeMemberAcrPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const oldAcrQuads = Array.from(accessControlResource); expect(oldAcrQuads).toHaveLength(1); expect(oldAcrQuads[0].predicate.value).toBe(acp.accessMembers); expect(oldAcrQuads[0].object.value).toBe( "https://some.pod/policy-resource#policy" ); }); }); describe("removeAcrPolicyUrlAll", () => { it("removes all URLs that served as its Policy from the given ACR", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#other-policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removeAcrPolicyUrlAll(resourceWithAcr); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toEqual([]); }); 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#other-policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removeAcrPolicyUrlAll(resourceWithAcr); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(1); expect(acrQuads[0].predicate.value).toBe(acp.accessMembers); expect(acrQuads[0].object.value).toBe( "https://some.pod/policy-resource#other-policy" ); }); it("does not modify the input ACR", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); removeAcrPolicyUrlAll(resourceWithAcr); const oldAcrQuads = Array.from(accessControlResource); expect(oldAcrQuads).toHaveLength(1); expect(oldAcrQuads[0].predicate.value).toBe(acp.access); expect(oldAcrQuads[0].object.value).toBe( "https://some.pod/policy-resource#policy" ); }); }); describe("removeMemberAcrPolicyUrlAll", () => { it("removes all URLs that served as its Policy from the given ACR", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#other-policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removeMemberAcrPolicyUrlAll(resourceWithAcr); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toEqual([]); }); 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.access), DataFactory.namedNode("https://some.pod/policy-resource#other-policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removeMemberAcrPolicyUrlAll(resourceWithAcr); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(1); expect(acrQuads[0].predicate.value).toBe(acp.access); expect(acrQuads[0].object.value).toBe( "https://some.pod/policy-resource#other-policy" ); }); it("does not modify the input ACR", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.accessMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); removeMemberAcrPolicyUrlAll(resourceWithAcr); const oldAcrQuads = Array.from(accessControlResource); expect(oldAcrQuads).toHaveLength(1); expect(oldAcrQuads[0].predicate.value).toBe(acp.accessMembers); expect(oldAcrQuads[0].object.value).toBe( "https://some.pod/policy-resource#policy" ); }); }); describe("addPolicyUrl", () => { it("adds the given URL as a Policy for the given Resource", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(rdf.type), DataFactory.namedNode(acp.AccessControl) ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = addPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(2); expect(acrQuads[1].predicate.value).toBe(acp.apply); expect(acrQuads[1].object.value).toBe( "https://some.pod/policy-resource#policy" ); }); it("does not remove existing Policy URLs", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(rdf.type), DataFactory.namedNode(acp.AccessControl) ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.apply), DataFactory.namedNode("https://some.pod/policy-resource#other-policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = addPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(3); expect(acrQuads[1].predicate.value).toBe(acp.apply); expect(acrQuads[1].object.value).toBe( "https://some.pod/policy-resource#other-policy" ); expect(acrQuads[2].predicate.value).toBe(acp.apply); expect(acrQuads[2].object.value).toBe( "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 ); addPolicyUrl(resourceWithAcr, "https://some.pod/policy-resource#policy"); const oldAcrQuads = Array.from(accessControlResource); expect(oldAcrQuads).toEqual([]); }); }); describe("addMemberPolicyUrl", () => { it("adds the given URL as a Policy for the given Resource's children", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(rdf.type), DataFactory.namedNode(acp.AccessControl) ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = addMemberPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(2); expect(acrQuads[1].predicate.value).toBe(acp.applyMembers); expect(acrQuads[1].object.value).toBe( "https://some.pod/policy-resource#policy" ); }); it("does not remove existing Policy URLs", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(rdf.type), DataFactory.namedNode(acp.AccessControl) ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.applyMembers), DataFactory.namedNode("https://some.pod/policy-resource#other-policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = addMemberPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(3); expect(acrQuads[1].predicate.value).toBe(acp.applyMembers); expect(acrQuads[1].object.value).toBe( "https://some.pod/policy-resource#other-policy" ); expect(acrQuads[2].predicate.value).toBe(acp.applyMembers); expect(acrQuads[2].object.value).toBe( "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 ); addMemberPolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const oldAcrQuads = Array.from(accessControlResource); expect(oldAcrQuads).toEqual([]); }); }); 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(rdf.type), DataFactory.namedNode(acp.AccessControl) ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.apply), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(rdf.type), DataFactory.namedNode(acp.AccessControl) ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.applyMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(rdf.type), DataFactory.namedNode(acp.AccessControl) ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.applyMembers), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const policyUrls = getMemberPolicyUrlAll(resourceWithAcr); expect(policyUrls).toEqual(["https://some.pod/policy-resource#policy"]); }); it("does not return own Policy URLs", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(rdf.type), DataFactory.namedNode(acp.AccessControl) ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.apply), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const policyUrls = getMemberPolicyUrlAll(resourceWithAcr); expect(policyUrls).toEqual([]); }); }); describe("removePolicyUrl", () => { it("removes the given URL as a Policy from the given ACR", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(rdf.type), DataFactory.namedNode(acp.AccessControl) ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.apply), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removePolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(1); }); 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 = removePolicyUrl( 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"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(rdf.type), DataFactory.namedNode(acp.AccessControl) ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.apply), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.apply), DataFactory.namedNode("https://some.pod/policy-resource#other-policy") ) ); const resourceWithAcr = addMockAcrTo( mockSolidDatasetFrom("https://some.pod/resource"), accessControlResource ); const updatedResourceWithAcr = removePolicyUrl( resourceWithAcr, "https://some.pod/policy-resource#policy" ); const acrQuads = Array.from(updatedResourceWithAcr.internal_acp.acr); expect(acrQuads).toHaveLength(2); expect(acrQuads[1].predicate.value).toBe(acp.apply); expect(acrQuads[1].object.value).toBe( "https://some.pod/policy-resource#other-policy" ); }); it("does not remove Member Control Policy URLs", () => { const accessControlResource = mockAcrFor("https://some.pod/resource"); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(rdf.type), DataFactory.namedNode(acp.AccessControl) ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.namedNode(acp.apply), DataFactory.namedNode("https://some.pod/policy-resource#policy") ) ); accessControlResource.add( DataFactory.quad( DataFactory.namedNode(getSourceUrl(accessControlResource)), DataFactory.