Skip to content
This repository was archived by the owner on Dec 6, 2023. It is now read-only.

Commit ce74e02

Browse files
Merge pull request #14 from pdsinterop/fix-1339-1340
Fix #1339 and #1340
2 parents 9ecb769 + f2567f5 commit ce74e02

11 files changed

+112
-37
lines changed

src/authorization/AllStaticReader.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { CredentialGroup } from '../authentication/Credentials';
2-
import type { PermissionReaderInput } from './PermissionReader';
2+
import { ResourceIdentifier } from '../http/representation/ResourceIdentifier';
3+
import type { PermissionReaderInput, PermissionReaderOutput } from './PermissionReader';
34
import { PermissionReader } from './PermissionReader';
45
import type { Permission, PermissionSet } from './permissions/Permissions';
56

@@ -21,13 +22,13 @@ export class AllStaticReader extends PermissionReader {
2122
});
2223
}
2324

24-
public async handle({ credentials }: PermissionReaderInput): Promise<PermissionSet> {
25+
public async handle({ credentials }: PermissionReaderInput): Promise<PermissionReaderOutput> {
2526
const result: PermissionSet = {};
2627
for (const [ key, value ] of Object.entries(credentials) as [CredentialGroup, Permission][]) {
2728
if (value) {
2829
result[key] = this.permissions;
2930
}
3031
}
31-
return result;
32+
return { permissions:result };
3233
}
3334
}

src/authorization/Authorizer.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ export interface AuthorizerInput {
2020
* Permissions that are available for the request.
2121
*/
2222
permissionSet: PermissionSet;
23+
/**
24+
* Only perform the check if the resource does not exist
25+
* Used for ancestor creation checks
26+
*/
27+
onlyIfNotExist?: boolean;
2328
}
2429

