feat: Add index signature to Credentials

* feat: abstract Credentials type for Authorizer

* feat: abstract Credentials type in CredentialsExtractor & PermissionReader

* chore: typegraphical corrections in documentation

Co-authored-by: Ted Thibodeau Jr <tthibodeau@openlinksw.com>

* fix: remove accidental .js extension

* feat: also check for undefined credentials when deciding 401/404

* docs: corrections to code documentation

Co-authored-by: Ted Thibodeau Jr <tthibodeau@openlinksw.com>

* fix: revert abstraction & index signature to Credentials

* chhore: fix linter issues

---------

Co-authored-by: Ted Thibodeau Jr <tthibodeau@openlinksw.com>
This commit is contained in:
Wouter Termont 2023-11-10 08:10:10 +01:00 committed by GitHub
parent e7d8081aff
commit 86f45923ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 10 additions and 7 deletions

View File

@ -5,4 +5,5 @@ export type Credentials = {
agent?: { webId: string }; agent?: { webId: string };
client?: { clientId: string }; client?: { clientId: string };
issuer?: { url: string }; issuer?: { url: string };
[key: string]: unknown;
}; };

View File

@ -18,7 +18,7 @@ export interface AuthorizerInput {
} }
/** /**
* Verifies if the credentials provide access with the given permissions on the resource. * Verifies whether the credentials provide access with the given permissions on the resource.
* An {@link Error} with the necessary explanation will be thrown when permissions are not granted. * An {@link Error} with the necessary explanation will be thrown if permissions are not granted.
*/ */
export abstract class Authorizer extends AsyncHandler<AuthorizerInput> {} export abstract class Authorizer extends AsyncHandler<AuthorizerInput> {}

View File

@ -13,7 +13,7 @@ import { AccessMode } from './permissions/Permissions';
/** /**
* Authorizer that bases its decision on the output it gets from its PermissionReader. * Authorizer that bases its decision on the output it gets from its PermissionReader.
* For each permission it checks if the reader allows that for at least one credential type, * For each permission it checks if the reader allows that for at least one credential type,
* if yes authorization is granted. * if yes, authorization is granted.
* `undefined` values for reader results are interpreted as `false`. * `undefined` values for reader results are interpreted as `false`.
*/ */
export class PermissionBasedAuthorizer extends Authorizer { export class PermissionBasedAuthorizer extends Authorizer {
@ -37,7 +37,9 @@ export class PermissionBasedAuthorizer extends Authorizer {
// Ensure all required modes are within the agent's permissions. // Ensure all required modes are within the agent's permissions.
for (const [ identifier, modes ] of requestedModes.entrySets()) { for (const [ identifier, modes ] of requestedModes.entrySets()) {
const modeString = [ ...modes ].join(','); const modeString = [ ...modes ].join(',');
this.logger.debug(`Checking if ${credentials.agent?.webId} has ${modeString} permissions for ${identifier.path}`); this.logger.debug(
`Checking if ${JSON.stringify(credentials)} has ${modeString} permissions for ${identifier.path}`,
);
const permissionSet = availablePermissions.get(identifier) ?? {}; const permissionSet = availablePermissions.get(identifier) ?? {};
for (const mode of modes) { for (const mode of modes) {
try { try {
@ -82,7 +84,7 @@ export class PermissionBasedAuthorizer extends Authorizer {
private requireModePermission(credentials: Credentials, permissionSet: PermissionSet, mode: AccessMode): void { private requireModePermission(credentials: Credentials, permissionSet: PermissionSet, mode: AccessMode): void {
if (!permissionSet[mode]) { if (!permissionSet[mode]) {
if (this.isAuthenticated(credentials)) { if (this.isAuthenticated(credentials)) {
this.logger.warn(`Agent ${credentials.agent!.webId} has no ${mode} permissions`); this.logger.warn(`Agent ${JSON.stringify(credentials)} has no ${mode} permissions`);
throw new ForbiddenHttpError(); throw new ForbiddenHttpError();
} else { } else {
// Solid, §2.1: "When a client does not provide valid credentials when requesting a resource that requires it, // Solid, §2.1: "When a client does not provide valid credentials when requesting a resource that requires it,
@ -99,6 +101,6 @@ export class PermissionBasedAuthorizer extends Authorizer {
* @param credentials - Credentials to check. * @param credentials - Credentials to check.
*/ */
private isAuthenticated(credentials: Credentials): boolean { private isAuthenticated(credentials: Credentials): boolean {
return typeof credentials.agent?.webId === 'string'; return Object.values(credentials).some((cred): boolean => cred !== undefined);
} }
} }

View File

@ -16,7 +16,7 @@ export interface PermissionReaderInput {
/** /**
* Discovers the permissions of the given credentials on the given identifier. * Discovers the permissions of the given credentials on the given identifier.
* In case the reader finds no permission for the requested identifiers and credentials * If the reader finds no permission for the requested identifiers and credentials,
* it can return an empty or incomplete map. * it can return an empty or incomplete map.
*/ */
export abstract class PermissionReader extends AsyncHandler<PermissionReaderInput, PermissionMap> {} export abstract class PermissionReader extends AsyncHandler<PermissionReaderInput, PermissionMap> {}