From 828e38d3d0441dce0cccfcd5dd3cbeb677743862 Mon Sep 17 00:00:00 2001 From: Dev-CasperTheGhost <53900565+Dev-CasperTheGhost@users.noreply.github.com> Date: Mon, 1 Nov 2021 12:27:31 +0100 Subject: [PATCH 1/5] :tada: start on #99 --- .../migrations/20211101110733_/migration.sql | 15 +++ packages/api/prisma/schema.prisma | 12 +++ .../controllers/admin/manage/CadSettings.ts | 61 +++++++++++- packages/api/src/middlewares/IsAuth.ts | 2 + .../components/admin/manage/ApiTokenTab.tsx | 99 +++++++++++++++++++ .../src/pages/admin/manage/cad-settings.tsx | 13 +-- packages/client/src/types/prisma.ts | 14 ++- 7 files changed, 205 insertions(+), 11 deletions(-) create mode 100644 packages/api/prisma/migrations/20211101110733_/migration.sql create mode 100644 packages/client/src/components/admin/manage/ApiTokenTab.tsx diff --git a/packages/api/prisma/migrations/20211101110733_/migration.sql b/packages/api/prisma/migrations/20211101110733_/migration.sql new file mode 100644 index 000000000..0aaa99c8b --- /dev/null +++ b/packages/api/prisma/migrations/20211101110733_/migration.sql @@ -0,0 +1,15 @@ +-- AlterTable +ALTER TABLE "cad" ADD COLUMN "apiTokenId" TEXT; + +-- CreateTable +CREATE TABLE "ApiToken" ( + "id" TEXT NOT NULL, + "enabled" BOOLEAN NOT NULL DEFAULT false, + "token" TEXT, + "routes" TEXT[], + + CONSTRAINT "ApiToken_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "cad" ADD CONSTRAINT "cad_apiTokenId_fkey" FOREIGN KEY ("apiTokenId") REFERENCES "ApiToken"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index 2e6fd183d..aed0afa0c 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -28,6 +28,8 @@ model cad { disabledFeatures Feature[] miscCadSettings MiscCadSettings? @relation(fields: [miscCadSettingsId], references: [id]) miscCadSettingsId String? + apiToken ApiToken? @relation(fields: [apiTokenId], references: [id]) + apiTokenId String? } model MiscCadSettings { @@ -45,6 +47,16 @@ model MiscCadSettings { cad cad[] } +model ApiToken { + id String @id @default(cuid()) + enabled Boolean @default(false) + token String? + // empty = * + routes String[] + + cad cad[] +} + model User { id String @id @default(cuid()) username String @unique @db.VarChar(255) diff --git a/packages/api/src/controllers/admin/manage/CadSettings.ts b/packages/api/src/controllers/admin/manage/CadSettings.ts index dd6823983..222595ad2 100644 --- a/packages/api/src/controllers/admin/manage/CadSettings.ts +++ b/packages/api/src/controllers/admin/manage/CadSettings.ts @@ -6,12 +6,13 @@ import { } from "@snailycad/schemas"; import { Controller } from "@tsed/di"; import { BodyParams, Context } from "@tsed/platform-params"; -import { Get, JsonRequestBody, Put } from "@tsed/schema"; +import { Delete, Get, JsonRequestBody, Put } from "@tsed/schema"; import { prisma } from "../../../lib/prisma"; import { IsAuth, IsOwner } from "../../../middlewares"; import { BadRequest } from "@tsed/exceptions"; import { UseBefore } from "@tsed/common"; import { Socket } from "../../../services/SocketService"; +import { nanoid } from "nanoid"; @Controller("/cad-settings") export class ManageCitizensController { @@ -106,4 +107,62 @@ export class ManageCitizensController { return updated; } + + @UseBefore(IsAuth, IsOwner) + @Put("/api-token") + async updateApiToken(@Context() ctx: Context, @BodyParams() body: JsonRequestBody) { + const cad = ctx.get("cad"); + console.log({ cad }); + + const existing = + cad.apiTokenId && + (await prisma.apiToken.findFirst({ + where: { + id: cad.apiTokenId, + }, + })); + + if (existing) { + const updated = await prisma.apiToken.update({ + where: { + id: existing.id, + }, + data: { + enabled: body.get("enabled"), + }, + }); + + return updated; + } + + const apiToken = await prisma.apiToken.create({ + data: { + cad: { connect: { id: cad.id } }, + token: nanoid(40), + }, + }); + + return apiToken; + } + + @UseBefore(IsAuth, IsOwner) + @Delete("/api-token") + async regenerateApiToken(@Context() ctx: Context) { + const cad = ctx.get("cad"); + + if (!cad.apiTokenId) { + throw new BadRequest("noApiTokenId"); + } + + const updated = await prisma.apiToken.update({ + where: { + id: cad.apiTokenId, + }, + data: { + token: nanoid(40), + }, + }); + + return updated; + } } diff --git a/packages/api/src/middlewares/IsAuth.ts b/packages/api/src/middlewares/IsAuth.ts index 788a81426..cd167c04f 100644 --- a/packages/api/src/middlewares/IsAuth.ts +++ b/packages/api/src/middlewares/IsAuth.ts @@ -14,6 +14,8 @@ const CAD_SELECT = (user: Pick) => ({ liveMapSocketURl: user.rank === Rank.OWNER, registrationCode: user.rank === Rank.OWNER, steamApiKey: user.rank === Rank.OWNER, + apiTokenId: user.rank === Rank.OWNER, + apiToken: user.rank === Rank.OWNER, discordWebhookURL: true, miscCadSettings: true, miscCadSettingsId: true, diff --git a/packages/client/src/components/admin/manage/ApiTokenTab.tsx b/packages/client/src/components/admin/manage/ApiTokenTab.tsx new file mode 100644 index 000000000..939860d39 --- /dev/null +++ b/packages/client/src/components/admin/manage/ApiTokenTab.tsx @@ -0,0 +1,99 @@ +import * as React from "react"; +import { Tab } from "@headlessui/react"; +import { Button } from "components/Button"; +import { FormField } from "components/form/FormField"; +import { PasswordInput } from "components/form/Input"; +import { Toggle } from "components/form/Toggle"; +import { Loader } from "components/Loader"; +import { useAuth } from "context/AuthContext"; +import { Formik } from "formik"; +import useFetch from "lib/useFetch"; +import { useTranslations } from "use-intl"; + +export const ApiTokenTab = () => { + const common = useTranslations("Common"); + const { state, execute } = useFetch(); + const { cad } = useAuth(); + + const [token, setToken] = React.useState(""); + + async function onSubmit(values: typeof INITIAL_VALUES) { + const { json } = await execute("/admin/manage/cad-settings/api-token", { + method: "PUT", + data: values, + }); + + if (json.token) { + setToken(json.token); + } + } + + async function handleRegenerate() { + const { json } = await execute("/admin/manage/cad-settings/api-token", { + method: "DELETE", + }); + + if (json.token) { + setToken(json.token); + } + } + + function handleClick(e: React.MouseEvent) { + const t = e.target as HTMLInputElement; + t.select(); + } + + const INITIAL_VALUES = { + enabled: cad?.apiToken?.enabled ?? false, + token: cad?.apiToken?.token ?? "", + }; + + return ( + +

