added registration

This commit is contained in:
Vomitblood 2025-01-14 02:39:24 +08:00
parent 0edb234718
commit 9e466635de
11 changed files with 153 additions and 44 deletions

View file

@ -19,6 +19,9 @@
},
{
"url": "https://*"
},
{
"url": "http://localhost:5000"
}
],
"identifier": "http:default"

View file

@ -33,15 +33,12 @@ export const ServerStatus = () => {
try {
const response = await fetch(serverUrl + "/health");
if (response.ok) {
console.log("connected");
setServerConnection("connected");
} else {
console.log("disconnected");
await sleep(500);
setServerConnection("disconnected");
}
} catch (e) {
console.log("disconnected", e);
await sleep(500);
setServerConnection("disconnected");
}
@ -60,13 +57,11 @@ export const ServerStatus = () => {
useEffect(() => {
// only start interval if server is connected
if (serverConnection === "connected") {
const intervalId = setInterval(checkServerConnection, 2000);
console.log("Started server ping interval");
const intervalId = setInterval(checkServerConnection, 5000);
// cleanup interval on disconnection or component unmount
return () => {
clearInterval(intervalId);
console.log("Stopped server ping interval");
};
}
}, [serverConnection, serverUrl]);
@ -82,7 +77,7 @@ export const ServerStatus = () => {
>
{serverConnection === "connecting" && (
<CircularProgress
size='20px'
size="20px"
sx={{ mr: 1 }}
/>
)}
@ -90,7 +85,7 @@ export const ServerStatus = () => {
<Chip
{...chipProps}
onClick={clickEvent}
size='small'
size="small"
/>
<Popover
anchorEl={anchorEl}
@ -124,15 +119,15 @@ export const ServerStatus = () => {
}}
>
<Stack
alignItems='center'
display='flex'
direction='row'
alignItems="center"
display="flex"
direction="row"
spacing={1}
>
<ServerUrlInput />
<Button
type='submit'
variant='outlined'
type="submit"
variant="outlined"
>
Connect
</Button>

View file

@ -9,7 +9,7 @@ export const SqlInjection = () => {
const theme = useTheme();
// states
const [subTabValue, setSubTabValue] = useState("style");
const [subTabValue, setSubTabValue] = useState("register");
// logic for switching tabs
const subTabChangeEvent = (newTabValue: string) => {

View file

@ -4,18 +4,21 @@ import { fetch } from "@tauri-apps/plugin-http";
import { useAtom } from "jotai";
import { useState } from "react";
import { serverUrlAtom } from "../../../lib/jotai";
import { sleep } from "../../../lib/utility";
import { HeaderLogo } from "../../Generic/HeaderLogo";
import { useNotification } from "../../../contexts/NotificationContext";
export const SqlInjectionRegister = () => {
// contexts
const { openNotification } = useNotification();
// atoms
const [serverUrl, setServerUrl] = useAtom(serverUrlAtom);
// states
const [emailValueRaw, setEmailValueRaw] = useState<string>("");
const [errorMsg, setErrorMsg] = useState<string>("");
const [passwordValueRaw, setPasswordValueRaw] = useState<string>("");
const [rePasswordValueRaw, setRePasswordValueRaw] = useState<string>("");
const [passwordErrorMsg, setPasswordErrorMsg] = useState<string>("");
const [passwordStrength, setPasswordStrength] = useState(0);
const [passwordStrengthColor, setPasswordStrengthColor] = useState<"primary" | "error" | "warning" | "success">(
"primary",
@ -86,16 +89,45 @@ export const SqlInjectionRegister = () => {
};
const nextClickEvent = async () => {
// make a request to the server
setRegisterLoading(true);
// remove trailing slash
// reset the error messages
setErrorMsg("");
// ensure that the server url does not end with a trailing slash
setServerUrl(serverUrl.replace(/\/$/, ""));
// construct the request body
const requestBody = {
email: emailValueRaw,
password: passwordValueRaw,
rePassword: rePasswordValueRaw,
};
// start loading indicator
setRegisterLoading(true);
try {
const response = await fetch(serverUrl + "/sql-register");
console.log;
// make request good
const response = await fetch(serverUrl + "/register-sql", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
});
// check if registration was successful
if (!response.ok) {
const errorMessage = await response.text();
console.log("Registration failed:", errorMessage);
setErrorMsg(errorMessage);
} else {
openNotification("Registration successful");
}
} catch (e) {
console.log("failed", e);
await sleep(500);
// Log the error and handle failure
console.log("Request failed", e);
} finally {
// Stop loading indicator regardless of success/failure
setRegisterLoading(false);
}
};
@ -133,6 +165,7 @@ export const SqlInjectionRegister = () => {
}}
>
<TextField
error={Boolean(errorMsg)}
fullWidth
id="email"
label="Email"
@ -144,9 +177,8 @@ export const SqlInjectionRegister = () => {
variant="outlined"
/>
<TextField
error={Boolean(passwordErrorMsg)}
error={Boolean(errorMsg)}
fullWidth
helperText={Boolean(passwordErrorMsg) ? passwordErrorMsg : ""}
id="password"
label="Password"
onChange={(e: { target: { value: string } }) => passwordInputEvent(e.target.value)}
@ -157,9 +189,9 @@ export const SqlInjectionRegister = () => {
variant="outlined"
/>
<TextField
error={Boolean(passwordErrorMsg)}
error={Boolean(errorMsg)}
fullWidth
helperText={Boolean(passwordErrorMsg) ? passwordErrorMsg : ""}
helperText={Boolean(errorMsg) ? errorMsg : ""}
id="re-password"
label="Re-enter password"
onChange={(e: { target: { value: string } }) => rePasswordInputEvent(e.target.value)}

View file

@ -17,7 +17,10 @@ export const Testing = () => {
const close = () => setOpenState(false);
const testing = () => {
fetch("https://ip.vomitblood.com/ping").then((response) => {
fetch("http://localhost:5000/nuke-db").then((response) => {
console.log(response);
});
fetch("http://localhost:5000/setup-demo-db").then((response) => {
console.log(response);
});
};
@ -53,14 +56,14 @@ export const Testing = () => {
openButton={
<IconButton
onClick={() => setOpenState(true)}
size='small'
size="small"
>
<BugReportOutlined />
</IconButton>
}
openState={openState}
setMaximisedState={setMaximisedState}
title='Testing'
title="Testing"
/>
);
};

View file

@ -10,3 +10,5 @@ services:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:

View file

@ -7,7 +7,7 @@ require (
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.1 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
)

View file

@ -13,9 +13,15 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -21,7 +21,6 @@ const (
)
var DbPool *pgxpool.Pool
var allowedUsernames map[string]bool
// initialize connection to db
func ConnectToDb() (*pgxpool.Pool, error) {
@ -80,18 +79,18 @@ func SetupDemoDb(w http.ResponseWriter, r *http.Request) {
createTableSQL := `
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) NOT NULL,
password VARCHAR(100) NOT NULL
password VARCHAR(100) NOT NULL,
role VARCHAR(50) DEFAULT 'user'
);`
// also avoid duplicate entries
// avoid duplicate entries and specify roles
insertDataSQL := `
INSERT INTO users (username, email, password) VALUES
('alice', 'alice@example.com', 'asdfalicepassword'),
('bob', 'bob@example.com', 'asdfbobpassword'),
('charlie', 'charlie@example.com', 'asdfcharliepassword')
ON CONFLICT (username) DO NOTHING;`
INSERT INTO users (email, password, role) VALUES
('alice@example.com', 'asdfalicepassword', 'user'),
('bob@example.com', 'asdfbobpassword', 'user'),
('charlie@example.com', 'asdfcharliepassword', 'admin')
`
// execute create table
_, err := DbPool.Exec(context.Background(), createTableSQL)

View file

@ -24,6 +24,7 @@ func ServeApi() {
http.HandleFunc("/execute-sql", sql_injection.ExecuteSql)
http.HandleFunc("/login-sql", sql_injection.LoginSql)
http.HandleFunc("/secure-execute-sql", sql_injection.SecureExecuteSql)
http.HandleFunc("/register-sql", sql_injection.RegisterSql)
http.HandleFunc("/secure-login-sql", sql_injection.SecureLoginSql)
http.HandleFunc("/secure-get-user", sql_injection.SecureGetUser)
log.Println("Server is running on http://localhost:5000")

View file

@ -7,9 +7,11 @@ import (
"io"
"log"
"net/http"
"regexp"
"strings"
"github.com/Vomitblood/cspj-application/server/internal/db"
"golang.org/x/crypto/bcrypt"
)
// unsecure version
@ -147,6 +149,72 @@ func SecureExecuteSql(w http.ResponseWriter, r *http.Request) {
w.Write(jsonResp)
}
// register endpoint
func RegisterSql(w http.ResponseWriter, r *http.Request) {
// read the request body
var credentials struct {
Email string `json:"email"`
Password string `json:"password"`
RePassword string `json:"rePassword"`
}
if err := json.NewDecoder(r.Body).Decode(&credentials); err != nil {
http.Error(w, "Invalid request format", http.StatusBadRequest)
return
}
defer r.Body.Close()
// check if the email is an email using regex, if not reject
emailRegex := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
re := regexp.MustCompile(emailRegex)
if !re.MatchString(credentials.Email) {
http.Error(w, "Invalid email format", http.StatusBadRequest)
return
}
// check if the password matches, if not reject
if credentials.Password != credentials.RePassword {
http.Error(w, "Passwords do not match", http.StatusBadRequest)
return
}
// get the number of emails that matches
var existingUserCount int
emailCheckSQL := `SELECT COUNT(*) FROM users WHERE email = $1`
err := db.DbPool.QueryRow(context.Background(), emailCheckSQL, credentials.Email).Scan(&existingUserCount)
if err != nil {
http.Error(w, "Error checking email in the database", http.StatusInternalServerError)
log.Printf("Error checking email: %v", err)
return
}
// if there is more than 0 matches, that means email already exists, reject
if existingUserCount > 0 {
http.Error(w, "Email already exists", http.StatusConflict)
return
}
// hash the password
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(credentials.Password), bcrypt.DefaultCost)
if err != nil {
http.Error(w, "Error hashing password", http.StatusInternalServerError)
return
}
// over here the validations has passed, so insert into the db
insertSQL := `INSERT INTO users (email, password, role) VALUES ($1, $2, $3)`
_, err = db.DbPool.Exec(context.Background(), insertSQL, credentials.Email, hashedPassword, "user")
if err != nil {
http.Error(w, "Error inserting user into the database", http.StatusInternalServerError)
log.Printf("Error inserting user: %v", err)
return
}
// send back status ok
w.WriteHeader(http.StatusOK)
w.Write([]byte("User registered successfully"))
log.Println("User registered successfully:", credentials.Email)
}
// secure login
func SecureLoginSql(w http.ResponseWriter, r *http.Request) {
var credentials struct {