diff --git a/prisma/migrations/20230615181742_/migration.sql b/prisma/migrations/20230615181742_/migration.sql deleted file mode 100644 index 4513401..0000000 --- a/prisma/migrations/20230615181742_/migration.sql +++ /dev/null @@ -1,28 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[serialNumber]` on the table `Printer` will be added. If there are existing duplicate values, this will fail. - -*/ --- AlterTable -ALTER TABLE "Printer" ADD COLUMN "serialNumber" TEXT; - --- CreateTable -CREATE TABLE "PrinterStatus" ( - "id" SERIAL NOT NULL, - "tonerBlackLevel" INTEGER NOT NULL, - "tonerCyanLevel" INTEGER, - "tonerMagentaLevel" INTEGER, - "tonerYellowLevel" INTEGER, - "counter" INTEGER NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "printerId" INTEGER NOT NULL, - - CONSTRAINT "PrinterStatus_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "Printer_serialNumber_key" ON "Printer"("serialNumber"); - --- AddForeignKey -ALTER TABLE "PrinterStatus" ADD CONSTRAINT "PrinterStatus_printerId_fkey" FOREIGN KEY ("printerId") REFERENCES "Printer"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20230615120426_init/migration.sql b/prisma/migrations/20230619174010_init/migration.sql similarity index 54% rename from prisma/migrations/20230615120426_init/migration.sql rename to prisma/migrations/20230619174010_init/migration.sql index e2375bf..de74d18 100644 --- a/prisma/migrations/20230615120426_init/migration.sql +++ b/prisma/migrations/20230619174010_init/migration.sql @@ -21,15 +21,40 @@ CREATE TABLE "User" ( -- CreateTable CREATE TABLE "Printer" ( "id" SERIAL NOT NULL, - "hostname" TEXT, "friendlyName" TEXT, + "serialNumber" TEXT, + "hostname" TEXT, "ip" TEXT NOT NULL, "model" "PrinterModel" NOT NULL, + "blackTonerModel" TEXT, + "cyanTonerModel" TEXT, + "magentaTonerModel" TEXT, + "yellowTonerModel" TEXT, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "Printer_pkey" PRIMARY KEY ("id") ); +-- CreateTable +CREATE TABLE "PrinterStatus" ( + "id" SERIAL NOT NULL, + "tonerBlackLevel" INTEGER NOT NULL, + "tonerCyanLevel" INTEGER, + "tonerMagentaLevel" INTEGER, + "tonerYellowLevel" INTEGER, + "counter" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "printerId" INTEGER NOT NULL, + + CONSTRAINT "PrinterStatus_pkey" PRIMARY KEY ("id") +); + -- CreateIndex CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); + +-- CreateIndex +CREATE UNIQUE INDEX "Printer_serialNumber_key" ON "Printer"("serialNumber"); + +-- AddForeignKey +ALTER TABLE "PrinterStatus" ADD CONSTRAINT "PrinterStatus_printerId_fkey" FOREIGN KEY ("printerId") REFERENCES "Printer"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/src/controllers/LdapController.ts b/src/controllers/LdapController.ts index 207baa0..245b54e 100644 --- a/src/controllers/LdapController.ts +++ b/src/controllers/LdapController.ts @@ -15,6 +15,7 @@ type LdapUser = { mail: string | null displayName: string thumbnailPhoto: string | null + groups?: string[] } export class LdapController extends Client implements LdapClientInterface { @@ -45,19 +46,25 @@ export class LdapController extends Client implements LdapClientInterface { } } - async getUser(username: string) { + async getUser(username: string): Promise { return await this.adminBondOperation(async () => { const { searchEntries } = await this.search(DN, { scope: 'sub', filter: `(sAMAccountName=${username})`, - attributes: ['mail', 'sAMAccountName', 'displayName', 'thumbnailPhoto'], + attributes: [ + 'mail', + 'sAMAccountName', + 'displayName', + 'thumbnailPhoto', + 'dn' + ], explicitBufferAttributes: ['thumbnailPhoto'] }) if (!searchEntries.length) throw new Error('User not found on LDAP server.') - const { sAMAccountName, displayName, mail, thumbnailPhoto } = + const { sAMAccountName, displayName, mail, thumbnailPhoto, dn } = searchEntries[0] const ldapUser: LdapUser = { @@ -66,13 +73,26 @@ export class LdapController extends Client implements LdapClientInterface { mail: mail.toString(), thumbnailPhoto: `data:image/png;base64,${Buffer.from( thumbnailPhoto as Buffer - ).toString('base64')}` + ).toString('base64')}`, + groups: await this.getGroupsForUser(dn.toString()) } return ldapUser }) } + async getGroupsForUser(dn: string) { + const { searchEntries } = await this.search(DN, { + scope: 'sub', + filter: `(member:1.2.840.113556.1.4.1941:=${dn})`, + attributes: ['cn'] + }) + + if (!searchEntries.length) throw new Error('User not found on LDAP server.') + + return searchEntries.map(entry => entry.cn.toString()) + } + async authenticate(username: string, password: string) { await this.bind(`${DOMAIN}\\${username}`, password) diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index aedd3e7..bff8dcd 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -1,16 +1,35 @@ +import { User } from '@prisma/client' import { LdapController } from '../controllers/LdapController.js' import { prisma } from '../prisma.js' +const ADMIN_GROUP = process.env.ADMIN_GROUP || 'PP-SERTI' +const INSPECTOR_GROUP = process.env.INSPECTOR_GROUP || 'inspector' +const USER_GROUP = process.env.USER_GROUP || 'G_SERVIDORES' + export class UserController { static async importUser(username: string) { const ldap = new LdapController() - const user = await ldap.getUser(username) + const ldapUser = await ldap.getUser(username) - if (!user) throw new Error('User not found!') + if (!ldapUser) throw new Error('User not found!') + + const user = { + username: ldapUser.username, + displayName: ldapUser.displayName, + mail: ldapUser.mail, + thumbnailPhoto: ldapUser.thumbnailPhoto, + roles: [] as User['roles'] + } + + ldapUser.groups?.forEach(group => { + if (group === USER_GROUP) user.roles?.push('USER') + if (group === ADMIN_GROUP) user.roles?.push('ADMIN') + if (group === INSPECTOR_GROUP) user.roles?.push('INSPECTOR') + }) return await prisma.user.upsert({ - where: { username: user.username }, + where: { username: ldapUser.username }, update: user, create: user }) diff --git a/src/controllers/routes/LoginRouteController.ts b/src/controllers/routes/LoginRouteController.ts new file mode 100644 index 0000000..ea2b777 --- /dev/null +++ b/src/controllers/routes/LoginRouteController.ts @@ -0,0 +1,18 @@ +import { Request, Response } from 'express' +import { AuthenticationController } from '../AuthenticationController.js' + +export class LoginRouteController { + static async login(req: Request, res: Response) { + const { username, password } = req.body + + if (!username || !password) + return res.status(400).json({ error: 'Missing username or password' }) + + try { + const token = await AuthenticationController.login(username, password) + res.json({ token }) + } catch (error: any) { + res.status(401).json({ error: error.message }) + } + } +} diff --git a/src/controllers/routes/UserRouteController.ts b/src/controllers/routes/UserRouteController.ts index 6efcfd9..0f2aa8f 100644 --- a/src/controllers/routes/UserRouteController.ts +++ b/src/controllers/routes/UserRouteController.ts @@ -13,7 +13,7 @@ export class UserRouteController { where: { username } }) - if (!user) return await UserController.importUser(username) + if (!user) return res.json(await UserController.importUser(username)) else UserController.importUser(username) res.json(user) diff --git a/src/server.ts b/src/server.ts index 91d49dd..93abfd7 100644 --- a/src/server.ts +++ b/src/server.ts @@ -7,6 +7,7 @@ import { hasRolesMiddleware } from './middleware/hasRolesMiddleware.js' import { UserRouteController } from './controllers/routes/UserRouteController.js' import { AuthenticationController } from './controllers/AuthenticationController.js' +import { LoginRouteController } from './controllers/routes/LoginRouteController.js' export const app = express() @@ -22,19 +23,7 @@ app.get('/api/', async (req: Request, res: Response) => { }) // Login route -app.post('/api/login', async (req: Request, res: Response) => { - const { username, password } = req.body - - if (!username || !password) - return res.status(400).json({ error: 'Missing username or password' }) - - try { - const token = await AuthenticationController.login(username, password) - res.json({ token }) - } catch (error: any) { - res.status(401).json({ error: error.message }) - } -}) +app.post('/api/login', LoginRouteController.login) app.get('/api/me', authMiddleware, async (req: Request, res: Response) => res.json(res.locals.user)