From 492beedf004f9d96a1e75e8e63417b347fa7ea40 Mon Sep 17 00:00:00 2001 From: Mohamed ELIDRISSI <67818913+elidrissidev@users.noreply.github.com> Date: Sun, 12 Feb 2023 00:02:52 +0100 Subject: [PATCH] refactor: add server side validation schema for apiKeys (#4859) * refactor: add tests for apiKey api routes * refactor: move files to subfolder * refactor: schema for apiKeys.create and apiKeys.delete --- server/routes/api/apiKeys/apiKeys.test.ts | 75 ++++++++++++++++++++++ server/routes/api/{ => apiKeys}/apiKeys.ts | 14 ++-- server/routes/api/apiKeys/index.ts | 1 + server/routes/api/apiKeys/schema.ts | 20 ++++++ 4 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 server/routes/api/apiKeys/apiKeys.test.ts rename server/routes/api/{ => apiKeys}/apiKeys.ts (85%) create mode 100644 server/routes/api/apiKeys/index.ts create mode 100644 server/routes/api/apiKeys/schema.ts diff --git a/server/routes/api/apiKeys/apiKeys.test.ts b/server/routes/api/apiKeys/apiKeys.test.ts new file mode 100644 index 000000000..dee707e6d --- /dev/null +++ b/server/routes/api/apiKeys/apiKeys.test.ts @@ -0,0 +1,75 @@ +import { buildApiKey, buildUser } from "@server/test/factories"; +import { getTestServer } from "@server/test/support"; + +const server = getTestServer(); + +describe("#apiKeys.create", () => { + it("should allow creating an api key", async () => { + const user = await buildUser(); + + const res = await server.post("/api/apiKeys.create", { + body: { + token: user.getJwtToken(), + name: "My API Key", + }, + }); + const body = await res.json(); + + expect(res.status).toEqual(200); + expect(body.data.name).toEqual("My API Key"); + }); + + it("should require authentication", async () => { + const res = await server.post("/api/apiKeys.create"); + expect(res.status).toEqual(401); + }); +}); + +describe("#apiKeys.list", () => { + it("should return api keys of a user", async () => { + const user = await buildUser(); + await buildApiKey({ + name: "My API Key", + userId: user.id, + }); + + const res = await server.post("/api/apiKeys.list", { + body: { + token: user.getJwtToken(), + }, + }); + const body = await res.json(); + + expect(res.status).toEqual(200); + expect(body.data[0].name).toEqual("My API Key"); + }); + + it("should require authentication", async () => { + const res = await server.post("/api/apiKeys.list"); + expect(res.status).toEqual(401); + }); +}); + +describe("#apiKeys.delete", () => { + it("should delete users api key", async () => { + const user = await buildUser(); + const apiKey = await buildApiKey({ + name: "My API Key", + userId: user.id, + }); + + const res = await server.post("/api/apiKeys.delete", { + body: { + token: user.getJwtToken(), + id: apiKey.id, + }, + }); + + expect(res.status).toEqual(200); + }); + + it("should require authentication", async () => { + const res = await server.post("/api/apiKeys.delete"); + expect(res.status).toEqual(401); + }); +}); diff --git a/server/routes/api/apiKeys.ts b/server/routes/api/apiKeys/apiKeys.ts similarity index 85% rename from server/routes/api/apiKeys.ts rename to server/routes/api/apiKeys/apiKeys.ts index 7b96dea0d..542f15a52 100644 --- a/server/routes/api/apiKeys.ts +++ b/server/routes/api/apiKeys/apiKeys.ts @@ -1,20 +1,21 @@ import Router from "koa-router"; import auth from "@server/middlewares/authentication"; +import validate from "@server/middlewares/validate"; import { ApiKey, Event } from "@server/models"; import { authorize } from "@server/policies"; import { presentApiKey } from "@server/presenters"; import { APIContext } from "@server/types"; -import { assertUuid, assertPresent } from "@server/validation"; -import pagination from "./middlewares/pagination"; +import pagination from "../middlewares/pagination"; +import * as T from "./schema"; const router = new Router(); router.post( "apiKeys.create", auth({ member: true }), - async (ctx: APIContext) => { + validate(T.APIKeysCreateSchema), + async (ctx: APIContext) => { const { name } = ctx.request.body; - assertPresent(name, "name is required"); const { user } = ctx.state.auth; authorize(user, "createApiKey", user.team); @@ -65,10 +66,11 @@ router.post( router.post( "apiKeys.delete", auth({ member: true }), - async (ctx: APIContext) => { + validate(T.APIKeysDeleteSchema), + async (ctx: APIContext) => { const { id } = ctx.request.body; - assertUuid(id, "id is required"); const { user } = ctx.state.auth; + const key = await ApiKey.findByPk(id); authorize(user, "delete", key); diff --git a/server/routes/api/apiKeys/index.ts b/server/routes/api/apiKeys/index.ts new file mode 100644 index 000000000..f2fbc16ad --- /dev/null +++ b/server/routes/api/apiKeys/index.ts @@ -0,0 +1 @@ +export { default } from "./apiKeys"; diff --git a/server/routes/api/apiKeys/schema.ts b/server/routes/api/apiKeys/schema.ts new file mode 100644 index 000000000..4cae67078 --- /dev/null +++ b/server/routes/api/apiKeys/schema.ts @@ -0,0 +1,20 @@ +import { z } from "zod"; +import BaseSchema from "@server/routes/api/BaseSchema"; + +export const APIKeysCreateSchema = BaseSchema.extend({ + body: z.object({ + /** API Key name */ + name: z.string(), + }), +}); + +export type APIKeysCreateReq = z.infer; + +export const APIKeysDeleteSchema = BaseSchema.extend({ + body: z.object({ + /** API Key Id */ + id: z.string().uuid(), + }), +}); + +export type APIKeysDeleteReq = z.infer;