2530
/**

src/authorization/AuxiliaryReader.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { AuxiliaryStrategy } from '../http/auxiliary/AuxiliaryStrategy';
2+
import { ResourceIdentifier } from '../http/representation/ResourceIdentifier';
23
import { getLoggerFor } from '../logging/LogUtil';
34
import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError';
4-
import type { PermissionReaderInput } from './PermissionReader';
5+
import type { PermissionReaderInput, PermissionReaderOutput } from './PermissionReader';
56
import { PermissionReader } from './PermissionReader';
67
import type { PermissionSet } from './permissions/Permissions';
78

@@ -27,16 +28,16 @@ export class AuxiliaryReader extends PermissionReader {
2728
return this.resourceReader.canHandle(resourceAuth);
2829
}
2930

30-
public async handle(auxiliaryAuth: PermissionReaderInput): Promise<PermissionSet> {
31+
public async handle(auxiliaryAuth: PermissionReaderInput): Promise<PermissionReaderOutput> {
3132
const resourceAuth = this.getRequiredAuthorization(auxiliaryAuth);
3233
this.logger.debug(`Checking auth request for ${auxiliaryAuth.identifier.path} on ${resourceAuth.identifier.path}`);
33-
return this.resourceReader.handle(resourceAuth);
34+
return { permissions: (await this.resourceReader.handle(resourceAuth) as PermissionSet)};
3435
}
3536

36-
public async handleSafe(auxiliaryAuth: PermissionReaderInput): Promise<PermissionSet> {
37+
public async handleSafe(auxiliaryAuth: PermissionReaderInput): Promise<PermissionReaderOutput> {
3738
const resourceAuth = this.getRequiredAuthorization(auxiliaryAuth);
3839
this.logger.debug(`Checking auth request for ${auxiliaryAuth.identifier.path} to ${resourceAuth.identifier.path}`);
39-
return this.resourceReader.handleSafe(resourceAuth);
40+
return { permissions: (await this.resourceReader.handleSafe(resourceAuth) as PermissionSet)};
4041
}
4142

4243
private getRequiredAuthorization(auxiliaryAuth: PermissionReaderInput): PermissionReaderInput {

src/authorization/OwnerPermissionReader.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { CredentialGroup } from '../authentication/Credentials';
22
import type { AuxiliaryIdentifierStrategy } from '../http/auxiliary/AuxiliaryIdentifierStrategy';
3+
import { ResourceIdentifier } from '../http/representation/ResourceIdentifier';
34
import type { AccountSettings, AccountStore } from '../identity/interaction/email-password/storage/AccountStore';
45
import { getLoggerFor } from '../logging/LogUtil';
56
import { createErrorMessage } from '../util/errors/ErrorUtil';
67
import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError';
7-
import type { PermissionReaderInput } from './PermissionReader';
8+
import type { PermissionReaderInput, PermissionReaderOutput } from './PermissionReader';
89
import { PermissionReader } from './PermissionReader';
910
import type { AclPermission } from './permissions/AclPermission';
1011
import type { PermissionSet } from './permissions/Permissions';
@@ -24,23 +25,25 @@ export class OwnerPermissionReader extends PermissionReader {
2425
this.aclStrategy = aclStrategy;
2526
}
2627

27-
public async handle(input: PermissionReaderInput): Promise<PermissionSet> {
28+
public async handle(input: PermissionReaderInput): Promise<PermissionReaderOutput> {
2829
try {
2930
await this.ensurePodOwner(input);
3031
} catch (error: unknown) {
3132
this.logger.debug(`No pod owner Control permissions: ${createErrorMessage(error)}`);
32-
return {};
33+
return { permissions: {}};
3334
}
3435
this.logger.debug(`Granting Control permissions to owner on ${input.identifier.path}`);
3536

36-
return { [CredentialGroup.agent]: {
37-
read: true,
38-
write: true,
39-
append: true,
40-
create: true,
41-
delete: true,
42-
control: true,
43-
} as AclPermission };
37+
return {
38+
permissions: { [CredentialGroup.agent]: {
39+
read: true,
40+
write: true,
41+
append: true,
42+
create: true,
43+
delete: true,
44+
control: true,
45+
} as AclPermission }
46+
};
4447
}
4548

4649
/**

src/authorization/PathBasedReader.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { ResourceIdentifier } from '../http/representation/ResourceIdentifier';
12
import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError';
23
import { ensureTrailingSlash, trimTrailingSlashes } from '../util/PathUtil';
34

4-
import type { PermissionReaderInput } from './PermissionReader';
5+
import type { PermissionReaderInput, PermissionReaderOutput } from './PermissionReader';
56
import { PermissionReader } from './PermissionReader';
67
import type { PermissionSet } from './permissions/Permissions';
78

@@ -30,9 +31,9 @@ export class PathBasedReader extends PermissionReader {
3031
await reader.canHandle(input);
3132
}
3233

33-
public async handle(input: PermissionReaderInput): Promise<PermissionSet> {
34+
public async handle(input: PermissionReaderInput): Promise<PermissionReaderOutput> {
3435
const reader = this.findReader(input.identifier.path);
35-
return reader.handle(input);
36+
return { permissions: (await reader.handle(input) as PermissionSet) };
3637
}
3738

3839
/**

src/authorization/PermissionBasedAuthorizer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ export class PermissionBasedAuthorizer extends Authorizer {
3131
}
3232

3333
public async handle(input: AuthorizerInput): Promise<void> {
34-
const { credentials, modes, identifier, permissionSet } = input;
34+
const { credentials, modes, identifier, permissionSet, onlyIfNotExist } = input;
35+
36+
if (onlyIfNotExist && await this.resourceSet.hasResource(identifier)) {
37+
return
38+
}
3539

3640
const modeString = [ ...modes ].join(',');
3741
this.logger.debug(`Checking if ${credentials.agent?.webId} has ${modeString} permissions for ${identifier.path}`);

src/authorization/PermissionReader.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ export interface PermissionReaderInput {
2020
modes: Set<AccessMode>;
2121
}
2222

23+
export interface PermissionReaderOutput {
24+
permissions: PermissionSet;
25+
ancestors?: ResourceIdentifier[];
26+
}
27+
2328
/**
2429
* Discovers the permissions of the given credentials on the given identifier.
2530
*/
26-
export abstract class PermissionReader extends AsyncHandler<PermissionReaderInput, PermissionSet> {}
31+
export abstract class PermissionReader extends AsyncHandler<PermissionReaderInput, PermissionReaderOutput> {}

src/authorization/UnionPermissionReader.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { CredentialGroup } from '../authentication/Credentials';
22
import { UnionHandler } from '../util/handlers/UnionHandler';
3-
import type { PermissionReader } from './PermissionReader';
3+
import type { PermissionReader, PermissionReaderOutput } from './PermissionReader';
44
import type { Permission, PermissionSet } from './permissions/Permissions';
55

