mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Replace acl specific permissions with generic permissions
This required AuxiliaryStrategy to have a new function indicating if the auxiliary resource just used its associated resource authorization or its own.
This commit is contained in:
parent
5104cd56e8
commit
7f8b923399
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/memory.json",
|
||||
"files-scs:config/storage/key-value/resource-store.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/dynamic.json",
|
||||
"files-scs:config/storage/key-value/resource-store.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/file.json",
|
||||
"files-scs:config/storage/key-value/resource-store.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/file.json",
|
||||
"files-scs:config/storage/key-value/resource-store.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/file.json",
|
||||
"files-scs:config/storage/key-value/resource-store.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -30,5 +30,4 @@ Contains a list of metadata writers that will be run on outgoing responses.
|
||||
## Modes
|
||||
Determines which modes are needed for requests,
|
||||
by default this is based on the used HTTP method.
|
||||
* *acl*: The default setup with specific support for accessing .acl documents.
|
||||
* *no-acl*: Same as above but interprets .acl documents as any other document.
|
||||
* *default*: Bases required modes on HTTP method.
|
||||
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
|
||||
"@graph": [
|
||||
{
|
||||
"comment": "Makes sure acl files require control permissions.",
|
||||
"@id": "urn:solid-server:default:ModesExtractor",
|
||||
"@type": "WaterfallHandler",
|
||||
"handlers": [
|
||||
{
|
||||
"@type": "AclModesExtractor",
|
||||
"aclStrategy": { "@id": "urn:solid-server:default:AclStrategy" }
|
||||
},
|
||||
{ "@type": "MethodModesExtractor" },
|
||||
{ "@type": "SparqlPatchModesExtractor" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^1.0.0/components/context.jsonld",
|
||||
"@graph": [
|
||||
{
|
||||
"comment": "Extracts the required permissions based on the HTTP method.",
|
||||
"comment": "Determines required modes based on HTTP methods.",
|
||||
"@id": "urn:solid-server:default:ModesExtractor",
|
||||
"@type": "WaterfallHandler",
|
||||
"handlers": [
|
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/memory.json",
|
||||
"files-scs:config/storage/key-value/resource-store.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/regex.json",
|
||||
"files-scs:config/storage/key-value/memory.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/sparql.json",
|
||||
"files-scs:config/storage/key-value/memory.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/sparql.json",
|
||||
"files-scs:config/storage/key-value/memory.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -15,6 +15,7 @@
|
||||
"@type": "RdfValidator",
|
||||
"converter": { "@id": "urn:solid-server:default:RepresentationConverter" }
|
||||
},
|
||||
"ownAuthorization": true,
|
||||
"requiredInRoot": true
|
||||
},
|
||||
{
|
||||
|
@ -16,7 +16,8 @@ export class AllStaticReader extends PermissionReader {
|
||||
read: allow,
|
||||
write: allow,
|
||||
append: allow,
|
||||
control: allow,
|
||||
create: allow,
|
||||
delete: allow,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { AuxiliaryIdentifierStrategy } from '../ldp/auxiliary/AuxiliaryIdentifierStrategy';
|
||||
import type { AuxiliaryStrategy } from '../ldp/auxiliary/AuxiliaryStrategy';
|
||||
import type { PermissionSet } from '../ldp/permissions/Permissions';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError';
|
||||
@ -15,9 +15,9 @@ export class AuxiliaryReader extends PermissionReader {
|
||||
protected readonly logger = getLoggerFor(this);
|
||||
|
||||
private readonly resourceReader: PermissionReader;
|
||||
private readonly auxiliaryStrategy: AuxiliaryIdentifierStrategy;
|
||||
private readonly auxiliaryStrategy: AuxiliaryStrategy;
|
||||
|
||||
public constructor(resourceReader: PermissionReader, auxiliaryStrategy: AuxiliaryIdentifierStrategy) {
|
||||
public constructor(resourceReader: PermissionReader, auxiliaryStrategy: AuxiliaryStrategy) {
|
||||
super();
|
||||
this.resourceReader = resourceReader;
|
||||
this.auxiliaryStrategy = auxiliaryStrategy;
|
||||
@ -44,6 +44,11 @@ export class AuxiliaryReader extends PermissionReader {
|
||||
if (!this.auxiliaryStrategy.isAuxiliaryIdentifier(auxiliaryAuth.identifier)) {
|
||||
throw new NotImplementedHttpError('AuxiliaryAuthorizer only supports auxiliary resources.');
|
||||
}
|
||||
|
||||
if (this.auxiliaryStrategy.usesOwnAuthorization(auxiliaryAuth.identifier)) {
|
||||
throw new NotImplementedHttpError('Auxiliary resource uses its own permissions.');
|
||||
}
|
||||
|
||||
return {
|
||||
...auxiliaryAuth,
|
||||
identifier: this.auxiliaryStrategy.getAssociatedIdentifier(auxiliaryAuth.identifier),
|
||||
|
@ -3,7 +3,9 @@ import { Store } from 'n3';
|
||||
import { CredentialGroup } from '../authentication/Credentials';
|
||||
import type { Credential, CredentialSet } from '../authentication/Credentials';
|
||||
import type { AuxiliaryIdentifierStrategy } from '../ldp/auxiliary/AuxiliaryIdentifierStrategy';
|
||||
import type { Permission, PermissionSet } from '../ldp/permissions/Permissions';
|
||||
import { AclMode } from '../ldp/permissions/AclPermission';
|
||||
import type { AclPermission } from '../ldp/permissions/AclPermission';
|
||||
import type { PermissionSet } from '../ldp/permissions/Permissions';
|
||||
import { AccessMode } from '../ldp/permissions/Permissions';
|
||||
import type { Representation } from '../ldp/representation/Representation';
|
||||
import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
|
||||
@ -14,7 +16,6 @@ import { createErrorMessage } from '../util/errors/ErrorUtil';
|
||||
import { ForbiddenHttpError } from '../util/errors/ForbiddenHttpError';
|
||||
import { InternalServerError } from '../util/errors/InternalServerError';
|
||||
import { NotFoundHttpError } from '../util/errors/NotFoundHttpError';
|
||||
import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError';
|
||||
import type { IdentifierStrategy } from '../util/identifiers/IdentifierStrategy';
|
||||
import { readableToQuads } from '../util/StreamUtil';
|
||||
import { ACL, RDF } from '../util/Vocabularies';
|
||||
@ -22,11 +23,11 @@ import type { AccessChecker } from './access-checkers/AccessChecker';
|
||||
import type { PermissionReaderInput } from './PermissionReader';
|
||||
import { PermissionReader } from './PermissionReader';
|
||||
|
||||
const modesMap: Record<string, AccessMode> = {
|
||||
const modesMap: Record<string, keyof AclPermission> = {
|
||||
[ACL.Read]: AccessMode.read,
|
||||
[ACL.Write]: AccessMode.write,
|
||||
[ACL.Append]: AccessMode.append,
|
||||
[ACL.Control]: AccessMode.control,
|
||||
[ACL.Control]: AclMode.control,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
@ -50,12 +51,6 @@ export class WebAclReader extends PermissionReader {
|
||||
this.accessChecker = accessChecker;
|
||||
}
|
||||
|
||||
public async canHandle({ identifier }: PermissionReaderInput): Promise<void> {
|
||||
if (this.aclStrategy.isAuxiliaryIdentifier(identifier)) {
|
||||
throw new NotImplementedHttpError('WebAclAuthorizer does not support permissions on auxiliary resources.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an agent is allowed to execute the requested actions.
|
||||
* Will throw an error if this is not the case.
|
||||
@ -66,24 +61,28 @@ export class WebAclReader extends PermissionReader {
|
||||
// Determine the required access modes
|
||||
this.logger.debug(`Retrieving permissions of ${credentials.agent?.webId} for ${identifier.path}`);
|
||||
|
||||
const isAcl = this.aclStrategy.isAuxiliaryIdentifier(identifier);
|
||||
const mainIdentifier = isAcl ? this.aclStrategy.getAssociatedIdentifier(identifier) : identifier;
|
||||
|
||||
// Determine the full authorization for the agent granted by the applicable ACL
|
||||
const acl = await this.getAclRecursive(identifier);
|
||||
return this.createPermissions(credentials, acl);
|
||||
const acl = await this.getAclRecursive(mainIdentifier);
|
||||
return this.createPermissions(credentials, acl, isAcl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Authorization object based on the quads found in the ACL.
|
||||
* @param credentials - Credentials to check permissions for.
|
||||
* @param acl - Store containing all relevant authorization triples.
|
||||
* @param isAcl - If the target resource is an acl document.
|
||||
*/
|
||||
private async createPermissions(credentials: CredentialSet, acl: Store):
|
||||
private async createPermissions(credentials: CredentialSet, acl: Store, isAcl: boolean):
|
||||
Promise<PermissionSet> {
|
||||
const publicPermissions = await this.determinePermissions(acl, credentials.public);
|
||||
const agentPermissions = await this.determinePermissions(acl, credentials.agent);
|
||||
|
||||
return {
|
||||
[CredentialGroup.agent]: agentPermissions,
|
||||
[CredentialGroup.public]: publicPermissions,
|
||||
[CredentialGroup.agent]: this.updateAclPermissions(agentPermissions, isAcl),
|
||||
[CredentialGroup.public]: this.updateAclPermissions(publicPermissions, isAcl),
|
||||
};
|
||||
}
|
||||
|
||||
@ -93,10 +92,10 @@ export class WebAclReader extends PermissionReader {
|
||||
* @param acl - Store containing all relevant authorization triples.
|
||||
* @param credentials - Credentials to find the permissions for.
|
||||
*/
|
||||
private async determinePermissions(acl: Store, credentials?: Credential): Promise<Permission> {
|
||||
const permissions: Permission = {};
|
||||
private async determinePermissions(acl: Store, credentials?: Credential): Promise<AclPermission> {
|
||||
const aclPermissions: AclPermission = {};
|
||||
if (!credentials) {
|
||||
return permissions;
|
||||
return aclPermissions;
|
||||
}
|
||||
|
||||
// Apply all ACL rules
|
||||
@ -108,18 +107,43 @@ export class WebAclReader extends PermissionReader {
|
||||
const modes = acl.getObjects(rule, ACL.mode, null);
|
||||
for (const { value: mode } of modes) {
|
||||
if (mode in modesMap) {
|
||||
permissions[modesMap[mode]] = true;
|
||||
aclPermissions[modesMap[mode]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (permissions.write) {
|
||||
if (aclPermissions.write) {
|
||||
// Write permission implies Append permission
|
||||
permissions.append = true;
|
||||
aclPermissions.append = true;
|
||||
}
|
||||
|
||||
return permissions;
|
||||
return aclPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the correct values for non-acl permissions such as create and delete.
|
||||
* Also adds the correct values to indicate that having control permission
|
||||
* implies having read/write/etc. on the acl resource.
|
||||
*
|
||||
* The main reason for keeping the control value is so we can correctly set the WAC-Allow header later.
|
||||
*/
|
||||
private updateAclPermissions(aclPermissions: AclPermission, isAcl: boolean): AclPermission {
|
||||
if (isAcl) {
|
||||
return {
|
||||
read: aclPermissions.control,
|
||||
append: aclPermissions.control,
|
||||
write: aclPermissions.control,
|
||||
create: aclPermissions.control,
|
||||
delete: aclPermissions.control,
|
||||
control: aclPermissions.control,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...aclPermissions,
|
||||
create: aclPermissions.write,
|
||||
delete: aclPermissions.write,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -154,7 +154,6 @@ export * from './ldp/operations/PostOperationHandler';
|
||||
export * from './ldp/operations/PutOperationHandler';
|
||||
|
||||
// LDP/Permissions
|
||||
export * from './ldp/permissions/AclModesExtractor';
|
||||
export * from './ldp/permissions/Permissions';
|
||||
export * from './ldp/permissions/ModesExtractor';
|
||||
export * from './ldp/permissions/MethodModesExtractor';
|
||||
|
@ -9,6 +9,12 @@ import type { AuxiliaryIdentifierStrategy } from './AuxiliaryIdentifierStrategy'
|
||||
* supported by this strategy.
|
||||
*/
|
||||
export interface AuxiliaryStrategy extends AuxiliaryIdentifierStrategy {
|
||||
/**
|
||||
* Whether this auxiliary resources uses its own authorization instead of the associated resource authorization.
|
||||
* @param identifier - Identifier of the auxiliary resource.
|
||||
*/
|
||||
usesOwnAuthorization: (identifier: ResourceIdentifier) => boolean;
|
||||
|
||||
/**
|
||||
* Whether the root storage container requires this auxiliary resource to be present.
|
||||
* If yes, this means they can't be deleted individually from such a container.
|
||||
|
@ -14,13 +14,15 @@ export class ComposedAuxiliaryStrategy implements AuxiliaryStrategy {
|
||||
private readonly identifierStrategy: AuxiliaryIdentifierStrategy;
|
||||
private readonly metadataGenerator?: MetadataGenerator;
|
||||
private readonly validator?: Validator;
|
||||
private readonly ownAuthorization: boolean;
|
||||
private readonly requiredInRoot: boolean;
|
||||
|
||||
public constructor(identifierStrategy: AuxiliaryIdentifierStrategy, metadataGenerator?: MetadataGenerator,
|
||||
validator?: Validator, requiredInRoot = false) {
|
||||
validator?: Validator, ownAuthorization = false, requiredInRoot = false) {
|
||||
this.identifierStrategy = identifierStrategy;
|
||||
this.metadataGenerator = metadataGenerator;
|
||||
this.validator = validator;
|
||||
this.ownAuthorization = ownAuthorization;
|
||||
this.requiredInRoot = requiredInRoot;
|
||||
}
|
||||
|
||||
@ -40,6 +42,10 @@ export class ComposedAuxiliaryStrategy implements AuxiliaryStrategy {
|
||||
return this.identifierStrategy.getAssociatedIdentifier(identifier);
|
||||
}
|
||||
|
||||
public usesOwnAuthorization(): boolean {
|
||||
return this.ownAuthorization;
|
||||
}
|
||||
|
||||
public isRequiredInRoot(): boolean {
|
||||
return this.requiredInRoot;
|
||||
}
|
||||
|
@ -18,6 +18,11 @@ export class RoutingAuxiliaryStrategy extends RoutingAuxiliaryIdentifierStrategy
|
||||
super(sources);
|
||||
}
|
||||
|
||||
public usesOwnAuthorization(identifier: ResourceIdentifier): boolean {
|
||||
const source = this.getMatchingSource(identifier);
|
||||
return source.usesOwnAuthorization(identifier);
|
||||
}
|
||||
|
||||
public isRequiredInRoot(identifier: ResourceIdentifier): boolean {
|
||||
const source = this.getMatchingSource(identifier);
|
||||
return source.isRequiredInRoot(identifier);
|
||||
|
@ -1,10 +1,13 @@
|
||||
import { ACL, AUTH } from '../../../util/Vocabularies';
|
||||
import type { AccessMode } from '../../permissions/Permissions';
|
||||
import { AclMode } from '../../permissions/AclPermission';
|
||||
import type { AclPermission } from '../../permissions/AclPermission';
|
||||
import { AccessMode } from '../../permissions/Permissions';
|
||||
|
||||
import type { OperationMetadataCollectorInput } from './OperationMetadataCollector';
|
||||
import { OperationMetadataCollector } from './OperationMetadataCollector';
|
||||
|
||||
const VALID_METHODS = new Set([ 'HEAD', 'GET' ]);
|
||||
const VALID_ACL_MODES = new Set([ AccessMode.read, AccessMode.write, AccessMode.append, AclMode.control ]);
|
||||
|
||||
/**
|
||||
* Indicates which acl permissions are available on the requested resource.
|
||||
@ -15,18 +18,20 @@ export class WebAclMetadataCollector extends OperationMetadataCollector {
|
||||
if (!operation.permissionSet || !VALID_METHODS.has(operation.method)) {
|
||||
return;
|
||||
}
|
||||
const user = operation.permissionSet.agent ?? {};
|
||||
const everyone = operation.permissionSet.public ?? {};
|
||||
const user: AclPermission = operation.permissionSet.agent ?? {};
|
||||
const everyone: AclPermission = operation.permissionSet.public ?? {};
|
||||
|
||||
const modes = new Set<AccessMode>([ ...Object.keys(user), ...Object.keys(everyone) ] as AccessMode[]);
|
||||
|
||||
for (const mode of modes) {
|
||||
const capitalizedMode = mode.charAt(0).toUpperCase() + mode.slice(1) as 'Read' | 'Write' | 'Append' | 'Control';
|
||||
if (everyone[mode]) {
|
||||
metadata.add(AUTH.terms.publicMode, ACL.terms[capitalizedMode]);
|
||||
}
|
||||
if (user[mode]) {
|
||||
metadata.add(AUTH.terms.userMode, ACL.terms[capitalizedMode]);
|
||||
if (VALID_ACL_MODES.has(mode)) {
|
||||
const capitalizedMode = mode.charAt(0).toUpperCase() + mode.slice(1) as 'Read' | 'Write' | 'Append' | 'Control';
|
||||
if (everyone[mode]) {
|
||||
metadata.add(AUTH.terms.publicMode, ACL.terms[capitalizedMode]);
|
||||
}
|
||||
if (user[mode]) {
|
||||
metadata.add(AUTH.terms.userMode, ACL.terms[capitalizedMode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
|
||||
import type { AuxiliaryIdentifierStrategy } from '../auxiliary/AuxiliaryIdentifierStrategy';
|
||||
import type { Operation } from '../operations/Operation';
|
||||
import { ModesExtractor } from './ModesExtractor';
|
||||
import { AccessMode } from './Permissions';
|
||||
|
||||
export class AclModesExtractor extends ModesExtractor {
|
||||
private readonly aclStrategy: AuxiliaryIdentifierStrategy;
|
||||
|
||||
public constructor(aclStrategy: AuxiliaryIdentifierStrategy) {
|
||||
super();
|
||||
this.aclStrategy = aclStrategy;
|
||||
}
|
||||
|
||||
public async canHandle({ target }: Operation): Promise<void> {
|
||||
if (!this.aclStrategy.isAuxiliaryIdentifier(target)) {
|
||||
throw new NotImplementedHttpError('Can only determine permissions of acl resources');
|
||||
}
|
||||
}
|
||||
|
||||
public async handle(): Promise<Set<AccessMode>> {
|
||||
return new Set([ AccessMode.control ]);
|
||||
}
|
||||
}
|
10
src/ldp/permissions/AclPermission.ts
Normal file
10
src/ldp/permissions/AclPermission.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import type { Permission } from './Permissions';
|
||||
|
||||
export enum AclMode {
|
||||
control = 'control',
|
||||
}
|
||||
|
||||
// Adds a control field to the permissions to specify this WAC-specific value
|
||||
export type AclPermission = Permission & {
|
||||
[mode in AclMode]?: boolean;
|
||||
};
|
@ -27,6 +27,8 @@ export class MethodModesExtractor extends ModesExtractor {
|
||||
if (WRITE_METHODS.has(method)) {
|
||||
result.add(AccessMode.write);
|
||||
result.add(AccessMode.append);
|
||||
result.add(AccessMode.create);
|
||||
result.add(AccessMode.delete);
|
||||
} else if (APPEND_METHODS.has(method)) {
|
||||
result.add(AccessMode.append);
|
||||
}
|
||||
|
@ -7,7 +7,8 @@ export enum AccessMode {
|
||||
read = 'read',
|
||||
append = 'append',
|
||||
write = 'write',
|
||||
control = 'control',
|
||||
create = 'create',
|
||||
delete = 'delete',
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,6 +31,8 @@ export class SparqlPatchModesExtractor extends ModesExtractor {
|
||||
if (this.needsWrite(update)) {
|
||||
result.add(AccessMode.write);
|
||||
result.add(AccessMode.append);
|
||||
result.add(AccessMode.create);
|
||||
result.add(AccessMode.delete);
|
||||
} else if (this.needsAppend(update)) {
|
||||
result.add(AccessMode.append);
|
||||
}
|
||||
|
@ -177,7 +177,8 @@ describe.each(stores)('An LDP handler with auth using %s', (name, { storeConfig,
|
||||
|
||||
const response = await fetch(`${baseUrl}.acl`);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('wac-allow')).toBe('user="control",public="control"');
|
||||
expect(response.headers.get('wac-allow'))
|
||||
.toBe('user="append control read write",public="append control read write"');
|
||||
|
||||
// Close response
|
||||
await response.text();
|
||||
|
@ -13,7 +13,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/key-value/memory.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
"files-scs:config/util/auxiliary/acl.json",
|
||||
|
@ -13,7 +13,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/memory.json",
|
||||
"files-scs:config/storage/key-value/memory.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/dynamic.json",
|
||||
"files-scs:config/storage/key-value/memory.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -17,7 +17,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/memory.json",
|
||||
"files-scs:config/storage/key-value/resource-store.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/key-value/memory.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
"files-scs:config/util/auxiliary/acl.json",
|
||||
|
@ -13,7 +13,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/memory.json",
|
||||
"files-scs:config/storage/key-value/memory.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -18,7 +18,7 @@
|
||||
"files-scs:config/ldp/handler/default.json",
|
||||
"files-scs:config/ldp/metadata-parser/default.json",
|
||||
"files-scs:config/ldp/metadata-writer/default.json",
|
||||
"files-scs:config/ldp/modes/acl.json",
|
||||
"files-scs:config/ldp/modes/default.json",
|
||||
"files-scs:config/storage/backend/memory.json",
|
||||
"files-scs:config/storage/key-value/resource-store.json",
|
||||
"files-scs:config/storage/middleware/default.json",
|
||||
|
@ -7,7 +7,8 @@ function getPermissions(allow: boolean): Permission {
|
||||
read: allow,
|
||||
write: allow,
|
||||
append: allow,
|
||||
control: allow,
|
||||
create: allow,
|
||||
delete: allow,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { CredentialGroup } from '../../../src/authentication/Credentials';
|
||||
import { AuxiliaryReader } from '../../../src/authorization/AuxiliaryReader';
|
||||
import type { PermissionReader } from '../../../src/authorization/PermissionReader';
|
||||
import type { AuxiliaryIdentifierStrategy } from '../../../src/ldp/auxiliary/AuxiliaryIdentifierStrategy';
|
||||
import type { AuxiliaryStrategy } from '../../../src/ldp/auxiliary/AuxiliaryStrategy';
|
||||
import type { PermissionSet } from '../../../src/ldp/permissions/Permissions';
|
||||
import type { ResourceIdentifier } from '../../../src/ldp/representation/ResourceIdentifier';
|
||||
import { NotImplementedHttpError } from '../../../src/util/errors/NotImplementedHttpError';
|
||||
@ -12,8 +12,8 @@ describe('An AuxiliaryReader', (): void => {
|
||||
const associatedIdentifier = { path: 'http://test.com/foo' };
|
||||
const auxiliaryIdentifier = { path: 'http://test.com/foo.dummy' };
|
||||
const permissionSet: PermissionSet = { [CredentialGroup.agent]: { read: true }};
|
||||
let source: PermissionReader;
|
||||
let strategy: AuxiliaryIdentifierStrategy;
|
||||
let source: jest.Mocked<PermissionReader>;
|
||||
let strategy: jest.Mocked<AuxiliaryStrategy>;
|
||||
let reader: AuxiliaryReader;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
@ -27,6 +27,7 @@ describe('An AuxiliaryReader', (): void => {
|
||||
isAuxiliaryIdentifier: jest.fn((identifier: ResourceIdentifier): boolean => identifier.path.endsWith(suffix)),
|
||||
getAssociatedIdentifier: jest.fn((identifier: ResourceIdentifier): ResourceIdentifier =>
|
||||
({ path: identifier.path.slice(0, -suffix.length) })),
|
||||
usesOwnAuthorization: jest.fn().mockReturnValue(false),
|
||||
} as any;
|
||||
reader = new AuxiliaryReader(source, strategy);
|
||||
});
|
||||
@ -39,7 +40,12 @@ describe('An AuxiliaryReader', (): void => {
|
||||
);
|
||||
await expect(reader.canHandle({ identifier: associatedIdentifier, credentials }))
|
||||
.rejects.toThrow(NotImplementedHttpError);
|
||||
source.canHandle = jest.fn().mockRejectedValue(new Error('no source support'));
|
||||
|
||||
strategy.usesOwnAuthorization.mockReturnValueOnce(true);
|
||||
await expect(reader.canHandle({ identifier: auxiliaryIdentifier, credentials }))
|
||||
.rejects.toThrow(NotImplementedHttpError);
|
||||
|
||||
source.canHandle.mockRejectedValue(new Error('no source support'));
|
||||
await expect(reader.canHandle({ identifier: auxiliaryIdentifier, credentials }))
|
||||
.rejects.toThrow('no source support');
|
||||
});
|
||||
@ -61,9 +67,15 @@ describe('An AuxiliaryReader', (): void => {
|
||||
expect(source.handleSafe).toHaveBeenLastCalledWith(
|
||||
{ identifier: associatedIdentifier, credentials },
|
||||
);
|
||||
|
||||
await expect(reader.handleSafe({ identifier: associatedIdentifier, credentials }))
|
||||
.rejects.toThrow(NotImplementedHttpError);
|
||||
source.handleSafe = jest.fn().mockRejectedValue(new Error('no source support'));
|
||||
|
||||
strategy.usesOwnAuthorization.mockReturnValueOnce(true);
|
||||
await expect(reader.canHandle({ identifier: auxiliaryIdentifier, credentials }))
|
||||
.rejects.toThrow(NotImplementedHttpError);
|
||||
|
||||
source.handleSafe.mockRejectedValue(new Error('no source support'));
|
||||
await expect(reader.handleSafe({ identifier: auxiliaryIdentifier, credentials }))
|
||||
.rejects.toThrow('no source support');
|
||||
});
|
||||
|
@ -44,13 +44,13 @@ describe('A UnionPermissionReader', (): void => {
|
||||
|
||||
it('merges same fields using false > true > undefined.', async(): Promise<void> => {
|
||||
readers[0].handle.mockResolvedValue(
|
||||
{ [CredentialGroup.agent]: { read: true, write: false, append: undefined, control: true }},
|
||||
{ [CredentialGroup.agent]: { read: true, write: false, append: undefined, create: true, delete: undefined }},
|
||||
);
|
||||
readers[1].handle.mockResolvedValue(
|
||||
{ [CredentialGroup.agent]: { read: false, write: true, append: true, control: true }},
|
||||
{ [CredentialGroup.agent]: { read: false, write: true, append: true, create: true, delete: undefined }},
|
||||
);
|
||||
await expect(unionReader.handle(input)).resolves.toEqual({
|
||||
[CredentialGroup.agent]: { read: false, write: false, append: true, control: true },
|
||||
[CredentialGroup.agent]: { read: false, write: false, append: true, create: true },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -12,7 +12,6 @@ import { INTERNAL_QUADS } from '../../../src/util/ContentTypes';
|
||||
import { ForbiddenHttpError } from '../../../src/util/errors/ForbiddenHttpError';
|
||||
import { InternalServerError } from '../../../src/util/errors/InternalServerError';
|
||||
import { NotFoundHttpError } from '../../../src/util/errors/NotFoundHttpError';
|
||||
import { NotImplementedHttpError } from '../../../src/util/errors/NotImplementedHttpError';
|
||||
import { SingleRootIdentifierStrategy } from '../../../src/util/identifiers/SingleRootIdentifierStrategy';
|
||||
import { guardedStreamFrom } from '../../../src/util/StreamUtil';
|
||||
|
||||
@ -51,10 +50,8 @@ describe('A WebAclReader', (): void => {
|
||||
reader = new WebAclReader(aclStrategy, store, identifierStrategy, accessChecker);
|
||||
});
|
||||
|
||||
it('handles all non-acl inputs.', async(): Promise<void> => {
|
||||
await expect(reader.canHandle({ identifier, credentials })).resolves.toBeUndefined();
|
||||
await expect(reader.canHandle({ identifier: aclStrategy.getAuxiliaryIdentifier(identifier) } as any))
|
||||
.rejects.toThrow(NotImplementedHttpError);
|
||||
it('handles all input.', async(): Promise<void> => {
|
||||
await expect(reader.canHandle({ } as any)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns undefined permissions for undefined credentials.', async(): Promise<void> => {
|
||||
@ -137,15 +134,39 @@ describe('A WebAclReader', (): void => {
|
||||
await expect(promise).rejects.toThrow(ForbiddenHttpError);
|
||||
});
|
||||
|
||||
it('allows an agent to append if they have write access.', async(): Promise<void> => {
|
||||
it('allows an agent to append/create/delete if they have write access.', async(): Promise<void> => {
|
||||
store.getRepresentation.mockResolvedValue({ data: guardedStreamFrom([
|
||||
quad(nn('auth'), nn(`${rdf}type`), nn(`${acl}Authorization`)),
|
||||
quad(nn('auth'), nn(`${acl}accessTo`), nn(identifier.path)),
|
||||
quad(nn('auth'), nn(`${acl}mode`), nn(`${acl}Write`)),
|
||||
]) } as Representation);
|
||||
await expect(reader.handle({ identifier, credentials })).resolves.toEqual({
|
||||
[CredentialGroup.public]: { write: true, append: true },
|
||||
[CredentialGroup.agent]: { write: true, append: true },
|
||||
[CredentialGroup.public]: { write: true, append: true, create: true, delete: true },
|
||||
[CredentialGroup.agent]: { write: true, append: true, create: true, delete: true },
|
||||
});
|
||||
});
|
||||
|
||||
it('allows everything on an acl resource if control permissions are granted.', async(): Promise<void> => {
|
||||
store.getRepresentation.mockResolvedValue({ data: guardedStreamFrom([
|
||||
quad(nn('auth'), nn(`${rdf}type`), nn(`${acl}Authorization`)),
|
||||
quad(nn('auth'), nn(`${acl}accessTo`), nn(identifier.path)),
|
||||
quad(nn('auth'), nn(`${acl}mode`), nn(`${acl}Control`)),
|
||||
]) } as Representation);
|
||||
await expect(reader.handle({ identifier: { path: `${identifier.path}.acl` }, credentials })).resolves.toEqual({
|
||||
[CredentialGroup.public]: { read: true, write: true, append: true, create: true, delete: true, control: true },
|
||||
[CredentialGroup.agent]: { read: true, write: true, append: true, create: true, delete: true, control: true },
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects everything on an acl resource if there are no control permissions.', async(): Promise<void> => {
|
||||
store.getRepresentation.mockResolvedValue({ data: guardedStreamFrom([
|
||||
quad(nn('auth'), nn(`${rdf}type`), nn(`${acl}Authorization`)),
|
||||
quad(nn('auth'), nn(`${acl}accessTo`), nn(identifier.path)),
|
||||
quad(nn('auth'), nn(`${acl}mode`), nn(`${acl}Read`)),
|
||||
]) } as Representation);
|
||||
await expect(reader.handle({ identifier: { path: `${identifier.path}.acl` }, credentials })).resolves.toEqual({
|
||||
[CredentialGroup.public]: {},
|
||||
[CredentialGroup.agent]: {},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -24,7 +24,7 @@ describe('A ComposedAuxiliaryStrategy', (): void => {
|
||||
validator = {
|
||||
handleSafe: jest.fn(),
|
||||
} as any;
|
||||
strategy = new ComposedAuxiliaryStrategy(identifierStrategy, metadataGenerator, validator, true);
|
||||
strategy = new ComposedAuxiliaryStrategy(identifierStrategy, metadataGenerator, validator, false, true);
|
||||
});
|
||||
|
||||
it('calls the AuxiliaryIdentifierStrategy for related calls.', async(): Promise<void> => {
|
||||
@ -45,6 +45,10 @@ describe('A ComposedAuxiliaryStrategy', (): void => {
|
||||
expect(identifierStrategy.isAuxiliaryIdentifier).toHaveBeenLastCalledWith(identifier);
|
||||
});
|
||||
|
||||
it('returns the injected value for usesOwnAuthorization.', async(): Promise<void> => {
|
||||
expect(strategy.usesOwnAuthorization()).toBe(false);
|
||||
});
|
||||
|
||||
it('returns the injected value for isRequiredInRoot.', async(): Promise<void> => {
|
||||
expect(strategy.isRequiredInRoot()).toBe(true);
|
||||
});
|
||||
|
@ -11,6 +11,10 @@ class SimpleSuffixStrategy implements AuxiliaryStrategy {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public usesOwnAuthorization(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public getAuxiliaryIdentifier(identifier: ResourceIdentifier): ResourceIdentifier {
|
||||
return { path: `${identifier.path}${this.suffix}` };
|
||||
}
|
||||
@ -77,6 +81,15 @@ describe('A RoutingAuxiliaryStrategy', (): void => {
|
||||
expect(sources[1].addMetadata).toHaveBeenLastCalledWith(metadata);
|
||||
});
|
||||
|
||||
it('#usesOwnAuthorization returns the result of the correct source.', async(): Promise<void> => {
|
||||
sources[0].usesOwnAuthorization = jest.fn();
|
||||
sources[1].usesOwnAuthorization = jest.fn();
|
||||
strategy.usesOwnAuthorization(dummy2Id);
|
||||
expect(sources[0].usesOwnAuthorization).toHaveBeenCalledTimes(0);
|
||||
expect(sources[1].usesOwnAuthorization).toHaveBeenCalledTimes(1);
|
||||
expect(sources[1].usesOwnAuthorization).toHaveBeenLastCalledWith(dummy2Id);
|
||||
});
|
||||
|
||||
it('#isRequiredInRoot returns the result of the correct source.', async(): Promise<void> => {
|
||||
sources[0].isRequiredInRoot = jest.fn();
|
||||
sources[1].isRequiredInRoot = jest.fn();
|
||||
|
@ -2,6 +2,7 @@ import 'jest-rdf';
|
||||
import { CredentialGroup } from '../../../../../src/authentication/Credentials';
|
||||
import { WebAclMetadataCollector } from '../../../../../src/ldp/operations/metadata/WebAclMetadataCollector';
|
||||
import type { Operation } from '../../../../../src/ldp/operations/Operation';
|
||||
import type { AclPermission } from '../../../../../src/ldp/permissions/AclPermission';
|
||||
import { RepresentationMetadata } from '../../../../../src/ldp/representation/RepresentationMetadata';
|
||||
import { ACL, AUTH } from '../../../../../src/util/Vocabularies';
|
||||
|
||||
@ -38,7 +39,7 @@ describe('A WebAclMetadataCollector', (): void => {
|
||||
|
||||
it('adds corresponding metadata for all permissions present.', async(): Promise<void> => {
|
||||
operation.permissionSet = {
|
||||
[CredentialGroup.agent]: { read: true, write: true, control: false },
|
||||
[CredentialGroup.agent]: { read: true, write: true, control: false } as AclPermission,
|
||||
[CredentialGroup.public]: { read: true, write: false },
|
||||
};
|
||||
await expect(writer.handle({ metadata, operation })).resolves.toBeUndefined();
|
||||
@ -46,4 +47,15 @@ describe('A WebAclMetadataCollector', (): void => {
|
||||
expect(metadata.getAll(AUTH.terms.userMode)).toEqualRdfTermArray([ ACL.terms.Read, ACL.terms.Write ]);
|
||||
expect(metadata.get(AUTH.terms.publicMode)).toEqualRdfTerm(ACL.terms.Read);
|
||||
});
|
||||
|
||||
it('ignores unknown modes.', async(): Promise<void> => {
|
||||
operation.permissionSet = {
|
||||
[CredentialGroup.agent]: { read: true, create: true },
|
||||
[CredentialGroup.public]: { read: true },
|
||||
};
|
||||
await expect(writer.handle({ metadata, operation })).resolves.toBeUndefined();
|
||||
expect(metadata.quads()).toHaveLength(2);
|
||||
expect(metadata.getAll(AUTH.terms.userMode)).toEqualRdfTermArray([ ACL.terms.Read ]);
|
||||
expect(metadata.get(AUTH.terms.publicMode)).toEqualRdfTerm(ACL.terms.Read);
|
||||
});
|
||||
});
|
||||
|
@ -1,26 +0,0 @@
|
||||
import type { AuxiliaryIdentifierStrategy } from '../../../../src/ldp/auxiliary/AuxiliaryIdentifierStrategy';
|
||||
import { AclModesExtractor } from '../../../../src/ldp/permissions/AclModesExtractor';
|
||||
import { AccessMode } from '../../../../src/ldp/permissions/Permissions';
|
||||
import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError';
|
||||
|
||||
describe('An AclModesExtractor', (): void => {
|
||||
let extractor: AclModesExtractor;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
const aclStrategy = {
|
||||
isAuxiliaryIdentifier: (id): boolean => id.path.endsWith('.acl'),
|
||||
} as AuxiliaryIdentifierStrategy;
|
||||
extractor = new AclModesExtractor(aclStrategy);
|
||||
});
|
||||
|
||||
it('can only handle acl files.', async(): Promise<void> => {
|
||||
await expect(extractor.canHandle({ target: { path: 'http://test.com/foo' }} as any))
|
||||
.rejects.toThrow(NotImplementedHttpError);
|
||||
await expect(extractor.canHandle({ target: { path: 'http://test.com/foo.acl' }} as any))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns control permissions.', async(): Promise<void> => {
|
||||
await expect(extractor.handle()).resolves.toEqual(new Set([ AccessMode.control ]));
|
||||
});
|
||||
});
|
@ -29,11 +29,11 @@ describe('A MethodModesExtractor', (): void => {
|
||||
|
||||
it('requires write for PUT operations.', async(): Promise<void> => {
|
||||
await expect(extractor.handle({ method: 'PUT' } as Operation))
|
||||
.resolves.toEqual(new Set([ AccessMode.append, AccessMode.write ]));
|
||||
.resolves.toEqual(new Set([ AccessMode.append, AccessMode.write, AccessMode.create, AccessMode.delete ]));
|
||||
});
|
||||
|
||||
it('requires write for DELETE operations.', async(): Promise<void> => {
|
||||
await expect(extractor.handle({ method: 'DELETE' } as Operation))
|
||||
.resolves.toEqual(new Set([ AccessMode.append, AccessMode.write ]));
|
||||
.resolves.toEqual(new Set([ AccessMode.append, AccessMode.write, AccessMode.create, AccessMode.delete ]));
|
||||
});
|
||||
});
|
||||
|
@ -58,7 +58,8 @@ describe('A SparqlPatchModesExtractor', (): void => {
|
||||
factory.createPattern(factory.createTerm('<s>'), factory.createTerm('<p>'), factory.createTerm('<o>')),
|
||||
]) },
|
||||
} as unknown as Operation;
|
||||
await expect(extractor.handle(operation)).resolves.toEqual(new Set([ AccessMode.append, AccessMode.write ]));
|
||||
await expect(extractor.handle(operation))
|
||||
.resolves.toEqual(new Set([ AccessMode.append, AccessMode.write, AccessMode.create, AccessMode.delete ]));
|
||||
});
|
||||
|
||||
it('requires append for composite operations with an insert.', async(): Promise<void> => {
|
||||
@ -81,6 +82,7 @@ describe('A SparqlPatchModesExtractor', (): void => {
|
||||
factory.createPattern(factory.createTerm('<s>'), factory.createTerm('<p>'), factory.createTerm('<o>')),
|
||||
]) ]) },
|
||||
} as unknown as Operation;
|
||||
await expect(extractor.handle(operation)).resolves.toEqual(new Set([ AccessMode.append, AccessMode.write ]));
|
||||
await expect(extractor.handle(operation))
|
||||
.resolves.toEqual(new Set([ AccessMode.append, AccessMode.write, AccessMode.create, AccessMode.delete ]));
|
||||
});
|
||||
});
|
||||
|
@ -91,6 +91,10 @@ class SimpleSuffixStrategy implements AuxiliaryStrategy {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public usesOwnAuthorization(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public getAuxiliaryIdentifier(identifier: ResourceIdentifier): ResourceIdentifier {
|
||||
return { path: `${identifier.path}${this.suffix}` };
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import type { ResourceStore, Permission } from '../../src/';
|
||||
import type { ResourceStore } from '../../src/';
|
||||
import { BasicRepresentation } from '../../src/';
|
||||
import type { AclPermission } from '../../src/ldp/permissions/AclPermission';
|
||||
|
||||
export class AclHelper {
|
||||
public readonly store: ResourceStore;
|
||||
@ -11,7 +12,7 @@ export class AclHelper {
|
||||
public async setSimpleAcl(
|
||||
resource: string,
|
||||
options: {
|
||||
permissions: Partial<Permission>;
|
||||
permissions: AclPermission;
|
||||
agentClass?: 'agent' | 'authenticated';
|
||||
agent?: string;
|
||||
accessTo?: boolean;
|
||||
@ -32,7 +33,7 @@ export class AclHelper {
|
||||
];
|
||||
|
||||
for (const perm of [ 'Read', 'Append', 'Write', 'Control' ]) {
|
||||
if (options.permissions[perm.toLowerCase() as keyof Permission]) {
|
||||
if (options.permissions[perm.toLowerCase() as keyof AclPermission]) {
|
||||
acl.push(`;\n acl:mode acl:${perm}`);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user