Skip to content

Commit 9e18a7d

Browse files
authored
Replace AuthenticatedRequest (#54)
2 parents 7b671f5 + 7341c96 commit 9e18a7d

File tree

6 files changed

+46
-90
lines changed

6 files changed

+46
-90
lines changed

src/Request.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import http, {OutgoingHttpHeader} from "node:http";
44
import stream from "node:stream";
55
import {Authenticator} from "./auth/Authenticator.js";
66
import {Authorisation} from "./auth/Authorisation.js";
7-
import {AuthenticatedRequest} from "./auth/AuthenticatedRequest.js";
7+
import {Permission} from "./auth/index.js";
8+
import {ThrowableResponse} from "./response/index.js";
89
import {Server} from "./Server.js";
10+
import {ServerErrorRegistry} from "./ServerErrorRegistry.js";
911

1012
/**
1113
* An incoming HTTP request from a connected client.
@@ -158,24 +160,6 @@ export class Request<A> {
158160
return await authenticator.authenticate(this);
159161
}
160162

161-
/**
162-
* Attempt to authenticate this request with one of the {@link Server}’s {@link Authenticator}s.
163-
* @returns `null` if the request lacks authorisation information.
164-
*/
165-
public async authenticate(): Promise<AuthenticatedRequest<A> | null> {
166-
const authorisation = await this.getAuthorisation();
167-
if (authorisation === null) return null;
168-
return new AuthenticatedRequest<A>(
169-
authorisation,
170-
this.method,
171-
this.url,
172-
this.headers,
173-
this.bodyStream,
174-
this.ip,
175-
this.server,
176-
);
177-
}
178-
179163
/**
180164
* Returns a boolean value that declares whether the body has been read yet.
181165
*/
@@ -249,6 +233,33 @@ export class Request<A> {
249233
return (await this.blob()).text();
250234
}
251235

236+
/**
237+
* Require that authorisation can be obtained from this request.
238+
* @throws {@link ThrowableResponse} of {@link ServerErrorRegistry.ErrorCodes.UNAUTHORISED} if authorisation cannot
239+
* be obtained.
240+
*/
241+
public async auth(): Promise<Authorisation<A>>;
242+
243+
/**
244+
* Require that authorisation can be obtained from this request and that the given (requested) permission(s) are
245+
* ALL within the scope of the authorisation.
246+
* @param permissions The requested permissions.
247+
* @throws {@link ThrowableResponse} of {@link ServerErrorRegistry.ErrorCodes.UNAUTHORISED} if authorisation cannot
248+
* be obtained.
249+
* @throws {@link ThrowableResponse} of {@link ServerErrorRegistry.ErrorCodes.NO_PERMISSION} if the authorisation
250+
* lacks any of the requested permissions.
251+
*/
252+
public async auth(...permissions: [Permission, ...Permission[]]): Promise<Authorisation<A>>;
253+
254+
public async auth(...permissions: Permission[]): Promise<Authorisation<A>> {
255+
const authorisation = await this.getAuthorisation();
256+
if (authorisation === null)
257+
throw new ThrowableResponse(this.server.errors._get(ServerErrorRegistry.ErrorCodes.UNAUTHORISED, null));
258+
if (permissions.length > 0 && !authorisation.hasAll(permissions))
259+
throw new ThrowableResponse(this.server.errors._get(ServerErrorRegistry.ErrorCodes.NO_PERMISSION, null));
260+
return authorisation;
261+
}
262+
252263
/**
253264
* Response headers that the Response to this request should include.
254265
* @internal

src/ServerErrorRegistry.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class ServerErrorRegistry<A> {
2727

2828
[ServerErrorRegistry.ErrorCodes.NO_PERMISSION]:
2929
new TextResponse("You do not have permission to perform this action.", 403),
30+
31+
[ServerErrorRegistry.ErrorCodes.UNAUTHORISED]:
32+
new TextResponse("Authentication information was either absent or invalid.", 401),
3033
};
3134
}
3235

@@ -57,6 +60,7 @@ namespace ServerErrorRegistry {
5760
INTERNAL,
5861
PRECONDITION_FAILED,
5962
NO_PERMISSION,
63+
UNAUTHORISED,
6064
}
6165
}
6266

src/auth/AuthenticatedRequest.ts

Lines changed: 0 additions & 69 deletions
This file was deleted.

src/auth/Authenticator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export interface Authenticator<A extends any> {
1313
canAuthenticate(request: Request<A>): boolean;
1414

1515
/**
16-
* Authenticate the given request. If authenticate fails, e.g. due to missing or invalid information, such as
16+
* Authenticate the given request. If authentication fails, e.g. due to missing or invalid information, such as
1717
* credentials, the authenticator should return `null`, which can be communicated to the client by implementing
1818
* applications using a 401 status response.
1919
* @param request

src/auth/PermissionGroup.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ export class PermissionGroup implements Permissible, Iterable<Permission> {
2828
return false;
2929
}
3030

31+
/**
32+
* Check that the group has all the specified permissions.
33+
* @param permissions The permissions to check.
34+
*/
35+
public hasAll(permissions: Iterable<Permission>): boolean {
36+
for (const permission of permissions)
37+
if (!this.has(permission))
38+
return false;
39+
return true;
40+
}
41+
3142
/**
3243
* An iterator over the permissions in this group.
3344
*/

src/auth/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
/* Auto-generated by generateIndices.sh */
2-
export * from "./AuthenticatedRequest.js";
32
export * from "./Authenticator.js";
43
export * from "./Authorisation.js";
54
export * from "./Permissible.js";

0 commit comments

Comments
 (0)