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:
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user