Public API access

+ + + {({ handleChange, handleSubmit, values }) => ( +
+ + + + + + + + +
+ {cad?.apiTokenId ? ( + + ) : null} + +
+
+ )} +
+
+ ); +}; diff --git a/packages/client/src/pages/admin/manage/cad-settings.tsx b/packages/client/src/pages/admin/manage/cad-settings.tsx index c0a100b34..dbee83881 100644 --- a/packages/client/src/pages/admin/manage/cad-settings.tsx +++ b/packages/client/src/pages/admin/manage/cad-settings.tsx @@ -1,6 +1,5 @@ import * as React from "react"; import { useAuth } from "context/AuthContext"; -import { useRouter } from "next/router"; import { rank } from "types/prisma"; import { AdminLayout } from "components/admin/AdminLayout"; import { useTranslations } from "use-intl"; @@ -22,16 +21,16 @@ import { TabsContainer } from "components/tabs/TabsContainer"; import { Tab } from "@headlessui/react"; import { MiscFeatures } from "components/admin/manage/MiscFeatures"; import { requestAll } from "lib/utils"; +import { ApiTokenTab } from "components/admin/manage/ApiTokenTab"; export default function CadSettings() { const { state, execute } = useFetch(); const { user, cad, setCad } = useAuth(); - const router = useRouter(); const t = useTranslations("Management"); const common = useTranslations("Common"); - const SETTINGS_TABS = [t("GENERAL_SETTINGS"), t("FEATURES"), t("MISC_SETTINGS")]; + const SETTINGS_TABS = [t("GENERAL_SETTINGS"), t("FEATURES"), t("MISC_SETTINGS"), "Api Token"]; async function onSubmit(values: typeof INITIAL_VALUES) { const { json } = await execute("/admin/manage/cad-settings", { @@ -44,12 +43,6 @@ export default function CadSettings() { } } - React.useEffect(() => { - if (user?.rank !== rank.OWNER) { - // router.push("/403"); - } - }, [user, router]); - if (user?.rank !== rank.OWNER) { return null; } @@ -156,6 +149,8 @@ export default function CadSettings() { + + ); diff --git a/packages/client/src/types/prisma.ts b/packages/client/src/types/prisma.ts index 60e14aff4..2fe445006 100644 --- a/packages/client/src/types/prisma.ts +++ b/packages/client/src/types/prisma.ts @@ -15,8 +15,9 @@ export type cad = { discordWebhookURL: string | null; whitelisted: boolean; towWhitelisted: boolean; + apiTokenId: string | null; disabledFeatures: Feature[]; -} & { miscCadSettings: MiscCadSettings }; +} & { miscCadSettings: MiscCadSettings; apiToken: ApiToken | null }; /** * Model MiscCadSettings @@ -35,6 +36,17 @@ export type MiscCadSettings = { allowDuplicateCitizenNames: boolean; }; +/** + * Model ApiToken + */ + +export type ApiToken = { + id: string; + enabled: boolean; + token: string | null; + routes: string[]; +}; + /** * Model User */ From f6927018dddad2e9a1e7c1fe56ec2db775f22c91 Mon Sep 17 00:00:00 2001 From: Dev-CasperTheGhost <53900565+Dev-CasperTheGhost@users.noreply.github.com> Date: Mon, 1 Nov 2021 16:10:35 +0100 Subject: [PATCH 2/5] :tada: implement new permissions API system --- packages/api/src/controllers/admin/Values.ts | 18 +-- .../controllers/admin/manage/Businesses.ts | 4 +- .../controllers/admin/manage/CadSettings.ts | 12 +- .../src/controllers/admin/manage/Citizens.ts | 4 +- .../api/src/controllers/admin/manage/Units.ts | 4 +- .../api/src/controllers/admin/manage/Users.ts | 8 +- .../dispatch/Calls911Controller.ts | 8 +- .../dispatch/DispatchController.ts | 6 +- .../controllers/dispatch/SearchController.ts | 4 +- .../src/controllers/ems-fd/EmsFdController.ts | 8 +- .../api/src/controllers/leo/LeoController.ts | 10 +- .../src/controllers/leo/SearchController.ts | 2 +- packages/api/src/lib/auth.ts | 16 +-- packages/api/src/middlewares/IsAuth.ts | 105 +++++++++++++++-- packages/api/src/middlewares/Permissions.ts | 110 ------------------ packages/api/src/middlewares/index.ts | 1 - packages/config/src/index.ts | 2 + packages/config/src/routes.ts | 69 +++++++++++ 18 files changed, 205 insertions(+), 186 deletions(-) delete mode 100644 packages/api/src/middlewares/Permissions.ts create mode 100644 packages/config/src/routes.ts diff --git a/packages/api/src/controllers/admin/Values.ts b/packages/api/src/controllers/admin/Values.ts index 0ab3300bd..1c7a3b072 100644 --- a/packages/api/src/controllers/admin/Values.ts +++ b/packages/api/src/controllers/admin/Values.ts @@ -1,19 +1,11 @@ import { ValueType, PrismaClient } from ".prisma/client"; import { CREATE_PENAL_CODE_SCHEMA, validate, VALUE_SCHEMA } from "@snailycad/schemas"; -import { - Get, - Controller, - PathParams, - UseBeforeEach, - UseBefore, - BodyParams, - QueryParams, -} from "@tsed/common"; +import { Get, Controller, PathParams, UseBeforeEach, BodyParams, QueryParams } from "@tsed/common"; import { Delete, JsonRequestBody, Patch, Post, Put } from "@tsed/schema"; import { prisma } from "../../lib/prisma"; -import { IsAdmin } from "../../middlewares/Permissions"; import { IsValidPath } from "../../middlewares/ValidPath"; import { BadRequest, NotFound } from "@tsed/exceptions"; +import { IsAuth } from "../../middlewares"; type NameType = Exclude< keyof PrismaClient, @@ -42,7 +34,7 @@ const GET_VALUES: Partial> }; @Controller("/admin/values/:path") -@UseBeforeEach(IsValidPath) +@UseBeforeEach(IsAuth, IsValidPath) export class ValuesController { @Get("/") async getValueByPath(@PathParams("path") path: string, @QueryParams("paths") rawPaths: string) { @@ -89,7 +81,6 @@ export class ValuesController { return values; } - @UseBefore(IsAdmin) @Post("/") async createValueByPath(@BodyParams() body: JsonRequestBody, @PathParams("path") path: string) { const type = this.getTypeFromPath(path); @@ -241,7 +232,6 @@ export class ValuesController { return value; } - @UseBefore(IsAdmin) @Delete("/:id") async deleteValueByPathAndId(@PathParams("id") id: string, @PathParams("path") path: string) { const type = this.getTypeFromPath(path); @@ -278,7 +268,6 @@ export class ValuesController { return true; } - @UseBefore(IsAdmin) @Patch("/:id") async patchValueByPathAndId( @BodyParams() body: JsonRequestBody, @@ -459,7 +448,6 @@ export class ValuesController { } @Put("/positions") - @UseBefore(IsAdmin) async updatePositions(@BodyParams() body: JsonRequestBody) { const ids = body.get("ids"); diff --git a/packages/api/src/controllers/admin/manage/Businesses.ts b/packages/api/src/controllers/admin/manage/Businesses.ts index 08bef41f3..a1dbaf97c 100644 --- a/packages/api/src/controllers/admin/manage/Businesses.ts +++ b/packages/api/src/controllers/admin/manage/Businesses.ts @@ -5,9 +5,9 @@ import { BodyParams, Context, PathParams } from "@tsed/platform-params"; import { Delete, Get, JsonRequestBody } from "@tsed/schema"; import { userProperties } from "../../../lib/auth"; import { prisma } from "../../../lib/prisma"; -import { IsAuth, IsAdmin } from "../../../middlewares"; +import { IsAuth } from "../../../middlewares"; -@UseBeforeEach(IsAuth, IsAdmin) +@UseBeforeEach(IsAuth) @Controller("/businesses-admin") export class ManageBusinessesController { @Get("/") diff --git a/packages/api/src/controllers/admin/manage/CadSettings.ts b/packages/api/src/controllers/admin/manage/CadSettings.ts index 222595ad2..614b58e97 100644 --- a/packages/api/src/controllers/admin/manage/CadSettings.ts +++ b/packages/api/src/controllers/admin/manage/CadSettings.ts @@ -8,7 +8,7 @@ import { Controller } from "@tsed/di"; import { BodyParams, Context } from "@tsed/platform-params"; import { Delete, Get, JsonRequestBody, Put } from "@tsed/schema"; import { prisma } from "../../../lib/prisma"; -import { IsAuth, IsOwner } from "../../../middlewares"; +import { IsAuth } from "../../../middlewares"; import { BadRequest } from "@tsed/exceptions"; import { UseBefore } from "@tsed/common"; import { Socket } from "../../../services/SocketService"; @@ -35,7 +35,7 @@ export class ManageCitizensController { return { ...cad, registrationCode: !!cad!.registrationCode }; } - @UseBefore(IsAuth, IsOwner) + @UseBefore(IsAuth) @Put("/") async updateCadSettings(@Context() ctx: Context, @BodyParams() body: JsonRequestBody) { const error = validate(CAD_SETTINGS_SCHEMA, body.toJSON(), true); @@ -64,7 +64,7 @@ export class ManageCitizensController { return updated; } - @UseBefore(IsAuth, IsOwner) + @UseBefore(IsAuth) @Put("/features") async updateDisabledFeatures(@Context() ctx: Context, @BodyParams() body: JsonRequestBody) { const error = validate(DISABLED_FEATURES_SCHEMA, body.toJSON(), true); @@ -84,7 +84,7 @@ export class ManageCitizensController { return updated; } - @UseBefore(IsAuth, IsOwner) + @UseBefore(IsAuth) @Put("/misc") async updateMiscSettings(@Context() ctx: Context, @BodyParams() body: JsonRequestBody) { const error = validate(CAD_MISC_SETTINGS_SCHEMA, body.toJSON(), true); @@ -108,7 +108,7 @@ export class ManageCitizensController { return updated; } - @UseBefore(IsAuth, IsOwner) + @UseBefore(IsAuth) @Put("/api-token") async updateApiToken(@Context() ctx: Context, @BodyParams() body: JsonRequestBody) { const cad = ctx.get("cad"); @@ -145,7 +145,7 @@ export class ManageCitizensController { return apiToken; } - @UseBefore(IsAuth, IsOwner) + @UseBefore(IsAuth) @Delete("/api-token") async regenerateApiToken(@Context() ctx: Context) { const cad = ctx.get("cad"); diff --git a/packages/api/src/controllers/admin/manage/Citizens.ts b/packages/api/src/controllers/admin/manage/Citizens.ts index f87945c5d..8c3c4a760 100644 --- a/packages/api/src/controllers/admin/manage/Citizens.ts +++ b/packages/api/src/controllers/admin/manage/Citizens.ts @@ -5,9 +5,9 @@ import { BodyParams, Context, PathParams } from "@tsed/platform-params"; import { Delete, Get, JsonRequestBody } from "@tsed/schema"; import { userProperties } from "../../../lib/auth"; import { prisma } from "../../../lib/prisma"; -import { IsAuth, IsAdmin } from "../../../middlewares"; +import { IsAuth } from "../../../middlewares"; -@UseBeforeEach(IsAuth, IsAdmin) +@UseBeforeEach(IsAuth) @Controller("/citizens") export class ManageCitizensController { @Get("/") diff --git a/packages/api/src/controllers/admin/manage/Units.ts b/packages/api/src/controllers/admin/manage/Units.ts index 204e3dd1d..77b5f0e41 100644 --- a/packages/api/src/controllers/admin/manage/Units.ts +++ b/packages/api/src/controllers/admin/manage/Units.ts @@ -5,11 +5,11 @@ import { UseBeforeEach } from "@tsed/platform-middlewares"; import { Get, JsonRequestBody, Put } from "@tsed/schema"; import { unitProperties } from "../../../lib/officer"; import { prisma } from "../../../lib/prisma"; -import { IsAuth, IsSupervisor } from "../../../middlewares"; +import { IsAuth } from "../../../middlewares"; const include = unitProperties; -@UseBeforeEach(IsAuth, IsSupervisor) +@UseBeforeEach(IsAuth) @Controller("/units") export class ManageUnitsController { @Get("/") diff --git a/packages/api/src/controllers/admin/manage/Users.ts b/packages/api/src/controllers/admin/manage/Users.ts index 9e88f66a4..4fe290de0 100644 --- a/packages/api/src/controllers/admin/manage/Users.ts +++ b/packages/api/src/controllers/admin/manage/Users.ts @@ -6,13 +6,13 @@ import { UseBeforeEach } from "@tsed/platform-middlewares"; import { Delete, Get, JsonRequestBody, Post, Put } from "@tsed/schema"; import { userProperties } from "../../../lib/auth"; import { prisma } from "../../../lib/prisma"; -import { IsAuth, IsAdmin } from "../../../middlewares"; +import { IsAuth } from "../../../middlewares"; import { BAN_SCHEMA, UPDATE_USER_SCHEMA, validate } from "@snailycad/schemas"; import { Socket } from "../../../services/SocketService"; import { nanoid } from "nanoid"; import { genSaltSync, hashSync } from "bcrypt"; -@UseBeforeEach(IsAuth, IsAdmin) +@UseBeforeEach(IsAuth) @Controller("/users") export class ManageUsersController { private socket: Socket; @@ -22,7 +22,9 @@ export class ManageUsersController { @Get("/") async getUsers() { - const users = await prisma.user.findMany(); + const users = await prisma.user.findMany({ + select: userProperties, + }); return users; } diff --git a/packages/api/src/controllers/dispatch/Calls911Controller.ts b/packages/api/src/controllers/dispatch/Calls911Controller.ts index f1fe13cf4..cbb583485 100644 --- a/packages/api/src/controllers/dispatch/Calls911Controller.ts +++ b/packages/api/src/controllers/dispatch/Calls911Controller.ts @@ -7,8 +7,7 @@ import { BadRequest, NotFound } from "@tsed/exceptions"; import { prisma } from "../../lib/prisma"; import { Socket } from "../../services/SocketService"; import { UseBeforeEach } from "@tsed/platform-middlewares"; -import { IsAuth, IsDispatch } from "../../middlewares"; -import { UseBefore } from "@tsed/common"; +import { IsAuth } from "../../middlewares"; import { ShouldDoType, Officer, EmsFdDeputy } from ".prisma/client"; import { unitProperties } from "../../lib/officer"; @@ -71,7 +70,6 @@ export class Calls911Controller { return this.officerOrDeputyToUnit(call); } - @UseBefore(IsDispatch) @Put("/:id") async update911Call( @PathParams("id") id: string, @@ -167,7 +165,6 @@ export class Calls911Controller { return this.officerOrDeputyToUnit(updated); } - @UseBefore(IsDispatch) @Delete("/:id") async end911Call(@PathParams("id") id: string) { const call = await prisma.call911.findUnique({ @@ -181,7 +178,6 @@ export class Calls911Controller { return true; } - @UseBefore(IsDispatch) @Post("/events/:callId") async createCallEvent(@PathParams("callId") callId: string, @BodyParams() body: JsonRequestBody) { if (!body.get("description")) { @@ -208,7 +204,6 @@ export class Calls911Controller { return event; } - @UseBefore(IsDispatch) @Put("/events/:callId/:eventId") async updateCallEvent( @PathParams("callId") callId: string, @@ -252,7 +247,6 @@ export class Calls911Controller { return updated; } - @UseBefore(IsDispatch) @Delete("/events/:callId/:eventId") async deleteCallEvent( @PathParams("callId") callId: string, diff --git a/packages/api/src/controllers/dispatch/DispatchController.ts b/packages/api/src/controllers/dispatch/DispatchController.ts index 55f426edc..a4467d638 100644 --- a/packages/api/src/controllers/dispatch/DispatchController.ts +++ b/packages/api/src/controllers/dispatch/DispatchController.ts @@ -4,8 +4,8 @@ import { BodyParams, Context } from "@tsed/platform-params"; import { BadRequest } from "@tsed/exceptions"; import { prisma } from "../../lib/prisma"; import { Socket } from "../../services/SocketService"; -import { UseBefore, UseBeforeEach } from "@tsed/platform-middlewares"; -import { IsAuth, IsDispatch } from "../../middlewares"; +import { UseBeforeEach } from "@tsed/platform-middlewares"; +import { IsAuth } from "../../middlewares"; import { cad } from ".prisma/client"; @Controller("/dispatch") @@ -44,7 +44,6 @@ export class Calls911Controller { return { deputies, officers }; } - @UseBefore(IsDispatch) @Post("/aop") async updateAreaOfPlay(@Context("cad") cad: cad, @BodyParams() body: JsonRequestBody) { if (!body.get("aop")) { @@ -66,7 +65,6 @@ export class Calls911Controller { return updated; } - @UseBefore(IsDispatch) @Post("/signal-100") async setSignal100(@Context("cad") cad: cad, @BodyParams("value") value: boolean) { if (typeof value !== "boolean") { diff --git a/packages/api/src/controllers/dispatch/SearchController.ts b/packages/api/src/controllers/dispatch/SearchController.ts index ddd036d89..6133725b2 100644 --- a/packages/api/src/controllers/dispatch/SearchController.ts +++ b/packages/api/src/controllers/dispatch/SearchController.ts @@ -3,10 +3,10 @@ import { Post } from "@tsed/schema"; import { NotFound } from "@tsed/exceptions"; import { BodyParams } from "@tsed/platform-params"; import { prisma } from "../../lib/prisma"; -import { IsAuth, IsDispatch } from "../../middlewares"; +import { IsAuth } from "../../middlewares"; @Controller("/search") -@UseBeforeEach(IsAuth, IsDispatch) +@UseBeforeEach(IsAuth) export class SearchController { @Post("/address") async searchAddress(@BodyParams("address") address: string) { diff --git a/packages/api/src/controllers/ems-fd/EmsFdController.ts b/packages/api/src/controllers/ems-fd/EmsFdController.ts index c0b53f5ec..83a86add6 100644 --- a/packages/api/src/controllers/ems-fd/EmsFdController.ts +++ b/packages/api/src/controllers/ems-fd/EmsFdController.ts @@ -6,7 +6,6 @@ import { Req, MultipartFile, PlatformMulterFile, - UseBefore, } from "@tsed/common"; import { Delete, Get, JsonRequestBody, Post, Put } from "@tsed/schema"; import { @@ -21,7 +20,7 @@ import { prisma } from "../../lib/prisma"; import { cad, ShouldDoType, MiscCadSettings, User } from ".prisma/client"; import { setCookie } from "../../utils/setCookie"; import { AllowedFileExtension, allowedFileExtensions, Cookie } from "@snailycad/config"; -import { IsAuth, IsEmsFd } from "../../middlewares"; +import { IsAuth } from "../../middlewares"; import { signJWT } from "../../utils/jwt"; import { Socket } from "../../services/SocketService"; import { getWebhookData, sendDiscordWebhook } from "../../lib/discord"; @@ -38,7 +37,6 @@ export class EmsFdController { this.socket = socket; } - @UseBefore(IsEmsFd) @Get("/") async getUserDeputies(@Context("user") user: User) { const deputies = await prisma.emsFdDeputy.findMany({ @@ -59,7 +57,6 @@ export class EmsFdController { return { deputies, citizens }; } - @UseBefore(IsEmsFd) @Post("/") async createEmsFdDeputy(@BodyParams() body: JsonRequestBody, @Context("user") user: User) { const error = validate(CREATE_OFFICER_SCHEMA, body.toJSON(), true); @@ -105,7 +102,6 @@ export class EmsFdController { return deputy; } - @UseBefore(IsEmsFd) @Put("/:id") async updateDeputy( @PathParams("id") deputyId: string, @@ -272,7 +268,6 @@ export class EmsFdController { return updatedDeputy; } - @UseBefore(IsEmsFd) @Delete("/:id") async deleteDeputy(@PathParams("id") id: string, @Context() ctx: Context) { const deputy = await prisma.emsFdDeputy.findFirst({ @@ -373,7 +368,6 @@ export class EmsFdController { return updated; } - @UseBefore(IsEmsFd) @Post("/image/:id") async uploadImageToOfficer( @Context("user") user: User, diff --git a/packages/api/src/controllers/leo/LeoController.ts b/packages/api/src/controllers/leo/LeoController.ts index 5cc95b171..cd88c5c7a 100644 --- a/packages/api/src/controllers/leo/LeoController.ts +++ b/packages/api/src/controllers/leo/LeoController.ts @@ -15,7 +15,7 @@ import { prisma } from "../../lib/prisma"; import { Officer, cad, ShouldDoType, MiscCadSettings, User } from ".prisma/client"; import { setCookie } from "../../utils/setCookie"; import { AllowedFileExtension, allowedFileExtensions, Cookie } from "@snailycad/config"; -import { IsAuth, IsLeo } from "../../middlewares"; +import { IsAuth } from "../../middlewares"; import { signJWT } from "../../utils/jwt"; import { ActiveOfficer } from "../../middlewares/ActiveOfficer"; import { Socket } from "../../services/SocketService"; @@ -33,7 +33,6 @@ export class LeoController { this.socket = socket; } - @UseBefore(IsLeo) @Get("/") async getUserOfficers(@Context() ctx: Context) { const officers = await prisma.officer.findMany({ @@ -54,7 +53,6 @@ export class LeoController { return { officers, citizens }; } - @UseBefore(IsLeo) @Post("/") async createOfficer(@BodyParams() body: JsonRequestBody, @Context("user") user: User) { const error = validate(CREATE_OFFICER_SCHEMA, body.toJSON(), true); @@ -101,7 +99,6 @@ export class LeoController { return officer; } - @UseBefore(IsLeo) @Put("/:id") async updateOfficer( @PathParams("id") officerId: string, @@ -301,7 +298,6 @@ export class LeoController { return updatedOfficer; } - @UseBefore(IsLeo) @Delete("/:id") async deleteOfficer(@PathParams("id") officerId: string, @Context() ctx: Context) { const officer = await prisma.officer.findFirst({ @@ -324,7 +320,6 @@ export class LeoController { return true; } - @UseBefore(IsLeo) @Get("/logs") async getOfficerLogs(@Context() ctx: Context) { const logs = await prisma.officerLog.findMany({ @@ -366,7 +361,6 @@ export class LeoController { return Array.isArray(officers) ? officers : [officers]; } - @UseBefore(IsLeo) @Post("/image/:id") async uploadImageToOfficer( @Context("user") user: User, @@ -441,7 +435,6 @@ export class LeoController { this.socket.emitPanicButtonLeo(fullOfficer); } - @UseBefore(IsLeo) @Get("/impounded-vehicles") async getImpoundedVehicles() { const vehicles = await prisma.impoundedVehicle.findMany({ @@ -456,7 +449,6 @@ export class LeoController { return vehicles; } - @UseBefore(IsLeo) @Delete("/impounded-vehicles/:id") async checkoutImpoundedVehicle(@PathParams("id") id: string) { const vehicle = await prisma.impoundedVehicle.findUnique({ diff --git a/packages/api/src/controllers/leo/SearchController.ts b/packages/api/src/controllers/leo/SearchController.ts index 1871d1c7c..ad2e8a9ab 100644 --- a/packages/api/src/controllers/leo/SearchController.ts +++ b/packages/api/src/controllers/leo/SearchController.ts @@ -118,7 +118,7 @@ export class SearchController { return null; } - // not using Prisma's `OR` since it doesn't seem to be working 🤔 + // todo: not using Prisma's `OR` since it doesn't seem to be working 🤔 let vehicle = await prisma.registeredVehicle.findFirst({ where: { plate: { diff --git a/packages/api/src/lib/auth.ts b/packages/api/src/lib/auth.ts index 695a53e7f..f138e3bfa 100644 --- a/packages/api/src/lib/auth.ts +++ b/packages/api/src/lib/auth.ts @@ -23,31 +23,33 @@ export const userProperties = { tempPassword: true, }; -export async function getSessionUser(req: Req) { +export async function getSessionUser(req: Req, throwErrors = false) { const header = req.headers.cookie; - if (!header) { + if (throwErrors && !header) { throw new Unauthorized("Unauthorized"); } - const cookie = parse(header)[Cookie.Session]; + const cookie = parse(header ?? "")[Cookie.Session]; const jwtPayload = verifyJWT(cookie!); - if (!jwtPayload) { + if (throwErrors && !jwtPayload) { throw new Unauthorized("Unauthorized"); } const user = await prisma.user.findUnique({ where: { - id: jwtPayload.userId, + id: jwtPayload?.userId ?? "0000000", }, select: userProperties, }); - if (!user) { + if (!throwErrors && !user) return null as unknown as any; + + if (throwErrors && !user) { throw new NotFound("notFound"); } - const { tempPassword, ...rest } = user; + const { tempPassword, ...rest } = user! ?? {}; return { ...rest, hasTempPassword: !!tempPassword }; } diff --git a/packages/api/src/middlewares/IsAuth.ts b/packages/api/src/middlewares/IsAuth.ts index cd167c04f..99935b81c 100644 --- a/packages/api/src/middlewares/IsAuth.ts +++ b/packages/api/src/middlewares/IsAuth.ts @@ -1,9 +1,11 @@ import { Rank, User } from ".prisma/client"; +import { API_TOKEN_HEADER, DISABLED_API_TOKEN_ROUTES, PERMISSION_ROUTES } from "@snailycad/config"; import { Context, Middleware, Req, MiddlewareMethods } from "@tsed/common"; +import { BadRequest, Forbidden, Unauthorized } from "@tsed/exceptions"; import { getSessionUser } from "../lib/auth"; import { prisma } from "../lib/prisma"; -const CAD_SELECT = (user: Pick) => ({ +const CAD_SELECT = (user?: Pick) => ({ id: true, name: true, areaOfPlay: true, @@ -11,11 +13,11 @@ const CAD_SELECT = (user: Pick) => ({ towWhitelisted: true, whitelisted: true, disabledFeatures: true, - liveMapSocketURl: user.rank === Rank.OWNER, - registrationCode: user.rank === Rank.OWNER, - steamApiKey: user.rank === Rank.OWNER, - apiTokenId: user.rank === Rank.OWNER, - apiToken: user.rank === Rank.OWNER, + liveMapSocketURl: user?.rank === Rank.OWNER, + registrationCode: user?.rank === Rank.OWNER, + steamApiKey: user?.rank === Rank.OWNER, + apiTokenId: user?.rank === Rank.OWNER, + apiToken: user?.rank === Rank.OWNER, discordWebhookURL: true, miscCadSettings: true, miscCadSettingsId: true, @@ -24,7 +26,40 @@ const CAD_SELECT = (user: Pick) => ({ @Middleware() export class IsAuth implements MiddlewareMethods { async use(@Req() req: Req, @Context() ctx: Context) { - const user = await getSessionUser(req); + const header = req.headers[API_TOKEN_HEADER]; + + let user; + if (header) { + const cad = await prisma.cad.findFirst({ + select: { + apiToken: true, + }, + }); + + if (!cad?.apiToken?.enabled) { + throw new Unauthorized("Unauthorized"); + } + + if (cad.apiToken.token !== header) { + throw new Unauthorized("Unauthorized"); + } + + const isDisabled = isRouteDisabled(req); + if (isDisabled) { + throw new BadRequest("routeIsDisabled"); + } + + console.log({ header }); + } else { + user = await getSessionUser(req, true); + ctx.set("user", user); + + const hasPermission = hasPermissionForReq(req, user); + + if (!hasPermission) { + throw new Forbidden("Invalid Permissions"); + } + } let cad = await prisma.cad.findFirst({ select: CAD_SELECT(user), @@ -51,6 +86,60 @@ export class IsAuth implements MiddlewareMethods { } ctx.set("cad", cad); - ctx.set("user", user); } } + +function isRouteDisabled(req: Req) { + const url = req.originalUrl.toLowerCase(); + const requestMethod = req.method as any; + + const route = DISABLED_API_TOKEN_ROUTES.find(([r]) => r.startsWith(url) || url.startsWith(r)); + + if (route) { + const [, methods] = route; + + if (typeof methods === "string" && methods === "*") { + return true; + } else if (Array.isArray(methods) && methods.includes(requestMethod)) { + return true; + } + + return false; + } + + return false; +} + +function hasPermissionForReq(req: Req, user: User) { + const url = req.originalUrl.toLowerCase(); + const requestMethod = req.method as any; + + const [route] = PERMISSION_ROUTES.filter(([m, r]) => { + if (typeof r === "string") { + const isTrue = r.startsWith(url) || url.startsWith(r); + + if (m === "*") { + return isTrue; + } + + return m.includes(requestMethod.toUpperCase()) && isTrue; + } + + const isTrue = r.test(url) || url.match(r); + + if (m === "*") { + return isTrue; + } + + return m.includes(requestMethod) && isTrue; + }); + + if (route) { + const [, , callback] = route; + const hasPermission = callback(user); + + return hasPermission; + } + + return true; +} diff --git a/packages/api/src/middlewares/Permissions.ts b/packages/api/src/middlewares/Permissions.ts deleted file mode 100644 index 5f361e441..000000000 --- a/packages/api/src/middlewares/Permissions.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { User } from ".prisma/client"; -import { Middleware, MiddlewareMethods, Req } from "@tsed/common"; -import { Forbidden } from "@tsed/exceptions"; -import { getSessionUser } from "../lib/auth"; - -@Middleware() -export class IsAdmin implements MiddlewareMethods { - async use(@Req() req: Req) { - const user = await getSessionUser(req); - - if (!user) { - throw new Forbidden("Invalid Permissions"); - } - - await admin(user); - } -} - -@Middleware() -export class IsSupervisor implements MiddlewareMethods { - async use(@Req() req: Req) { - const user = await getSessionUser(req); - - if (!user) { - throw new Forbidden("Invalid Permissions"); - } - - const isSupervisor = await supervisor(user); - - if (!isSupervisor) { - await admin(user); - } - } -} - -@Middleware() -export class IsOwner implements MiddlewareMethods { - async use(@Req() req: Req) { - const user = await getSessionUser(req); - - if (!user) { - throw new Forbidden("Invalid Permissions"); - } - - if (user.rank !== "OWNER") { - throw new Forbidden("Invalid Permissions"); - } - } -} - -@Middleware() -export class IsDispatch implements MiddlewareMethods { - async use(@Req() req: Req) { - const user = await getSessionUser(req); - - if (!user) { - throw new Forbidden("Invalid Permissions"); - } - - if (!user.isDispatch) { - throw new Forbidden("Invalid Permissions"); - } - } -} - -@Middleware() -export class IsEmsFd implements MiddlewareMethods { - async use(@Req() req: Req) { - const user = await getSessionUser(req); - - if (!user) { - throw new Forbidden("Invalid Permissions"); - } - - if (!user.isEmsFd) { - throw new Forbidden("Invalid Permissions"); - } - } -} - -@Middleware() -export class IsLeo implements MiddlewareMethods { - async use(@Req() req: Req) { - const user = await getSessionUser(req); - - if (!user) { - throw new Forbidden("Invalid Permissions"); - } - - if (!user.isLeo) { - throw new Forbidden("Invalid Permissions"); - } - } -} - -async function admin(user: Pick) { - if (!["OWNER", "ADMIN"].includes(user.rank)) { - throw new Forbidden("Invalid Permissions"); - } - - return true; -} - -async function supervisor(user: Pick) { - if (!user.isSupervisor) { - throw new Forbidden("Invalid Permissions"); - } - - return true; -} diff --git a/packages/api/src/middlewares/index.ts b/packages/api/src/middlewares/index.ts index e33cfd50a..304322d70 100644 --- a/packages/api/src/middlewares/index.ts +++ b/packages/api/src/middlewares/index.ts @@ -1,3 +1,2 @@ export * from "./IsAuth"; export * from "./ValidPath"; -export * from "./Permissions"; diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index dd64727c3..388f460f4 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -4,7 +4,9 @@ export enum Cookie { ActiveDeputy = "snaily-cad-active-ems-fd-deputy", } +export const API_TOKEN_HEADER = "snaily-cad-api-token" as const; export type AllowedFileExtension = typeof allowedFileExtensions[number]; export const allowedFileExtensions = ["image/png", "image/gif", "image/jpeg", "image/jpg"] as const; export * from "./socket-events"; +export * from "./routes"; diff --git a/packages/config/src/routes.ts b/packages/config/src/routes.ts new file mode 100644 index 000000000..0d353ade7 --- /dev/null +++ b/packages/config/src/routes.ts @@ -0,0 +1,69 @@ +export type Method = "GET" | "POST" | "PATCH" | "PUT" | "DELETE"; + +/** + * `*` = all methods + */ +export type DisabledRoute = [string, Method[] | "*"]; + +export const DISABLED_API_TOKEN_ROUTES: DisabledRoute[] = [ + ["/v1/user", "*"], + ["/v1/admin/manage/cad-settings", "*"], + ["/v1/admin/manage/", ["POST", "DELETE", "PUT", "PATCH"]], + ["/v1/admin/values", ["POST", "DELETE", "PUT", "PATCH"]], + ["/v1/citizen", ["POST", "DELETE", "PUT", "PATCH"]], + // todo: add other routes +]; + +type _User = { + rank: "OWNER" | "USER" | "ADMIN"; + isLeo: boolean; + isDispatch: boolean; + isEmsFd: boolean; + isSupervisor: boolean; +}; +export type PermissionRoute = [Method[] | "*", string | RegExp, (user: _User) => boolean]; + +export const PERMISSION_ROUTES: PermissionRoute[] = [ + [ + ["PUT"], + // /v1/leo/:officerId/status + /\/v1\/(leo|ems-fd)\/[A-Z0-9]+\/status/i, + (u) => u.isLeo || u.isSupervisor || u.isDispatch || u.isEmsFd, + ], + [["GET"], /\/v1\/leo\/active-(officers|officer)/, (u) => u.isLeo || u.isDispatch], + ["*", "/v1/leo", (u) => u.isLeo], + + [["POST"], "/v1/search/name", (u) => u.isLeo || u.isDispatch], + [["POST"], "/v1/search/weapon", (u) => u.isLeo || u.isDispatch], + [["POST"], "/v1/search/vehicle", (u) => u.isLeo || u.isDispatch], + + [["GET"], /\/v1\/ems-fd\/active-(deputies|deputy)/, (u) => u.isEmsFd || u.isDispatch], + ["*", "/v1/ems-fd", (u) => u.isEmsFd], + + [["POST"], "/v1/search/medical-records", (u) => u.isEmsFd], + + [["GET"], "/v1/bolos", (u) => u.isLeo || u.isDispatch || u.isEmsFd], + [["POST", "PUT", "DELETE"], "/v1/bolos", (u) => u.isLeo || u.isDispatch], + + ["*", "/v1/admin/manage/cad-settings", (u) => ["OWNER"].includes(u.rank)], + [ + ["GET", "PATCH", "DELETE", "PUT", "POST"], + "/v1/admin/manage/units", + (u) => u.isSupervisor || ["ADMIN", "OWNER"].includes(u.rank), + ], + [ + ["GET", "PATCH", "DELETE", "PUT", "POST"], + "/v1/admin/manage/", + (u) => ["ADMIN", "OWNER"].includes(u.rank), + ], + [ + ["PATCH", "DELETE", "PUT", "POST"], + "/v1/admin/values/", + (u) => ["ADMIN", "OWNER"].includes(u.rank), + ], + [["PUT", "DELETE", "POST"], "/v1/911-calls/events", (u) => u.isDispatch], + [["POST"], "/v1/911-calls/assign-to/", (u) => u.isLeo || u.isEmsFd], + [["PUT", "DELETE"], "/v1/911-calls", (u) => u.isDispatch], + ["*", "/v1/dispatch", (u) => u.isDispatch], + ["*", "/v1/search/address", (u) => u.isDispatch], +]; From 57880421bc3217840c1c682b40d5ba86e23c6c1e Mon Sep 17 00:00:00 2001 From: Dev-CasperTheGhost <53900565+Dev-CasperTheGhost@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:49:51 +0100 Subject: [PATCH 3/5] :hammer: minor change --- .../src/controllers/leo/SearchController.ts | 25 ++++--------------- packages/api/src/lib/auth.ts | 25 +++++++++++-------- packages/api/src/lib/officer.ts | 8 +++--- packages/api/src/middlewares/ActiveOfficer.ts | 2 +- packages/api/src/middlewares/IsAuth.ts | 2 -- 5 files changed, 24 insertions(+), 38 deletions(-) diff --git a/packages/api/src/controllers/leo/SearchController.ts b/packages/api/src/controllers/leo/SearchController.ts index ad2e8a9ab..a3e716d11 100644 --- a/packages/api/src/controllers/leo/SearchController.ts +++ b/packages/api/src/controllers/leo/SearchController.ts @@ -118,12 +118,12 @@ export class SearchController { return null; } - // todo: not using Prisma's `OR` since it doesn't seem to be working 🤔 - let vehicle = await prisma.registeredVehicle.findFirst({ + const vehicle = await prisma.registeredVehicle.findFirst({ where: { - plate: { - startsWith: plateOrVin.toUpperCase(), - }, + OR: [ + { plate: { startsWith: plateOrVin.toUpperCase() } }, + { vinNumber: { startsWith: plateOrVin } }, + ], }, include: { citizen: true, @@ -132,21 +132,6 @@ export class SearchController { }, }); - if (!vehicle) { - vehicle = await prisma.registeredVehicle.findFirst({ - where: { - vinNumber: { - startsWith: plateOrVin, - }, - }, - include: { - citizen: true, - model: { include: { value: true } }, - registrationStatus: true, - }, - }); - } - if (!vehicle) { throw new NotFound("vehicleNotFound"); } diff --git a/packages/api/src/lib/auth.ts b/packages/api/src/lib/auth.ts index f138e3bfa..fae0800ed 100644 --- a/packages/api/src/lib/auth.ts +++ b/packages/api/src/lib/auth.ts @@ -4,6 +4,7 @@ import { parse } from "cookie"; import { Cookie } from "@snailycad/config"; import { verifyJWT } from "../utils/jwt"; import { prisma } from "./prisma"; +import { User } from ".prisma/client"; export const userProperties = { id: true, @@ -23,7 +24,7 @@ export const userProperties = { tempPassword: true, }; -export async function getSessionUser(req: Req, throwErrors = false) { +export async function getSessionUser(req: Req, throwErrors = false): Promise { const header = req.headers.cookie; if (throwErrors && !header) { @@ -37,19 +38,23 @@ export async function getSessionUser(req: Req, throwErrors = false) { throw new Unauthorized("Unauthorized"); } - const user = await prisma.user.findUnique({ - where: { - id: jwtPayload?.userId ?? "0000000", - }, - select: userProperties, - }); - - if (!throwErrors && !user) return null as unknown as any; + const user = jwtPayload + ? await prisma.user.findUnique({ + where: { + id: jwtPayload?.userId, + }, + select: userProperties, + }) + : null; + + if (!throwErrors && !user) { + return null as unknown as User; + } if (throwErrors && !user) { throw new NotFound("notFound"); } const { tempPassword, ...rest } = user! ?? {}; - return { ...rest, hasTempPassword: !!tempPassword }; + return { ...rest, tempPassword: null, hasTempPassword: !!tempPassword } as unknown as User; } diff --git a/packages/api/src/lib/officer.ts b/packages/api/src/lib/officer.ts index 6ea00c706..e312854bd 100644 --- a/packages/api/src/lib/officer.ts +++ b/packages/api/src/lib/officer.ts @@ -1,9 +1,9 @@ +import { User } from ".prisma/client"; import { Cookie } from "@snailycad/config"; import { Req, Context } from "@tsed/common"; import { BadRequest, Forbidden, Unauthorized } from "@tsed/exceptions"; import { parse } from "cookie"; import { verifyJWT } from "../utils/jwt"; -import { getSessionUser } from "./auth"; import { prisma } from "./prisma"; export const unitProperties = { @@ -14,14 +14,12 @@ export const unitProperties = { rank: true, }; -export async function getActiveOfficer(req: Req, userId: string, ctx: Context) { +export async function getActiveOfficer(req: Req, user: User, ctx: Context) { const header = req.headers.cookie; if (!header) { throw new BadRequest("noActiveOfficer"); } - const user = await getSessionUser(req); - if (!user.isDispatch || !user.isLeo) { throw new Forbidden("Invalid Permissions"); } @@ -49,7 +47,7 @@ export async function getActiveOfficer(req: Req, userId: string, ctx: Context) { const officer = await prisma.officer.findFirst({ where: { - userId, + userId: user.id, id: jwtPayload?.officerId, }, include: unitProperties, diff --git a/packages/api/src/middlewares/ActiveOfficer.ts b/packages/api/src/middlewares/ActiveOfficer.ts index e36ff6e88..4d0a4dbf8 100644 --- a/packages/api/src/middlewares/ActiveOfficer.ts +++ b/packages/api/src/middlewares/ActiveOfficer.ts @@ -6,7 +6,7 @@ import { getActiveOfficer } from "../lib/officer"; export class ActiveOfficer implements MiddlewareMethods { async use(@Req() req: Req, @Context() ctx: Context) { const user = await getSessionUser(req); - const officer = await getActiveOfficer(req, user.id, ctx); + const officer = await getActiveOfficer(req, user, ctx); ctx.set("activeOfficer", officer); } diff --git a/packages/api/src/middlewares/IsAuth.ts b/packages/api/src/middlewares/IsAuth.ts index 99935b81c..7c4ce2f9e 100644 --- a/packages/api/src/middlewares/IsAuth.ts +++ b/packages/api/src/middlewares/IsAuth.ts @@ -48,8 +48,6 @@ export class IsAuth implements MiddlewareMethods { if (isDisabled) { throw new BadRequest("routeIsDisabled"); } - - console.log({ header }); } else { user = await getSessionUser(req, true); ctx.set("user", user); From c4a8783cbea7447e29d223ce92a7f3cf313c6b4c Mon Sep 17 00:00:00 2001 From: Dev-CasperTheGhost <53900565+Dev-CasperTheGhost@users.noreply.github.com> Date: Tue, 2 Nov 2021 07:37:56 +0100 Subject: [PATCH 4/5] :hammer: add info link for public API --- .../api/src/controllers/admin/manage/CadSettings.ts | 1 - .../src/components/admin/manage/ApiTokenTab.tsx | 12 ++++++++++++ packages/client/src/pages/citizen/[id]/edit.tsx | 12 ++++++------ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/api/src/controllers/admin/manage/CadSettings.ts b/packages/api/src/controllers/admin/manage/CadSettings.ts index 614b58e97..7dfaf9cb7 100644 --- a/packages/api/src/controllers/admin/manage/CadSettings.ts +++ b/packages/api/src/controllers/admin/manage/CadSettings.ts @@ -112,7 +112,6 @@ export class ManageCitizensController { @Put("/api-token") async updateApiToken(@Context() ctx: Context, @BodyParams() body: JsonRequestBody) { const cad = ctx.get("cad"); - console.log({ cad }); const existing = cad.apiTokenId && diff --git a/packages/client/src/components/admin/manage/ApiTokenTab.tsx b/packages/client/src/components/admin/manage/ApiTokenTab.tsx index 939860d39..c90aba61f 100644 --- a/packages/client/src/components/admin/manage/ApiTokenTab.tsx +++ b/packages/client/src/components/admin/manage/ApiTokenTab.tsx @@ -52,6 +52,18 @@ export const ApiTokenTab = () => {

Public API access

+

+ Read more info about{" "} + + Public API Access here + +

+ {({ handleChange, handleSubmit, values }) => (
diff --git a/packages/client/src/pages/citizen/[id]/edit.tsx b/packages/client/src/pages/citizen/[id]/edit.tsx index ae20e257f..a5a745c07 100644 --- a/packages/client/src/pages/citizen/[id]/edit.tsx +++ b/packages/client/src/pages/citizen/[id]/edit.tsx @@ -35,12 +35,6 @@ export default function EditCitizen() { const { citizen } = useCitizen(); const { gender, ethnicity } = useValues(); - React.useEffect(() => { - if (!citizen) { - console.log("citizen not found"); - } - }, [citizen]); - if (!citizen) { return null; } @@ -294,6 +288,12 @@ export const getServerSideProps: GetServerSideProps = async ({ query, locale, re headers: req.headers, }).catch(() => ({ data: null })); + if (!data) { + return { + notFound: true, + }; + } + return { props: { values, From bcc2bec676a9db4cadc13c57633c602f77f33a6a Mon Sep 17 00:00:00 2001 From: Dev-CasperTheGhost <53900565+Dev-CasperTheGhost@users.noreply.github.com> Date: Tue, 2 Nov 2021 08:07:28 +0100 Subject: [PATCH 5/5] :hammer: minor change --- packages/api/src/controllers/admin/manage/CadSettings.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api/src/controllers/admin/manage/CadSettings.ts b/packages/api/src/controllers/admin/manage/CadSettings.ts index 7dfaf9cb7..6d72aa31c 100644 --- a/packages/api/src/controllers/admin/manage/CadSettings.ts +++ b/packages/api/src/controllers/admin/manage/CadSettings.ts @@ -137,7 +137,7 @@ export class ManageCitizensController { const apiToken = await prisma.apiToken.create({ data: { cad: { connect: { id: cad.id } }, - token: nanoid(40), + token: nanoid(56), }, }); @@ -158,7 +158,7 @@ export class ManageCitizensController { id: cad.apiTokenId, }, data: { - token: nanoid(40), + token: nanoid(56), }, });