Skip to content

Commit 7decb39

Browse files
committed
GH-2 # add frontend
submitPassword cannot be referenced by the HTML
1 parent 3b3dea8 commit 7decb39

9 files changed

+126
-8
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
**/bundle.js
2+
package-lock.json

README.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Une CI basique sera mise en place. Uniquement la branche master sera utilisée.
1515

1616
Les conventions de code suivi sont:
1717
* [PEP8](https://pep8.org/) pour Python
18-
* TODO
18+
* [Airbnb](https://github.com/airbnb/javascript) pour JavaScript
1919

2020
### Partie fuite de mot de passe
2121

@@ -59,6 +59,9 @@ J'utilise un outils de profiling (en l'occurence Sentry car je l'avais sous la m
5959

6060
Plusieurs solutions sont envisagées. La première est de pousser les données en batch, ce qui nécessite avec `sadd` de pousser plusieurs password qui ont le même prefix. Cette solution parait compliqué à mettre en place, car elle oblige a stocker beaucoups d'informations en mémoire. D'autres solutions comme utiliser de l'asynchrone, ou encore du multiprocessing implique une forte complexité supplémentaire. Par exemple découper le fichier en entrée en une centaine de mini fichiers pour être traité par des "workers" différents. Heureusement Redis fournit un système de batch de commande via [les pipelines](https://redis.io/docs/latest/develop/use/pipelining/) qui permet d'avoir des commandes différentes dans un seul appel. Après quelques essaie, des batchs de 200 commandes semble être le bon compromis qui permet de faire 100k password en 3s, ce qui est plus acceptable.
6161

62+
---
63+
64+
Pour la partie frontend, je n'ai pas réussie à mon grand regret à utiliser la librairie npm dans le navigateur d'une manière compatible avec Jest. Je me suis rabattu sur `browserify` puis sur `esbuild`.
6265

6366
### Partie génération de mot de passe
6467

@@ -78,7 +81,8 @@ TODO
7881

7982
### Setup dev
8083

81-
`poetry sync` permet d'installer les dépendances, justfile (voir [ici pour l'installation](https://github.com/casey/just?tab=readme-ov-file#cross-platform)) permet de faire tourner les tests et le linter.
84+
`poetry sync` permet d'installer les dépendances, justfile (voir [ici pour l'installation](https://github.com/casey/just?tab=readme-ov-file#cross-platform)) permet de faire tourner les tests et le linter.
85+
`npm install` permet d'installer les dépendances coté frontend.
8286

8387
## Limite et améliorations possible
8488

package.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"type": "module",
3+
"scripts": {
4+
"build": "esbuild src/static/check_password_leak.js --bundle --outfile=src/static/bundle.js",
5+
"test": "node --experimental-vm-modules node_modules/.bin/jest"
6+
},
7+
"devDependencies": {
8+
"jest": "^29.7.0"
9+
},
10+
"dependencies": {
11+
"esbuild": "^0.24.2",
12+
"xxhash-wasm": "^1.1.0"
13+
}
14+
}

src/api/main.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1+
from os import path
2+
13
from fastapi import FastAPI
2-
from starlette.responses import RedirectResponse
4+
from starlette.staticfiles import StaticFiles
35

46
from .router import v1_router
57

8+
STATIC_DIRECTORY_PATH = path.join(path.dirname(path.dirname(path.abspath(__file__))), "static")
9+
610
app = FastAPI()
711
app.include_router(v1_router)
8-
9-
10-
@app.get("/")
11-
async def root() -> RedirectResponse:
12-
return RedirectResponse(url="/docs")
12+
app.mount("/", StaticFiles(directory=STATIC_DIRECTORY_PATH, html=True), name="static")

src/static/check_password_leak.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import xxhash from "xxhash-wasm";
2+
3+
function submitPassword() {
4+
let password = document.getElementById("submittedPassword").value;
5+
hashPrefix(password).then(prefix => alert(prefix));
6+
}
7+
8+
async function hashPrefix(password) {
9+
const { h32, h64 } = await xxhash();
10+
let digest = h32(password);
11+
let hexadecimal_digest = digest.toString(16)
12+
return hexadecimal_digest.substring(0, 5)
13+
}
14+
15+
export {hashPrefix};

src/static/favicon.ico

1.37 KB
Binary file not shown.

src/static/index.html

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>HIBP - clone</title>
6+
<link href="main.css" rel="stylesheet"/>
7+
<script type="module" src="bundle.js"></script>
8+
</head>
9+
<body>
10+
<div class="sidenav">
11+
<a href="/">Test de la fuite de mot de passe</a> <!-- TODO highlight current page -->
12+
<a href="/">Génération de mot de passe</a>
13+
<a href="/docs">Documentation OpenAPI</a>
14+
</div>
15+
16+
<div class="content">
17+
<h2>Test de la fuite de mot de passe</h2>
18+
<p>Cette page permet de tester si votre mot de passe est présent dans la base <a
19+
href="https://en.wikipedia.org/wiki/RockYou#Data_breach" target="_blank">RockYou</a>.</p>
20+
<input id=submittedPassword/>
21+
<input type="button" onclick="submitPassword()" value="Tester">
22+
<p>Le mot de passe n'est pas envoyé au serveur, uniquement les premiers caractères de son hash.</p>
23+
</div>
24+
25+
<script>
26+
document.addEventListener("keyup", ({key}) => {
27+
if (key === "Enter") {
28+
submitPassword()
29+
}
30+
})
31+
</script>
32+
</body>
33+
</html>

src/static/main.css

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* Repris de https://www.w3schools.com/css/tryit.asp?filename=trycss_template4 */
2+
3+
* {
4+
box-sizing: border-box;
5+
}
6+
7+
body {
8+
margin: 0;
9+
font-family: Arial, Helvetica, sans-serif;
10+
}
11+
12+
/* Style the side navigation */
13+
.sidenav {
14+
height: 100%;
15+
width: 200px;
16+
position: fixed;
17+
z-index: 1;
18+
top: 0;
19+
left: 0;
20+
background-color: #111;
21+
overflow-x: hidden;
22+
}
23+
24+
25+
/* Side navigation links */
26+
.sidenav a {
27+
color: white;
28+
padding: 16px;
29+
text-decoration: none;
30+
display: block;
31+
}
32+
33+
/* Change color on hover */
34+
.sidenav a:hover {
35+
background-color: #ddd;
36+
color: black;
37+
}
38+
39+
/* Style the content */
40+
.content {
41+
margin-left: 200px;
42+
padding-left: 20px;
43+
}

src/static_test/hashPrefix.test.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { hashPrefix } from "../static/check_password_leak.js";
2+
3+
test('hash prefix is identical to Python version', async () => {
4+
let prefix = await hashPrefix("rockyou");
5+
6+
expect(prefix).toBe("5c283");
7+
});

0 commit comments

Comments
 (0)