UNPKG

@inrupt/solid-client

Version:
1,719 lines (1,573 loc) • 246 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 } from "@jest/globals"; import * as fc from "fast-check"; import { WithAccessibleAcr } from "../acp/acp"; import { AccessControlResource, addPolicyUrl, getAcrPolicyUrlAll, getPolicyUrlAll, } from "../acp/control"; import { internal_createControl, internal_getAcr, } from "../acp/control.internal"; import { addMockAcrTo } from "../acp/mock"; import { createPolicy, getPolicy, removePolicy, setAllowModes, setDenyModes, setPolicy, } from "../acp/policy"; import { addNoneOfRuleUrl, addAnyOfRuleUrl, addAllOfRuleUrl, createRule, getRule, Rule, } from "../acp/rule"; import { acp } from "../constants"; import { IriString, ThingPersisted, UrlString, WithResourceInfo, WithServerResourceInfo, } from "../interfaces"; import { mockSolidDatasetFrom } from "../resource/mock"; import { addIri, addUrl } from "../thing/add"; import { getIri, getIriAll, getUrl, getUrlAll } from "../thing/get"; import { asIri, getThing, getThingAll, setThing } from "../thing/thing"; import { internal_getActorAccessAll, internal_getActorAccess, internal_getAgentAccess, internal_getAuthenticatedAccess, internal_getGroupAccess, internal_getPublicAccess, internal_hasInaccessiblePolicies, internal_getGroupAccessAll, internal_getAgentAccessAll, internal_setActorAccess, internal_setAgentAccess, internal_setGroupAccess, internal_setPublicAccess, internal_setAuthenticatedAccess, } from "./acp"; // Key: actor relation (e.g. agent), value: actor (e.g. a WebID) type MockRule = Partial< Record<typeof acp.agent | typeof acp.group, UrlString[]> >; interface MockAccess { read: boolean; append: boolean; write: boolean; } type MockPolicy = { allOf: Record<UrlString, MockRule>; anyOf: Record<UrlString, MockRule>; noneOf: Record<UrlString, MockRule>; allow: Partial<MockAccess>; deny: Partial<MockAccess>; }; type MockPolicies = { policies: Record<UrlString, Partial<MockPolicy>>; memberPolicies: Record<UrlString, Partial<MockPolicy>>; acrPolicies: Record<UrlString, Partial<MockPolicy>>; memberAcrPolicies: Record<UrlString, Partial<MockPolicy>>; }; const defaultAcrUrl = "https://some.pod/policies"; const defaultMockPolicy: MockPolicy = { allOf: {}, anyOf: {}, noneOf: {}, allow: {}, deny: {}, }; const defaultMockPolicies: MockPolicies = { policies: { [`${defaultAcrUrl}"#policy`]: defaultMockPolicy }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, }; function mockAcr( accessTo: UrlString, mockAcrUrl = defaultAcrUrl, mockPolicies: Partial<MockPolicies> = {} ): AccessControlResource { const allMockPolicies = { ...defaultMockPolicies, ...mockPolicies, }; let acr: AccessControlResource & WithServerResourceInfo = Object.assign( mockSolidDatasetFrom(mockAcrUrl), { accessTo: accessTo, } ); let control = internal_createControl({ url: mockAcrUrl }); function getRule(mockRuleUrl: UrlString, mockRule: MockRule): Rule { let rule = createRule(mockRuleUrl); Object.entries(mockRule).forEach(([mockActorRelation, mockActors]) => { mockActors?.forEach((mockActor) => { rule = addIri(rule, mockActorRelation, mockActor); }); }); return rule; } function addPolicy( policyType: IriString, policyUrl: UrlString, mockPolicy: Partial<MockPolicy> ) { let policy = createPolicy(policyUrl); const allOfRules = mockPolicy.allOf ? Object.entries(mockPolicy.allOf).map(([mockRuleUrl, mockRule]) => getRule(mockRuleUrl, mockRule) ) : []; const anyOfRules = mockPolicy.anyOf ? Object.entries(mockPolicy.anyOf).map(([mockRuleUrl, mockRule]) => getRule(mockRuleUrl, mockRule) ) : []; const noneOfRules = mockPolicy.noneOf ? Object.entries(mockPolicy.noneOf).map(([mockRuleUrl, mockRule]) => getRule(mockRuleUrl, mockRule) ) : []; acr = allOfRules.reduce(setThing, acr); acr = anyOfRules.reduce(setThing, acr); acr = noneOfRules.reduce(setThing, acr); if (mockPolicy.allow) { policy = setAllowModes(policy, { read: mockPolicy.allow.read === true, append: mockPolicy.allow.append === true, write: mockPolicy.allow.write === true, }); } if (mockPolicy.deny) { policy = setDenyModes(policy, { read: mockPolicy.deny.read === true, append: mockPolicy.deny.append === true, write: mockPolicy.deny.write === true, }); } policy = allOfRules.reduce( (policy, rule) => addIri(policy, acp.allOf, rule), policy ); policy = anyOfRules.reduce( (policy, rule) => addIri(policy, acp.anyOf, rule), policy ); policy = noneOfRules.reduce( (policy, rule) => addIri(policy, acp.noneOf, rule), policy ); acr = setThing(acr, policy); control = addUrl(control, policyType, policy); } Object.entries(allMockPolicies.policies).forEach( ([policyUrl, mockPolicy]) => { addPolicy(acp.apply, policyUrl, mockPolicy); } ); Object.entries(allMockPolicies.memberPolicies).forEach( ([policyUrl, mockPolicy]) => { addPolicy(acp.applyMembers, policyUrl, mockPolicy); } ); Object.entries(allMockPolicies.acrPolicies).forEach( ([policyUrl, mockPolicy]) => { addPolicy(acp.access, policyUrl, mockPolicy); } ); Object.entries(allMockPolicies.memberAcrPolicies).forEach( ([policyUrl, mockPolicy]) => { addPolicy(acp.accessMembers, policyUrl, mockPolicy); } ); acr = setThing(acr, control); return acr; } function mockResourceWithAcr( accessTo: UrlString, mockAcrUrl = defaultAcrUrl, mockPolicies: Partial<MockPolicies> = {} ): WithResourceInfo & WithAccessibleAcr { const acr = mockAcr(accessTo, mockAcrUrl, mockPolicies); const plainResource = mockSolidDatasetFrom(accessTo); return addMockAcrTo(plainResource, acr); } describe("hasInaccessiblePolicies", () => { it("returns false if the ACR contains no reference to either Policies or Rules", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/policies", { policies: {}, memberAcrPolicies: {}, acrPolicies: {}, memberPolicies: {}, } ); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(false); }); it("returns false if the ACR only contains references to Policies within the ACR", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": {} }, memberAcrPolicies: {}, acrPolicies: {}, memberPolicies: {}, } ); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(false); }); it("returns true if the ACR references a Policy in a different Resource", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/another-resource?ext=acr#policy": {} }, memberAcrPolicies: {}, acrPolicies: {}, memberPolicies: {}, } ); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(true); }); it("returns true if the ACR references a Policy in a different Resource, and the Policy is not defined in the ACR itself too", () => { const plainResource = mockSolidDatasetFrom("https://some.pod/resource"); let mockedAcr = mockAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/another-resource?ext=acr#policy": {} }, memberAcrPolicies: {}, acrPolicies: {}, memberPolicies: {}, } ); mockedAcr = removePolicy( mockedAcr, "https://some.pod/another-resource?ext=acr#policy" ); const resourceWithAcr = addMockAcrTo(plainResource, mockedAcr); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(true); }); it("returns true if the ACR references an ACR Policy in a different Resource", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberAcrPolicies: {}, acrPolicies: { "https://some.pod/another-resource?ext=acr#policy": {} }, memberPolicies: {}, } ); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(true); }); it("returns false if the ACR includes an unreferenced Policy with a different Resource's URL", () => { const plainResource = mockSolidDatasetFrom("https://some.pod/resource"); const policyInOtherResource = createPolicy( "https://some.pod/some-other-resource?ext=acr#inactive-policy" ); let mockedAcr = mockAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {} } ); mockedAcr = setPolicy(mockedAcr, policyInOtherResource); const resourceWithAcr = addMockAcrTo(plainResource, mockedAcr); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(false); }); it("returns false if the ACR only references Rules in the same Resource", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allOf: { "https://some.pod/resource?ext=acr#rule": {}, }, }, }, } ); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(false); }); it("returns true if the ACR references an allOf Rule in a different Resource", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberAcrPolicies: {}, acrPolicies: { "https://some.pod/resource?ext=acr#policy": { allOf: { "https://some.pod/other-rule-resource#rule": {}, }, }, }, memberPolicies: {}, } ); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(true); }); it("returns true if the ACR references an anyOf Rule in a different Resource", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberAcrPolicies: {}, acrPolicies: { "https://some.pod/resource?ext=acr#policy": { anyOf: { "https://some.pod/other-rule-resource#rule": {}, }, }, }, memberPolicies: {}, } ); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(true); }); it("returns true if the ACR references an active noneOf Rule in a different Resource", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberAcrPolicies: {}, acrPolicies: { "https://some.pod/resource?ext=acr#policy": { noneOf: { "https://some.pod/other-rule-resource#rule": {}, }, }, }, memberPolicies: {}, } ); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(true); }); it("returns false if the ACR includes an unreferenced Policy that references an allOf Rule in a different Resource", () => { let policyReferencingRuleInDifferentResource = createPolicy( "https://some.pod/resource?ext=acr#policy" ); policyReferencingRuleInDifferentResource = addAllOfRuleUrl( policyReferencingRuleInDifferentResource, "https://some.pod/other-resource#rule" ); const mockedAcr = setPolicy( mockAcr("https://some.pod/resource", "https://some.pod/resource", { policies: {}, }), policyReferencingRuleInDifferentResource ); const plainResource = mockSolidDatasetFrom("https://some.pod/resource"); const resourceWithAcr = addMockAcrTo(plainResource, mockedAcr); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(false); }); it("returns false if the ACR includes an unreferenced Policy that references an anyOf Rule in a different Resource", () => { let policyReferencingRuleInDifferentResource = createPolicy( "https://some.pod/resource?ext=acr#policy" ); policyReferencingRuleInDifferentResource = addAnyOfRuleUrl( policyReferencingRuleInDifferentResource, "https://some.pod/other-resource#rule" ); const mockedAcr = setPolicy( mockAcr("https://some.pod/resource", "https://some.pod/resource", { policies: {}, }), policyReferencingRuleInDifferentResource ); const plainResource = mockSolidDatasetFrom("https://some.pod/resource"); const resourceWithAcr = addMockAcrTo(plainResource, mockedAcr); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(false); }); it("returns false if the ACR includes an unreferenced Policy that references a noneOf Rule in a different Resource", () => { let policyReferencingRuleInDifferentResource = createPolicy( "https://some.pod/resource?ext=acr#policy" ); policyReferencingRuleInDifferentResource = addNoneOfRuleUrl( policyReferencingRuleInDifferentResource, "https://some.pod/other-resource#rule" ); const mockedAcr = setPolicy( mockAcr("https://some.pod/resource", "https://some.pod/resource", { policies: {}, }), policyReferencingRuleInDifferentResource ); const plainResource = mockSolidDatasetFrom("https://some.pod/resource"); const resourceWithAcr = addMockAcrTo(plainResource, mockedAcr); expect(internal_hasInaccessiblePolicies(resourceWithAcr)).toBe(false); }); }); describe("getActorAccess", () => { const webId = "https://some.pod/profile#me"; it("returns false for all access if no access was granted to the given actor", () => { const resourceWithAcr = mockResourceWithAcr( "https://arbitrary.pod/resource", "https://arbitrary.pod/resource?ext=acr", { policies: {}, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns true for Read access if that was granted to the given actor", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allow: { read: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: true, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns true for Append access if that was granted to the given actor", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allow: { append: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: true, write: false, controlRead: false, controlWrite: false, }); }); it("returns true for Write access if that was granted to the given actor", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allow: { write: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: true, controlRead: false, controlWrite: false, }); }); it("returns true for ControlRead access if that was granted to the given actor", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: {}, acrPolicies: { "https://some.pod/resource?ext=acr#policy": { allow: { read: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: true, controlWrite: false, }); }); it("returns true for ControlWrite access if that was granted to the given actor", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: {}, acrPolicies: { "https://some.pod/resource?ext=acr#policy": { allow: { write: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: true, }); }); it("returns false for Read access if that was denied for the given actor", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { deny: { read: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for Append access if that was denied for the given actor", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { deny: { append: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for Write access if that was denied for the given actor", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { deny: { write: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for ControlRead access if that was denied for the given actor", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: {}, acrPolicies: { "https://some.pod/resource?ext=acr#policy": { deny: { read: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for ControlWrite access if that was denied for the given actor", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: {}, acrPolicies: { "https://some.pod/resource?ext=acr#policy": { deny: { write: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for Read access if that was granted to the given actor for child Resources only", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: { "https://some.pod/resource?ext=acr#policy": { allow: { read: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for Append access if that was granted to the given actor for child Resources only", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: { "https://some.pod/resource?ext=acr#policy": { allow: { append: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for Write access if that was granted to the given actor for child Resources only", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: { "https://some.pod/resource?ext=acr#policy": { allow: { write: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for ControlRead access if that was granted to the given actor for child Resources only", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: { "https://some.pod/resource?ext=acr#policy": { allow: { read: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for ControlWrite access if that was granted to the given actor for child Resources only", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: { "https://some.pod/resource?ext=acr#policy": { allow: { write: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for Read access if that was denied for the given actor for child Resources only", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: { "https://some.pod/resource?ext=acr#policy": { deny: { read: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for Append access if that was denied for the given actor for child Resources only", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: { "https://some.pod/resource?ext=acr#policy": { deny: { append: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for Write access if that was denied for the given actor for child Resources only", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: { "https://some.pod/resource?ext=acr#policy": { deny: { write: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for ControlRead access if that was denied for the given actor for child Resources only", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: { "https://some.pod/resource?ext=acr#policy": { deny: { read: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns false for ControlWrite access if that was denied for the given actor for child Resources only", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: {}, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: { "https://some.pod/resource?ext=acr#policy": { deny: { write: true }, allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("does not apply a Policy that does not specify any access modes", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("applies a Policy that does not specify any Rules at all", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: true, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("applies a Policy that also specifies empty Rules", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allow: { read: true }, allOf: { "https://some.pod/resource?ext=acr#emptyRule": {}, }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: true, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("applies a Policy that only specifies non-existent Rules", () => { let mockedAcr = mockAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allow: { read: true }, allOf: { "https://some.pod/resource?ext=acr#emptyRule": {}, }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); let policyReferencingNonExistentRules = getPolicy( mockedAcr, "https://some.pod/resource?ext=acr#policy" )!; policyReferencingNonExistentRules = addIri( policyReferencingNonExistentRules, acp.allOf, "https://some.pod/resource?ext=acr#emptyRule" ); mockedAcr = setPolicy(mockedAcr, policyReferencingNonExistentRules); const plainResource = mockSolidDatasetFrom("https://some.pod/resource"); const resourceWithAcr = addPolicyUrl( addMockAcrTo(plainResource, mockedAcr), "https://some.pod/resource?ext=acr#policy" ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: true, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("returns null if some access is defined in separate Resources", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/other-resource?ext=acr#policy": { allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toBeNull(); }); describe("A Policy that references just the given actor in a single Rule", () => { it("applies for an allOf Rule", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: true, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("applies for an anyOf Rule", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { anyOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, allow: { append: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: true, write: false, controlRead: false, controlWrite: false, }); }); it("does not apply for a noneOf Rule", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { noneOf: { "https://some.pod/resource?ext=acr#rule": { [acp.agent]: [webId], }, }, allow: { append: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); }); describe("A Policy that references a Rule that applies to multiple actors, including the given one", () => { it("does apply for an allOf Rule", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allOf: { "https://some.pod/resource?ext=acr#applicable-rule": { [acp.agent]: [webId, "https://some.pod/other-profile#me"], }, }, allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: true, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("does apply for an anyOf Rule", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { anyOf: { "https://some.pod/resource?ext=acr#applicable-rule": { [acp.agent]: [webId, "https://some.pod/other-profile#me"], }, }, allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: true, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("does not apply for a noneOf Rule", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { noneOf: { "https://some.pod/resource?ext=acr#applicable-rule": { [acp.agent]: [webId, "https://some.pod/other-profile#me"], }, }, allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); }); describe("A Policy that references a Rule that does not include the given actor", () => { it("does not apply for an allOf Rule", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allOf: { "https://some.pod/resource?ext=acr#unapplicable-rule": { [acp.group]: ["https://some.pod/groups#group"], }, }, allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("does not apply for an anyOf Rule", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { anyOf: { "https://some.pod/resource?ext=acr#unapplicable-rule": { [acp.group]: ["https://some.pod/groups#group"], }, }, allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("does not apply for a noneOf Rule", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { noneOf: { "https://some.pod/resource?ext=acr#rule": { [acp.group]: ["https://some.pod/groups#group"], }, }, allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); }); describe("A Policy that references multiple of the same type of Rules, not all of which reference the given actor", () => { it("does not apply for allOf Rules", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allOf: { "https://some.pod/resource?ext=acr#applicable-rule": { [acp.agent]: [webId], }, "https://some.pod/resource?ext=acr#unapplicable-rule": { [acp.group]: ["https://some.pod/groups#group"], }, }, allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("does apply for anyOf Rules", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { anyOf: { "https://some.pod/resource?ext=acr#applicable-rule": { [acp.agent]: [webId], }, "https://some.pod/resource?ext=acr#unapplicable-rule": { [acp.group]: ["https://some.pod/groups#group"], }, }, allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: true, append: false, write: false, controlRead: false, controlWrite: false, }); }); it("does not apply for noneOf Rules", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { noneOf: { "https://some.pod/resource?ext=acr#applicable-rule": { [acp.agent]: [webId], }, "https://some.pod/resource?ext=acr#unapplicable-rule": { [acp.group]: ["https://some.pod/groups#group"], }, }, allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: false, append: false, write: false, controlRead: false, controlWrite: false, }); }); }); describe("A Policy that references multiple of the same type of Rules, all of which reference the given actor", () => { it("does apply for allOf Rules", () => { const resourceWithAcr = mockResourceWithAcr( "https://some.pod/resource", "https://some.pod/resource?ext=acr", { policies: { "https://some.pod/resource?ext=acr#policy": { allOf: { "https://some.pod/resource?ext=acr#applicable-rule": { [acp.agent]: [webId], }, "https://some.pod/resource?ext=acr#non-applicable-rule": { [acp.agent]: [webId], }, }, allow: { read: true }, }, }, memberPolicies: {}, acrPolicies: {}, memberAcrPolicies: {}, } ); const access = internal_getActorAccess(resourceWithAcr, acp.agent, webId); expect(access).toStrictEqual({ read: true, append: false, write: f