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) {
@ -79,19 +78,19 @@ func SetupDemoDb(w http.ResponseWriter, r *http.Request) {
// create table and insert demo data
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
id SERIAL PRIMARY KEY,
email 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 {