Skip to content

Commit

Permalink
Merge pull request #95 from VictoriqueMoe/allow-login-errors
Browse files Browse the repository at this point in the history
all login related errors now render IN the login page
  • Loading branch information
VictoriqueMoe authored Mar 7, 2024
2 parents 22da528 + 31bd21b commit 34a3af0
Show file tree
Hide file tree
Showing 16 changed files with 84 additions and 34 deletions.
4 changes: 1 addition & 3 deletions src/engine/IHttpErrorRenderEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ export interface IHttpErrorRenderEngine<T, E extends Exception> {
* @param obj
* @param response
*/
render(obj: HttpErrorRenderObj<E>, response: PlatformResponse): Promise<T>;
render(obj: HttpErrorRenderObj<E>, response?: PlatformResponse): Promise<T>;

/**
* Returns true if this render engine supports the exception thrown by the system
* @param exception
*/
supportsError(exception: Exception): boolean;

getTitle(): string | null;
}
14 changes: 0 additions & 14 deletions src/engine/impl/HttpErrorRenderers/AbstractEjsHttpRenderEngine.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Injectable, ProviderScope } from "@tsed/di";
import { HTTP_RENDER_ENGINE } from "../../../model/di/tokens.js";
import type { IHttpErrorRenderEngine } from "../../IHttpErrorRenderEngine.js";
import { AuthenticationError } from "../../../model/exceptions/AuthenticationError.js";
import { Exception } from "@tsed/exceptions";
import { HttpErrorRenderObj } from "../../../utils/typeings.js";
import { PlatformResponse } from "@tsed/common";

@Injectable({
scope: ProviderScope.SINGLETON,
type: HTTP_RENDER_ENGINE,
})
export class AuthenticationErrorRenderEngine implements IHttpErrorRenderEngine<string, AuthenticationError> {
public supportsError(exception: Exception): boolean {
return exception instanceof AuthenticationError;
}

public render(obj: HttpErrorRenderObj<AuthenticationError>, response: PlatformResponse): Promise<string> {
return response.render("login.ejs", obj);
}
}
9 changes: 3 additions & 6 deletions src/engine/impl/HttpErrorRenderers/DefaultHttpRenderEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,16 @@ export class DefaultHttpRenderEngine implements IHttpErrorRenderEngine<DefaultRe
return Promise.resolve(this.mapError(obj.internalError));
}

