mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-09-11 05:46:28 +02:00
Adds API login
This commit is contained in:
@@ -7,7 +7,6 @@ const certbotRouter: Router = Router()
|
||||
certbotRouter.use((request: Request, response: Response, _next: NextFunction) => {
|
||||
const filename = request.originalUrl.replace(/.*\//, "")
|
||||
const resolvedPath: string = path.resolve(`../certbot/.well-known/acme-challenge/${filename}`)
|
||||
console.log("response", resolvedPath)
|
||||
response.setHeader("Content-Type", "text/html")
|
||||
return response.sendFile(resolvedPath)
|
||||
})
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { Envie, EnvieWithoutId } from "../services/envies"
|
||||
import { Envie, EnvieWithoutId } from "../../services/envies"
|
||||
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
EnvieWithoutId,
|
@@ -1,5 +1,5 @@
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { JeuJav, JeuJavWithoutId } from "../services/jeuxJav"
|
||||
import { JeuJav, JeuJavWithoutId } from "../../services/jeuxJav"
|
||||
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
JeuJavWithoutId,
|
@@ -1,5 +1,5 @@
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { Membre, MembreWithoutId } from "../services/membres"
|
||||
import { Membre, MembreWithoutId } from "../../services/membres"
|
||||
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
MembreWithoutId,
|
@@ -15,9 +15,10 @@ import devServer from "./devServer"
|
||||
import ssr from "./ssr"
|
||||
|
||||
import certbotRouter from "../routes/certbot"
|
||||
import { jeuJavListGet } from "../gsheets/jeuJav"
|
||||
import { envieListGet, envieAdd } from "../gsheets/envies"
|
||||
import { membreGet, membreSet } from "../gsheets/membres"
|
||||
import { jeuJavListGet } from "./gsheets/jeuJav"
|
||||
import { envieListGet, envieAdd } from "./gsheets/envies"
|
||||
import { membreGet, membreSet } from "./gsheets/membres"
|
||||
import signInHandler from "./userManagement/signIn"
|
||||
import config from "../config"
|
||||
|
||||
const app = express()
|
||||
@@ -42,7 +43,11 @@ app.use(express.static(path.resolve(process.cwd(), "public")))
|
||||
// Enable dev-server in development
|
||||
if (__DEV__) devServer(app)
|
||||
|
||||
// Google Sheets requests
|
||||
/**
|
||||
* APIs
|
||||
*/
|
||||
|
||||
// Google Sheets API
|
||||
app.use(express.json())
|
||||
app.get("/JeuJavListGet", jeuJavListGet)
|
||||
app.get("/EnvieListGet", envieListGet)
|
||||
@@ -50,6 +55,9 @@ app.get("/MembreGet", membreGet)
|
||||
app.post("/MembreSet", membreSet)
|
||||
app.post("/EnvieAdd", envieAdd)
|
||||
|
||||
// Sign in & up API
|
||||
app.post("/api/user/login", signInHandler)
|
||||
|
||||
// Use React server-side rendering middleware
|
||||
app.get("*", ssr)
|
||||
|
||||
|
56
src/server/userManagement/__tests__/signIn.tsx
Executable file
56
src/server/userManagement/__tests__/signIn.tsx
Executable file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
||||
import { signIn } from "../signIn"
|
||||
|
||||
// Could do a full test with wget --header='Content-Type:application/json' --post-data='{"email":"pikiou.sub@gmail.com","password":"mot de passe"}' http://localhost:3000/api/user/login
|
||||
|
||||
const mockUser = {
|
||||
mail: "my.email@gmail.com",
|
||||
passe: "$2y$10$cuKFHEow2IVSZSPtoVsw6uZFNFOOP/v1V7fubbyvrxhZdsnxLHr.2",
|
||||
prenom: "monPrénom",
|
||||
}
|
||||
|
||||
jest.mock("../../gsheets/accessors", () => () => ({
|
||||
listGet: () => [mockUser],
|
||||
}))
|
||||
|
||||
describe("signIn with", () => {
|
||||
it("right password", async () => {
|
||||
const res = await signIn("my.email@gmail.com", "12345678")
|
||||
expect(res).toEqual({
|
||||
membre: {
|
||||
prenom: mockUser.prenom,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("invalid password length", async () => {
|
||||
await expect(signIn("my.email@gmail.com", "11011")).rejects.toThrow("Mot de passe invalid")
|
||||
})
|
||||
|
||||
it("empty password", async () => {
|
||||
await expect(signIn("my.email@gmail.com", " ")).rejects.toThrow("Mot de passe invalid")
|
||||
})
|
||||
|
||||
it("wrong password", async () => {
|
||||
await expect(signIn("my.email@gmail.com", "1234567891011")).rejects.toThrow(
|
||||
"Mauvais mot de passe pour cet email"
|
||||
)
|
||||
})
|
||||
|
||||
it("invalid email format", async () => {
|
||||
await expect(signIn("my.email@gmail", "12345678")).rejects.toThrow("Email invalid")
|
||||
})
|
||||
|
||||
it("empty email", async () => {
|
||||
await expect(signIn(" ", "12345678")).rejects.toThrow("Email invalid")
|
||||
})
|
||||
|
||||
it("unknown email", async () => {
|
||||
await expect(signIn("mon.emailBidon@gmail.com", "12345678")).rejects.toThrow(
|
||||
"Cet email ne correspond à aucun utilisateur"
|
||||
)
|
||||
})
|
||||
})
|
55
src/server/userManagement/signIn.ts
Normal file
55
src/server/userManagement/signIn.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Request, Response, NextFunction } from "express"
|
||||
import bcrypt from "bcrypt"
|
||||
import { Membre, MemberLogin, emailRegexp, passwordMinLength } from "../../services/membres"
|
||||
import getAccessors from "../gsheets/accessors"
|
||||
|
||||
const { listGet } = getAccessors("Membres", new Membre())
|
||||
|
||||
export default async function signInHandler(
|
||||
request: Request,
|
||||
response: Response,
|
||||
_next: NextFunction
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (typeof request.body.email !== "string" || typeof request.body.password !== "string") {
|
||||
throw Error()
|
||||
}
|
||||
const res = await signIn(request.body.email, request.body.password)
|
||||
response.status(200).json(res)
|
||||
} catch (e: any) {
|
||||
if (e.message) {
|
||||
response.status(200).json({ error: e.message })
|
||||
} else {
|
||||
response.status(400).json(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function signIn(rawEmail: string, rawPassword: string): Promise<MemberLogin> {
|
||||
const email = rawEmail.replace(/^\s*/, "").replace(/\s*$/, "")
|
||||
if (!emailRegexp.test(email)) {
|
||||
throw Error("Email invalid")
|
||||
}
|
||||
|
||||
const password = rawPassword.replace(/^\s*/, "").replace(/\s*$/, "")
|
||||
if (password.length < passwordMinLength) {
|
||||
throw Error("Mot de passe invalid")
|
||||
}
|
||||
|
||||
const membres: Membre[] = await listGet()
|
||||
const membre = membres.find((m) => m.mail === email)
|
||||
if (!membre) {
|
||||
throw Error("Cet email ne correspond à aucun utilisateur")
|
||||
}
|
||||
|
||||
const passwordMatch = await bcrypt.compare(password, membre.passe.replace(/^\$2y/, "$2a"))
|
||||
if (!passwordMatch) {
|
||||
throw Error("Mauvais mot de passe pour cet email")
|
||||
}
|
||||
|
||||
return {
|
||||
membre: {
|
||||
prenom: membre.prenom,
|
||||
},
|
||||
}
|
||||
}
|
@@ -28,6 +28,17 @@ export class Membre {
|
||||
passe = ""
|
||||
}
|
||||
|
||||
export const emailRegexp =
|
||||
/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i
|
||||
export const passwordMinLength = 4
|
||||
|
||||
export interface MemberLogin {
|
||||
membre?: {
|
||||
prenom: string
|
||||
}
|
||||
error?: string
|
||||
}
|
||||
|
||||
export type MembreWithoutId = Omit<Membre, "id">
|
||||
|
||||
export const membreGet = get<Membre>("Membre")
|
||||
|
Reference in New Issue
Block a user