Skip to content

Commit

Permalink
help
Browse files Browse the repository at this point in the history
  • Loading branch information
devksingh4 committed Aug 5, 2024
1 parent 85e759d commit a085c73
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 28 deletions.
3 changes: 2 additions & 1 deletion cloudformation/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Mappings:
HostedZoneId: Z04502822NVIA85WM2SML
ApiDomainName: "aws.qa.acmuiuc.org"
ValidCorsOrigins: ["*"]
AadValidClientId: "251efa82-f589-42e1-9ebb-e214a4f40a0f"
AadValidClientId: "39c28870-94e4-47ee-b4fb-affe0bf96c9f"
prod:
ApiCertificateArn: arn:aws:acm:us-east-1:298118738376:certificate/6142a0e2-d62f-478e-bf15-5bdb616fe705
HostedZoneId: Z05246633460N5MEB9DBF
Expand Down Expand Up @@ -98,6 +98,7 @@ Resources:
Variables:
RunEnvironment: !Ref RunEnvironment
ValidCorsOrigins: !Join [",", !FindInMap [ApiGwConfig, !Ref RunEnvironment, ValidCorsOrigins]]
AadValidClientId: !FindInMap [ApiGwConfig, !Ref RunEnvironment, AadValidClientId]
VpcConfig:
Ipv6AllowedForDualStack: !If [ShouldAttachVpc, True, !Ref AWS::NoValue]
SecurityGroupIds: !If [ShouldAttachVpc, !FindInMap [EnvironmentToCidr, !Ref RunEnvironment, SecurityGroupIds], !Ref AWS::NoValue]
Expand Down
20 changes: 7 additions & 13 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* eslint import/no-nodejs-modules: ["error", {"allow": ["crypto"]}] */
import { randomUUID } from "crypto";
import fastify from "fastify";
import { fastifyAuthPlugin } from "./plugins/auth.js";
import FastifyAuthProvider from '@fastify/auth';
import { EventsApiRoles } from "./roles.js";
import FastifyAuthProvider from "@fastify/auth";
import fastifyAuthPlugin from "./plugins/auth.js";
import protectedRoute from "./routes/protected.js";
const now = () => Date.now();
async function init() {
const app = fastify({
Expand All @@ -21,7 +21,7 @@ async function init() {
});
await app.register(fastifyAuthPlugin);
await app.register(FastifyAuthProvider);
app.runEnvironment = process.env.RunEnvironment ?? 'dev';
app.runEnvironment = process.env.RunEnvironment ?? "dev";
app.addHook("onRequest", (req, _, done) => {
req.startTime = now();
req.log.info({ url: req.raw.url }, "received request");
Expand All @@ -36,15 +36,9 @@ async function init() {
done();
});
app.get("/api/v1/healthz", (_, reply) => reply.send({ message: "UP" }));
app.get("/api/v1/protected", async (_, reply) => {
app.auth([
app.authenticate,
async (request, reply) => {
await app.authorize(request, reply, [EventsApiRoles.MANAGER]);
}
]);
reply.send({ message: "UP" });
});
await app.register(async (api, _options) => {
api.register(protectedRoute, { prefix: "/protected" });
}, { prefix: "/api/v1" });
return app;
}
if (import.meta.url === `file://${process.argv[1]}`) {
Expand Down
35 changes: 32 additions & 3 deletions src/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface BaseErrorParams<T extends string> {
httpStatusCode: number;
}

abstract class BaseError<T extends string> extends Error {
export abstract class BaseError<T extends string> extends Error {
public name: T;

public id: number;
Expand All @@ -20,22 +20,51 @@ abstract class BaseError<T extends string> extends Error {
this.id = id;
this.message = message;
this.httpStatusCode = httpStatusCode;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
toString() {

Check failure on line 27 in src/errors/index.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

Expected blank line between class members
return `Error ${this.id} (${this.name}): ${this.message}\n\n${this.stack}`

Check failure on line 28 in src/errors/index.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

Insert `;`
}
}

export class UnauthorizedError extends BaseError<"UnauthorizedError"> {
constructor({ message }: { message: string }) {
super({ name: "UnauthorizedError", id: 100, message, httpStatusCode: 401 });
super({ name: "UnauthorizedError", id: 101, message, httpStatusCode: 401 });
}
}

export class UnauthenticatedError extends BaseError<"UnauthenticatedError"> {
constructor({ message }: { message: string }) {
super({
name: "UnauthenticatedError",
id: 101,
id: 102,
message,
httpStatusCode: 403,
});
}
}

export class InternalServerError extends BaseError<"InternalServerError"> {
constructor() {
super({
name: "InternalServerError",
id: 100,
message: "An internal server error occurred. Please try again or contact support.",

Check failure on line 54 in src/errors/index.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

Insert `⏎·······`
httpStatusCode: 500,
});
}
}


Check failure on line 60 in src/errors/index.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

Delete `⏎`
export class NotFoundError extends BaseError<"NotFoundError"> {
constructor({endpointName}: {endpointName: string}) {

Check failure on line 62 in src/errors/index.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

Replace `endpointName}:·{endpointName:·string` with `·endpointName·}:·{·endpointName:·string·`
super({
name: "NotFoundError",
id: 103,
message: `${endpointName} is not a valid URL.`,
httpStatusCode: 404,
});
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import fastify, { FastifyInstance } from "fastify";
import FastifyAuthProvider from "@fastify/auth";
import fastifyAuthPlugin from "./plugins/auth.js";
import protectedRoute from "./routes/protected.js";
import errorHandlerPlugin from "./plugins/errorHandler.js";

const now = () => Date.now();

Expand All @@ -23,6 +24,7 @@ async function init() {
});
await app.register(fastifyAuthPlugin);
await app.register(FastifyAuthProvider);
await app.register(errorHandlerPlugin);
app.runEnvironment = process.env.RunEnvironment ?? "dev";
app.addHook("onRequest", (req, _, done) => {
req.startTime = now();
Expand Down
18 changes: 12 additions & 6 deletions src/plugins/auth.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { FastifyPluginAsync, FastifyReply, FastifyRequest } from "fastify";
import fp from "fastify-plugin";
import { UnauthenticatedError, UnauthorizedError } from "../errors/index.js";
import { EventsApiRoles } from "../roles.js";
import { BaseError, UnauthenticatedError, UnauthorizedError } from "../errors/index.js";

Check failure on line 3 in src/plugins/auth.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

Replace `·BaseError,·UnauthenticatedError,·UnauthorizedError·` with `⏎··BaseError,⏎··UnauthenticatedError,⏎··UnauthorizedError,⏎`
import { AppRoles } from "../roles.js";

// const GroupRoleMapping: Record<string, EventsApiRoles[]> = {};
// const GroupRoleMapping: Record<string, AppRoles[]> = {};

const authPlugin: FastifyPluginAsync = async (fastify, _options) => {
fastify.decorate(
Expand All @@ -13,8 +13,14 @@ const authPlugin: FastifyPluginAsync = async (fastify, _options) => {
_reply: FastifyReply,
): Promise<void> {
try {
request.log.info("Authenticating JWT");
} catch (_: unknown) {
const clientId = process.env.AadValidClientId;
if (!clientId) {
throw new UnauthenticatedError({message: "Server could not find valid AAD Client ID."})

Check failure on line 18 in src/plugins/auth.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

Replace `message:·"Server·could·not·find·valid·AAD·Client·ID."})` with `⏎············message:·"Server·could·not·find·valid·AAD·Client·ID.",⏎··········});`
}
} catch (err: unknown) {
if (err instanceof BaseError) {
throw err;
}
throw new UnauthenticatedError({ message: "Could not verify JWT." });
}
},
Expand All @@ -24,7 +30,7 @@ const authPlugin: FastifyPluginAsync = async (fastify, _options) => {
async function (
request: FastifyRequest,
_reply: FastifyReply,
_validRoles: EventsApiRoles[],
_validRoles: AppRoles[],
): Promise<void> {
try {
request.log.info("Authorizing JWT");
Expand Down
31 changes: 31 additions & 0 deletions src/plugins/errorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import fastify, { FastifyReply, FastifyRequest } from 'fastify';

Check warning on line 1 in src/plugins/errorHandler.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

'fastify' is defined but never used. Allowed unused vars must match /^_/u

Check failure on line 1 in src/plugins/errorHandler.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

Replace `'fastify'` with `"fastify"`
import fp from 'fastify-plugin';

Check failure on line 2 in src/plugins/errorHandler.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

Replace `'fastify-plugin'` with `"fastify-plugin"`
import { BaseError, InternalServerError, NotFoundError } from '../errors/index.js';

Check failure on line 3 in src/plugins/errorHandler.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

Replace `·BaseError,·InternalServerError,·NotFoundError·}·from·'../errors/index.js'` with `⏎··BaseError,⏎··InternalServerError,⏎··NotFoundError,⏎}·from·"../errors/index.js"`
import { request } from 'http';

Check warning on line 4 in src/plugins/errorHandler.ts

View workflow job for this annotation

GitHub Actions / Run Unit Tests

'request' is defined but never used. Allowed unused vars must match /^_/u

const errorHandlerPlugin = fp(async(fastify) => {
fastify.setErrorHandler((err: unknown, request: FastifyRequest, reply: FastifyReply) => {
let finalErr = new InternalServerError();
if (err instanceof BaseError) {
finalErr = err;
}
if (err instanceof BaseError) {
request.log.error({errId: err.id, errName: err.name}, finalErr.toString())
} else if (err instanceof Error) {
request.log.error({errName: err.name}, 'Native unhandled error: response sent to client.')
} else {
request.log.error('Native unhandled error: response sent to client.')
}
reply.status(finalErr.httpStatusCode).type('application/json').send({
error: true,
name: finalErr.name,
id: finalErr.id,
message: finalErr.message,
})
})
fastify.setNotFoundHandler((request: FastifyRequest) => {
throw new NotFoundError({endpointName: request.url});
})
})

export default errorHandlerPlugin;
2 changes: 1 addition & 1 deletion src/roles.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint-disable import/prefer-default-export */
export enum EventsApiRoles {
export enum AppRoles {
MANAGER,
}
4 changes: 2 additions & 2 deletions src/routes/protected.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FastifyPluginAsync } from "fastify";
import { EventsApiRoles } from "../roles.js";
import { AppRoles } from "../roles.js";

const protectedRoute: FastifyPluginAsync = async (fastify, _options) => {
fastify.get(
Expand All @@ -8,7 +8,7 @@ const protectedRoute: FastifyPluginAsync = async (fastify, _options) => {
onRequest: fastify.auth([
fastify.authenticate,
async (request, reply) => {
fastify.authorize(request, reply, [EventsApiRoles.MANAGER]);
fastify.authorize(request, reply, [AppRoles.MANAGER]);
},
]),
},
Expand Down
4 changes: 2 additions & 2 deletions src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FastifyRequest, FastifyInstance, FastifyReply } from "fastify";
import { EventsApiRoles } from "./roles.ts";
import { AppRoles } from "./roles.ts";
declare module "fastify" {
interface FastifyInstance {
authenticate: (
Expand All @@ -9,7 +9,7 @@ declare module "fastify" {
authorize: (
request: FastifyRequest,
reply: FastifyReply,
validRoles: EventsApiRoles[],
validRoles: AppRoles[],
) => Promise<void>;
runEnvironment: string;
}
Expand Down

0 comments on commit a085c73

Please sign in to comment.