Adds API login

This commit is contained in:
pikiou
2021-11-26 05:10:48 +01:00
parent 051784a6da
commit f952e24884
13 changed files with 228 additions and 14 deletions

View File

@@ -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)
})

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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)

View 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"
)
})
})

View 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,
},
}
}

View File

@@ -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")