public supportsError(): boolean {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public supportsError(_: Exception): boolean {
return false;
}

public mapError(error: Exception): DefaultRenderObj {
return {
name: error.origin?.name ?? error.name,
message: error.message,
status: error.status ?? 50,
status: error.status ?? 500,
};
}

public getTitle(): string | null {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ export class FileProtectedRenderEngine implements IHttpErrorRenderEngine<string,
return exception instanceof FileProtectedException;
}

public getTitle(): string {
return "Please enter a password for this file..";
}

public render(obj: HttpErrorRenderObj<FileProtectedException>, response: PlatformResponse): Promise<string> {
const isEncrypted = obj.internalError.isEncrypted;
return response.render("fileLogin.ejs", {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { IHttpErrorRenderEngine } from "../../IHttpErrorRenderEngine.js";
import { ReCAPTCHAException } from "../../../model/exceptions/ReCAPTCHAException.js";
import { Injectable, InjectContext, ProviderScope } from "@tsed/di";
import { HTTP_RENDER_ENGINE } from "../../../model/di/tokens.js";
import { HttpErrorRenderObj } from "../../../utils/typeings.js";
import { type PlatformContext, PlatformResponse } from "@tsed/common";
import { Exception } from "@tsed/exceptions";

@Injectable({
scope: ProviderScope.SINGLETON,
type: HTTP_RENDER_ENGINE,
})
export class ReCAPTCHALoginExceptionRenderEngine implements IHttpErrorRenderEngine<string, ReCAPTCHAException> {
@InjectContext()
protected $ctx?: PlatformContext;

public render(obj: HttpErrorRenderObj<ReCAPTCHAException>, response: PlatformResponse): Promise<string> {
return response.render("login.ejs", obj);
}

public supportsError(exception: Exception): boolean {
return this.$ctx?.url.includes("/login") ? exception instanceof ReCAPTCHAException : false;
}
}
3 changes: 2 additions & 1 deletion src/engine/impl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
export * from "./FileEngine.js";
export * from "./av/ClamAvEngine.js";
export * from "./av/MsDefenderEngine.js";
export * from "./HttpErrorRenderers/AbstractEjsHttpRenderEngine.js";
export * from "./HttpErrorRenderers/AuthenticationErrorRenderEngine.js";
export * from "./HttpErrorRenderers/DefaultHttpRenderEngine.js";
export * from "./HttpErrorRenderers/FileProtectedRenderEngine.js";
export * from "./HttpErrorRenderers/ReCAPTCHALoginExceptionRenderEngine.js";
1 change: 0 additions & 1 deletion src/filters/HttpExceptionFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export class HttpExceptionFilter implements ExceptionFilterMethods<Exception> {
status: exception.status,
message: exception.message,
internalError: exception,
title: renderEngine.getTitle(),
};
const response = ctx.response;
const template = await renderEngine.render(obj, response);
Expand Down
4 changes: 2 additions & 2 deletions src/filters/PassportExceptionFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { Catch } from "@tsed/common";
import { PassportException } from "@tsed/passport";
import { Inject } from "@tsed/di";
import { HttpExceptionFilter } from "./HttpExceptionFilter.js";
import { Unauthorized } from "@tsed/exceptions";
import { AuthenticationError } from "../model/exceptions/AuthenticationError.js";

@Catch(PassportException)
export class PassportExceptionFilter implements ExceptionFilterMethods<PassportException> {
public constructor(@Inject() private httpExceptionFilter: HttpExceptionFilter) {}

public catch(exception: PassportException, ctx: PlatformContext): unknown {
if (exception.name === "AuthenticationError") {
return this.httpExceptionFilter.catch(new Unauthorized("Unauthorized", exception.origin), ctx);
return this.httpExceptionFilter.catch(new AuthenticationError(exception.message, exception.origin), ctx);
}
return this.httpExceptionFilter.catch(exception, ctx);
}
Expand Down
3 changes: 2 additions & 1 deletion src/middleware/endpoint/ReCAPTCHAMiddleWare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Inject } from "@tsed/di";
import { ReCAPTCHAService } from "../../services/ReCAPTCHAService.js";
import { BadRequest } from "@tsed/exceptions";
import { BodyParams } from "@tsed/platform-params";
import { ReCAPTCHAException } from "../../model/exceptions/ReCAPTCHAException.js";

@Middleware()
export class ReCAPTCHAMiddleWare implements MiddlewareMethods {
Expand All @@ -15,7 +16,7 @@ export class ReCAPTCHAMiddleWare implements MiddlewareMethods {
}
const reCAPTCHAResp = await this.reCAPTCHAService.validateResponse(reCAPTCHA);
if (!reCAPTCHAResp) {
throw new BadRequest("reCAPTCHA response invalid.");
throw new ReCAPTCHAException("reCAPTCHA response invalid.");
}
}
}
2 changes: 1 addition & 1 deletion src/middleware/global/IpFilterMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class IpFilterMiddleware implements MiddlewareMethods {
const ip = NetworkUtils.getIp(req);
const isBlocked = await this.ipRepo.isIpBlocked(ip);
if (isBlocked) {
throw new Forbidden("Your IP has been blocked");
throw new Forbidden("");
}
}
}
11 changes: 11 additions & 0 deletions src/model/exceptions/AuthenticationError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Unauthorized } from "@tsed/exceptions";

export class AuthenticationError extends Unauthorized {
public constructor(message: string, origin?: Error | string) {
if (message === "Unauthorized") {
super("Incorrect email/password", origin);
} else {
super(message, origin);
}
}
}
7 changes: 7 additions & 0 deletions src/model/exceptions/ReCAPTCHAException.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { BadRequest } from "@tsed/exceptions";

export class ReCAPTCHAException extends BadRequest {
public constructor(message: string, origin?: Error | string) {
super(message, origin);
}
}
8 changes: 8 additions & 0 deletions src/public/login.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@
</div>
</form>
</div>
<% if(typeof internalError !== "undefined") { %>
<div class="card-footer">
<div class="alert alert-danger alert-dismissible" role="alert">
<%-message-%>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
</div>
<% } %>
</div>
</div>
</div>
Expand Down
1 change: 0 additions & 1 deletion src/utils/typeings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { FileUploadModel } from "../model/db/FileUpload.model.js";

export type HttpErrorRenderObj<T extends Exception> = {
status: number;
title: string | null;
message: string;
internalError: T;
};
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"noEmit": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"noImplicitOverride": true,
"skipLibCheck": true,
"lib": [
"es7",
"dom",
Expand Down

0 comments on commit 34a3af0

Please sign in to comment.