66
/**
@@ -12,14 +12,18 @@ export class UnionPermissionReader extends UnionHandler<PermissionReader> {
1212
super(readers);
1313
}
1414

15-
protected async combine(results: PermissionSet[]): Promise<PermissionSet> {
15+
protected async combine(results: PermissionReaderOutput[]): Promise<PermissionReaderOutput> {
1616
const result: PermissionSet = {};
17+
let ancestors;
1718
for (const permissionSet of results) {
18-
for (const [ key, value ] of Object.entries(permissionSet) as [ CredentialGroup, Permission | undefined ][]) {
19+
for (const [ key, value ] of Object.entries(permissionSet.permissions) as [ CredentialGroup, Permission | undefined ][]) {
1920
result[key] = this.applyPermissions(value, result[key]);
21+
if (permissionSet.ancestors) {
22+
ancestors = permissionSet.ancestors;
23+
}
2024
}
2125
}
22-
return result;
26+
return { permissions: result, ancestors };
2327
}
2428

2529
/**

src/authorization/WebAclReader.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import type { IdentifierStrategy } from '../util/identifiers/IdentifierStrategy'
1414
import { readableToQuads } from '../util/StreamUtil';
1515
import { ACL, RDF } from '../util/Vocabularies';
1616
import type { AccessChecker } from './access/AccessChecker';
17-
import type { PermissionReaderInput } from './PermissionReader';
17+
import type { PermissionReaderInput, PermissionReaderOutput } from './PermissionReader';
1818
import { PermissionReader } from './PermissionReader';
1919
import type { AclPermission } from './permissions/AclPermission';
2020
import { AclMode } from './permissions/AclPermission';
@@ -40,7 +40,7 @@ export class WebAclReader extends PermissionReader {
4040

4141
private readonly aclStrategy: AuxiliaryIdentifierStrategy;
4242
private readonly aclStore: ResourceStore;
43-
private readonly identifierStrategy: IdentifierStrategy;
43+
public readonly identifierStrategy: IdentifierStrategy;
4444
private readonly accessChecker: AccessChecker;
4545

4646
public constructor(aclStrategy: AuxiliaryIdentifierStrategy, aclStore: ResourceStore,
@@ -52,13 +52,26 @@ export class WebAclReader extends PermissionReader {
5252
this.accessChecker = accessChecker;
5353
}
5454

55+
// FIXME: this utility function is unrelated to permission
56+
// so should be moved elsewhere by editing the componentjs config:
57+
private getAncestors(identifier: ResourceIdentifier): ResourceIdentifier[] {
58+
let ancestor = this.identifierStrategy.getParentContainer(identifier);
59+
let ancestors: ResourceIdentifier[] = [];
60+
ancestors.push(ancestor);
61+
while(!this.identifierStrategy.isRootContainer(ancestor)) {
62+
ancestor = this.identifierStrategy.getParentContainer(ancestor);
63+
ancestors.push(ancestor);
64+
}
65+
return ancestors;
66+
}
67+
5568
/**
5669
* Checks if an agent is allowed to execute the requested actions.
5770
* Will throw an error if this is not the case.
5871
* @param input - Relevant data needed to check if access can be granted.
5972
*/
6073
public async handle({ identifier, credentials, modes }: PermissionReaderInput):
61-
Promise<PermissionSet> {
74+
Promise<PermissionReaderOutput> {
6275
// Determine the required access modes
6376
this.logger.debug(`Retrieving permissions of ${credentials.agent?.webId} for ${identifier.path}`);
6477

@@ -95,7 +108,7 @@ export class WebAclReader extends PermissionReader {
95108
permissions[CredentialGroup.public]!.delete =
96109
permissions[CredentialGroup.public]!.write && parentPermissions[CredentialGroup.public]!.write;
97110
}
98-
return permissions;
111+
return { permissions, ancestors: this.getAncestors(identifier) };
99112
}
100113

101114
/**

src/authorization/permissions/N3PatchModesExtractor.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ export class N3PatchModesExtractor extends ModesExtractor {
4545
// When ?insertions is non-empty, servers MUST (also) treat the request as an Append operation.
4646
if (inserts.length > 0) {
4747
accessModes.add(AccessMode.append);
48+
// require Write on c/r and Append-or-Write on c/
49+
// for PATCH-to-create c/r
50+
// ref https://github.com/solid-contrib/test-suite/issues/146
4851
if (!await this.resourceSet.hasResource(target)) {
52+
accessModes.add(AccessMode.write);
4953
accessModes.add(AccessMode.create);
5054
}
5155
}

0 commit comments

Comments
 (0)