mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Always grant control permissions to pod owners
This commit is contained in:
@@ -328,6 +328,43 @@ describe('A Solid server with IDP', (): void => {
|
||||
res = await state.session.fetch(newWebId, patchOptions);
|
||||
expect(res.status).toBe(205);
|
||||
});
|
||||
|
||||
it('always has control over data in the pod.', async(): Promise<void> => {
|
||||
const podBaseUrl = `${baseUrl}${podName}/`;
|
||||
const brokenAcl = '<#authorization> a <http://www.w3.org/ns/auth/acl#Authorization> .';
|
||||
|
||||
// Make the acl file unusable
|
||||
let res = await state.session.fetch(`${podBaseUrl}.acl`, {
|
||||
method: 'PUT',
|
||||
headers: { 'content-type': 'text/turtle' },
|
||||
body: brokenAcl,
|
||||
});
|
||||
expect(res.status).toBe(205);
|
||||
|
||||
// The owner is locked out of their own pod due to a faulty acl file
|
||||
res = await state.session.fetch(podBaseUrl);
|
||||
expect(res.status).toBe(403);
|
||||
|
||||
const fixedAcl = `@prefix acl: <http://www.w3.org/ns/auth/acl#>.
|
||||
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
|
||||
|
||||
<#authorization>
|
||||
a acl:Authorization;
|
||||
acl:agentClass foaf:Agent;
|
||||
acl:mode acl:Read;
|
||||
acl:accessTo <./>.`;
|
||||
// Owner can still update the acl
|
||||
res = await state.session.fetch(`${podBaseUrl}.acl`, {
|
||||
method: 'PUT',
|
||||
headers: { 'content-type': 'text/turtle' },
|
||||
body: fixedAcl,
|
||||
});
|
||||
expect(res.status).toBe(205);
|
||||
|
||||
// Access is possible again
|
||||
res = await state.session.fetch(podBaseUrl);
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setup', (): void => {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"files-scs:config/http/middleware/no-websockets.json",
|
||||
"files-scs:config/http/server-factory/no-websockets.json",
|
||||
"files-scs:config/http/static/default.json",
|
||||
"files-scs:config/identity/handler/default.json",
|
||||
"files-scs:config/ldp/authentication/debug-auth-header.json",
|
||||
"files-scs:config/ldp/authorization/webacl.json",
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
|
||||
84
test/unit/authorization/OwnerPermissionReader.test.ts
Normal file
84
test/unit/authorization/OwnerPermissionReader.test.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type { CredentialSet } from '../../../src/authentication/Credentials';
|
||||
import { CredentialGroup } from '../../../src/authentication/Credentials';
|
||||
import { OwnerPermissionReader } from '../../../src/authorization/OwnerPermissionReader';
|
||||
import type {
|
||||
AccountSettings,
|
||||
AccountStore,
|
||||
} from '../../../src/identity/interaction/email-password/storage/AccountStore';
|
||||
import type { AuxiliaryIdentifierStrategy } from '../../../src/ldp/auxiliary/AuxiliaryIdentifierStrategy';
|
||||
import type { ResourceIdentifier } from '../../../src/ldp/representation/ResourceIdentifier';
|
||||
|
||||
describe('An OwnerPermissionReader', (): void => {
|
||||
const owner = 'http://test.com/alice/profile/card#me';
|
||||
const podBaseUrl = 'http://test.com/alice/';
|
||||
let credentials: CredentialSet;
|
||||
let identifier: ResourceIdentifier;
|
||||
let settings: AccountSettings;
|
||||
let accountStore: jest.Mocked<AccountStore>;
|
||||
let aclStrategy: jest.Mocked<AuxiliaryIdentifierStrategy>;
|
||||
let reader: OwnerPermissionReader;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
credentials = { [CredentialGroup.agent]: { webId: owner }};
|
||||
|
||||
identifier = { path: `${podBaseUrl}.acl` };
|
||||
|
||||
settings = {
|
||||
useIdp: true,
|
||||
podBaseUrl,
|
||||
};
|
||||
|
||||
accountStore = {
|
||||
getSettings: jest.fn(async(webId: string): Promise<AccountSettings> => {
|
||||
if (webId === owner) {
|
||||
return settings;
|
||||
}
|
||||
throw new Error('No account');
|
||||
}),
|
||||
} as any;
|
||||
|
||||
aclStrategy = {
|
||||
isAuxiliaryIdentifier: jest.fn((id): boolean => id.path.endsWith('.acl')),
|
||||
} as any;
|
||||
|
||||
reader = new OwnerPermissionReader(accountStore, aclStrategy);
|
||||
});
|
||||
|
||||
it('returns empty permissions for non-ACL resources.', async(): Promise<void> => {
|
||||
identifier.path = podBaseUrl;
|
||||
await expect(reader.handle({ credentials, identifier })).resolves.toEqual({});
|
||||
});
|
||||
|
||||
it('returns empty permissions if there is no agent WebID.', async(): Promise<void> => {
|
||||
credentials = {};
|
||||
await expect(reader.handle({ credentials, identifier })).resolves.toEqual({});
|
||||
});
|
||||
|
||||
it('returns empty permissions if the agent has no account.', async(): Promise<void> => {
|
||||
credentials.agent!.webId = 'http://test.com/someone/else';
|
||||
await expect(reader.handle({ credentials, identifier })).resolves.toEqual({});
|
||||
});
|
||||
|
||||
it('returns empty permissions if the account has no pod.', async(): Promise<void> => {
|
||||
delete settings.podBaseUrl;
|
||||
await expect(reader.handle({ credentials, identifier })).resolves.toEqual({});
|
||||
});
|
||||
|
||||
it('returns empty permissions if the target identifier is not in the pod.', async(): Promise<void> => {
|
||||
identifier.path = 'http://somewhere.else/.acl';
|
||||
await expect(reader.handle({ credentials, identifier })).resolves.toEqual({});
|
||||
});
|
||||
|
||||
it('returns full permissions if the owner is accessing an ACL resource in their pod.', async(): Promise<void> => {
|
||||
await expect(reader.handle({ credentials, identifier })).resolves.toEqual({
|
||||
[CredentialGroup.agent]: {
|
||||
read: true,
|
||||
write: true,
|
||||
append: true,
|
||||
create: true,
|
||||
delete: true,
|
||||
control: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -200,7 +200,7 @@ describe('A RegistrationManager', (): void => {
|
||||
expect(podManager.createPod).toHaveBeenCalledTimes(1);
|
||||
expect(podManager.createPod).toHaveBeenLastCalledWith({ path: `${baseUrl}${podName}/` }, podSettings, false);
|
||||
expect(accountStore.create).toHaveBeenCalledTimes(1);
|
||||
expect(accountStore.create).toHaveBeenLastCalledWith(email, webId, password, { useIdp: false });
|
||||
expect(accountStore.create).toHaveBeenLastCalledWith(email, webId, password, { useIdp: false, podBaseUrl });
|
||||
expect(accountStore.verify).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(accountStore.deleteAccount).toHaveBeenCalledTimes(0);
|
||||
@@ -222,7 +222,7 @@ describe('A RegistrationManager', (): void => {
|
||||
expect(ownershipValidator.handleSafe).toHaveBeenCalledTimes(1);
|
||||
expect(ownershipValidator.handleSafe).toHaveBeenLastCalledWith({ webId });
|
||||
expect(accountStore.create).toHaveBeenCalledTimes(1);
|
||||
expect(accountStore.create).toHaveBeenLastCalledWith(email, webId, password, { useIdp: true });
|
||||
expect(accountStore.create).toHaveBeenLastCalledWith(email, webId, password, { useIdp: true, podBaseUrl });
|
||||
expect(identifierGenerator.generate).toHaveBeenCalledTimes(1);
|
||||
expect(identifierGenerator.generate).toHaveBeenLastCalledWith(podName);
|
||||
expect(podManager.createPod).toHaveBeenCalledTimes(1);
|
||||
@@ -242,7 +242,7 @@ describe('A RegistrationManager', (): void => {
|
||||
expect(ownershipValidator.handleSafe).toHaveBeenCalledTimes(1);
|
||||
expect(ownershipValidator.handleSafe).toHaveBeenLastCalledWith({ webId });
|
||||
expect(accountStore.create).toHaveBeenCalledTimes(1);
|
||||
expect(accountStore.create).toHaveBeenLastCalledWith(email, webId, password, { useIdp: true });
|
||||
expect(accountStore.create).toHaveBeenLastCalledWith(email, webId, password, { useIdp: true, podBaseUrl });
|
||||
expect(identifierGenerator.generate).toHaveBeenCalledTimes(1);
|
||||
expect(identifierGenerator.generate).toHaveBeenLastCalledWith(podName);
|
||||
expect(podManager.createPod).toHaveBeenCalledTimes(1);
|
||||
@@ -272,7 +272,10 @@ describe('A RegistrationManager', (): void => {
|
||||
expect(identifierGenerator.generate).toHaveBeenCalledTimes(1);
|
||||
expect(identifierGenerator.generate).toHaveBeenLastCalledWith(podName);
|
||||
expect(accountStore.create).toHaveBeenCalledTimes(1);
|
||||
expect(accountStore.create).toHaveBeenLastCalledWith(email, generatedWebID, password, { useIdp: true });
|
||||
expect(accountStore.create).toHaveBeenLastCalledWith(email,
|
||||
generatedWebID,
|
||||
password,
|
||||
{ useIdp: true, podBaseUrl });
|
||||
expect(accountStore.verify).toHaveBeenCalledTimes(1);
|
||||
expect(accountStore.verify).toHaveBeenLastCalledWith(email);
|
||||
expect(podManager.createPod).toHaveBeenCalledTimes(1);
|
||||
@@ -300,7 +303,10 @@ describe('A RegistrationManager', (): void => {
|
||||
expect(podManager.createPod).toHaveBeenCalledTimes(1);
|
||||
expect(podManager.createPod).toHaveBeenLastCalledWith({ path: baseUrl }, podSettings, true);
|
||||
expect(accountStore.create).toHaveBeenCalledTimes(1);
|
||||
expect(accountStore.create).toHaveBeenLastCalledWith(email, webId, password, { useIdp: false });
|
||||
expect(accountStore.create).toHaveBeenLastCalledWith(email,
|
||||
webId,
|
||||
password,
|
||||
{ useIdp: false, podBaseUrl: baseUrl });
|
||||
expect(accountStore.verify).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(identifierGenerator.generate).toHaveBeenCalledTimes(0);
|
||||
|
||||
Reference in New Issue
Block a user