Authentification Token

This commit is contained in:
2024-11-14 15:32:28 +01:00
parent f4d5f54846
commit c61a628ed4
10 changed files with 283 additions and 58 deletions

View File

@@ -0,0 +1,22 @@
import { NextFunction, Request, Response } from "express";
import jwt from "jsonwebtoken"
export function verifyToken(req: Request, res: Response, next: NextFunction) {
const token = req.header("Authorization")
if(!token) {
return res.status(401).json({
error: "Access denied"
})
}
try {
const decoded = jwt.verify(token, 'sjcucjdkdf')
req["id"] = decoded["userId"]
next()
} catch(error) {
res.status(401).json({
error: "Invalid token"
})
}
}

View File

@@ -6,6 +6,8 @@ import { Payment } from "../models/user/payment.model";
import { AccountRole } from "../models/user/accountRole.model";
import { Exercise } from "../models/exercises/exercise.model";
import { sequelize } from "../database";
import jwt from "jsonwebtoken"
import { verifyToken } from "../middlewares/auth.middleware";
export const account = Router()
@@ -19,30 +21,36 @@ account.get("/", (req: Request, res: Response) => {
})
// Login user
account.post("/login", async (req: Request, res: Response) => {
account.get("/login", async (req: Request, res: Response) => {
// Using raw SQL code for SQL injections!
const [results, metadata] =
await sequelize.query(
"SELECT * FROM Accounts " +
"WHERE (username='" + req.body.username +
"' AND password='" + req.body.password + "')")
"WHERE (username='" + req.query.username +
"' AND password='" + req.query.password + "')"
)
// Mechanism to check exercise solved
if (results.length > 1) {
Exercise.update(
{ solved: true },
{
where: {
nameEn: "Register"
}
{ solved: true },
{
where: {
nameEn: "Register"
}
)
}
)
}
if (results.length != 0) {
// Creating session token
const token = jwt.sign({ userId: results[0]["id"] }, 'sjcucjdkdf')
// Status: 200 OK
res.status(200).json(results[0])
res.status(200).json({
"success": true,
"token": token
})
} else {
// Status: 401 Unauthorized
res.status(401).json({
@@ -52,6 +60,20 @@ account.post("/login", async (req: Request, res: Response) => {
}
})
account.get("/account", verifyToken, async(req: Request, res: Response) => {
Account.findOne({
where: {
id: req["id"]
},
include: [ Address, AccountRole, Payment ]
})
.then(account => {
res.status(200).json(account)
})
})
// Creating a new user
account.post("/", async (req: Request, res: Response) => {
// Check if username is valid
@@ -97,26 +119,38 @@ account.post("/", async (req: Request, res: Response) => {
})
})
account.patch("/", (req: Request, res: Response) => {
account.patch("/", verifyToken, (req: Request, res: Response) => {
Account.update(req.body,
{
where: { id: req.body.id }
})
.then(async account => {
for (let payment of req.body.payments) {
await Payment.update(payment,
{
where: { id: payment.id }
}
)
if (payment.id == undefined) {
payment["accountId"] = req.body.id
await Payment.create(payment)
} else {
await Payment.update(payment,
{
where: { id: payment.id }
}
)
}
}
for (let address of req.body.addresses) {
await Address.update(address,
{
where: { id: address.id }
}
)
if (address.id == undefined) {
address["accountId"] = req.body.id
await Address.create(address)
} else {
await Address.update(address,
{
where: { id: address.id }
}
)
}
}
// Status: 200 OK

View File

@@ -17,6 +17,7 @@
"cors": "^2.8.5",
"electron-squirrel-startup": "^1.0.1",
"express": "^4.21.1",
"jsonwebtoken": "^9.0.2",
"moment": "^2.30.1",
"multer": "^1.4.5-lts.1",
"pinia": "^2.2.4",
@@ -36,6 +37,7 @@
"@electron/fuses": "^1.8.0",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.7",
"@types/node": "^22.7.6",
"@vitejs/plugin-vue": "^5.1.4",
"concurrently": "^9.0.1",
@@ -2096,6 +2098,16 @@
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
"license": "MIT"
},
"node_modules/@types/jsonwebtoken": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz",
"integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/keyv": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
@@ -3223,6 +3235,12 @@
"node": "*"
}
},
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
"license": "BSD-3-Clause"
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -4424,6 +4442,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -5987,6 +6014,55 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/jsonwebtoken": {
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
"license": "MIT",
"dependencies": {
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
"lodash.isnumber": "^3.0.3",
"lodash.isplainobject": "^4.0.6",
"lodash.isstring": "^4.0.1",
"lodash.once": "^4.0.0",
"ms": "^2.1.1",
"semver": "^7.5.4"
},
"engines": {
"node": ">=12",
"npm": ">=6"
}
},
"node_modules/jsonwebtoken/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
"integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jws": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
"license": "MIT",
"dependencies": {
"jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -6084,13 +6160,47 @@
"license": "MIT",
"peer": true
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
"license": "MIT"
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
"license": "MIT"
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
"license": "MIT"
},
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
"license": "MIT"
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
"license": "MIT"
},
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
"license": "MIT"
},
"node_modules/lodash.union": {
"version": "4.6.0",

View File

@@ -41,6 +41,7 @@
"cors": "^2.8.5",
"electron-squirrel-startup": "^1.0.1",
"express": "^4.21.1",
"jsonwebtoken": "^9.0.2",
"moment": "^2.30.1",
"multer": "^1.4.5-lts.1",
"pinia": "^2.2.4",
@@ -60,6 +61,7 @@
"@electron/fuses": "^1.8.0",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.7",
"@types/node": "^22.7.6",
"@vitejs/plugin-vue": "^5.1.4",
"concurrently": "^9.0.1",

View File

@@ -13,7 +13,13 @@ exerciseStore.getAllExercises()
<template>
<v-btn variant="plain" icon="mdi-magnify" to="/search" />
<v-btn v-if="accountStore.userAccount.id == undefined" variant="plain" icon="mdi-account" to="/account/login" />
<v-btn
v-if="accountStore.userAccountToken == ''"
variant="plain"
icon="mdi-account"
to="/account/login"
/>
<v-btn v-else variant="plain" icon="mdi-account-check" to="/account/home" />
<div>
@@ -27,7 +33,7 @@ exerciseStore.getAllExercises()
</div>
<v-btn
v-if="accountStore.userAccount.accountRole != null &&
v-if="accountStore.userAccountToken != '' &&
accountStore.userAccount.accountRole.privilegeAdminPanel"
variant="plain"
icon="mdi-table-cog"

View File

@@ -7,10 +7,15 @@ export async function fetchAllAccounts() {
return await axios.get(BASE_URL)
}
export async function loginAccount(username: string, password: string) {
return await axios.post(BASE_URL + "/login", {
username: username,
password: password
export async function login(username: string, password: string) {
return await axios.get(BASE_URL + "/login?username=" + username + "&password=" + password)
}
export async function getAccount(token: string) {
return await axios.get(BASE_URL + "/account", {
headers: {
"Authorization": token
}
})
}
@@ -18,8 +23,12 @@ export async function registerAccount(account: AccountModel) {
return await axios.post(BASE_URL, account)
}
export async function updateAccount(account: AccountModel) {
return await axios.patch(BASE_URL, account)
export async function updateAccount(account: AccountModel, token: string) {
return await axios.patch(BASE_URL, account, {
headers: {
"Authorization": token
}
})
}
export async function deleteAccount(account: AccountModel) {

View File

@@ -11,7 +11,7 @@ const router = useRouter()
async function startLogin() {
accountStore.login()
.then(result => {
if (accountStore.userAccount.id != undefined) {
if (accountStore.userAccountToken != "") {
router.push("/account/home")
}
})
@@ -30,6 +30,7 @@ async function startLogin() {
:label="$t('account.userData.username')"
prepend-icon="mdi-account"
v-model="accountStore.loginData.username"
variant="outlined"
clearable
/>
</v-col>
@@ -41,6 +42,7 @@ async function startLogin() {
:label="$t('account.userData.password')"
prepend-icon="mdi-key"
type="password"
variant="outlined"
v-model="accountStore.loginData.password"
clearable
/>

View File

@@ -31,6 +31,8 @@ async function registerAccount() {
prepend-icon="mdi-account"
v-model="accountStore.registerData.username"
clearable
hide-details
variant="outlined"
:rules="getStringRules()"
/>
</v-col>
@@ -44,6 +46,8 @@ async function registerAccount() {
type="password"
v-model="accountStore.registerData.password"
clearable
hide-details
variant="outlined"
:rules="getPasswordRules()"
/>
</v-col>
@@ -56,6 +60,8 @@ async function registerAccount() {
prepend-icon="mdi-mail"
v-model="accountStore.registerData.email"
:rules="getEmailRules()"
variant="outlined"
hide-details
clearable
/>
</v-col>

View File

@@ -15,15 +15,13 @@ export function generateResultsPdf() {
const exerciseData = []
exerciseStore.exerciseGroups.forEach(group => {
group.exercises.forEach(exercise => {
exerciseData.push([
group.groupNr + "." + exercise.exerciseNr,
group.nameDe,
exercise.nameDe,
exercise.solved ? 'Ja' : 'Nein'
])
})
exerciseStore.exercises.forEach(exercise => {
exerciseData.push([
exercise.exerciseGroup.groupNr + "." + exercise.exerciseNr,
exercise.exerciseGroup.nameDe,
exercise.nameDe,
exercise.solved ? 'Ja' : 'Nein'
])
})
// Title and image
@@ -37,11 +35,9 @@ export function generateResultsPdf() {
// Progress total
doc.setFontSize(28)
doc.text("Hat " + exerciseStore.exerciseGroups.reduce((counter, group) => {
for (let exercise of group.exercises) {
if (exercise.solved) {
counter++
}
doc.text("Hat " + exerciseStore.exercises.reduce((counter, exercise) => {
if (exercise.solved) {
counter++
}
return counter
@@ -57,10 +53,19 @@ export function generateResultsPdf() {
})
// Footer
doc.setFontSize(12)
doc.text(["Grundlagen der IT-Sicherheit", "Fachgebiet Usable Security and Privacy", "Institut für IT-Sicherheit", "Leibniz Universität Hannover"], midPage, pageHeight - 30, { align: "center" })
doc.text(
[
"Grundlagen der IT-Sicherheit",
"Fachgebiet Usable Security and Privacy",
"Institut für IT-Sicherheit", "Leibniz Universität Hannover"
],
midPage, pageHeight - 30, { align: "center" }
)
doc.text(moment().format("DD.MM.YYYY, HH:mm:ss"), midPage, pageHeight - 8, { align: "center" })
// Save
doc.save("eventmaster-exercise-result.pdf")
}

View File

@@ -1,7 +1,7 @@
import { useLocalStorage } from "@vueuse/core";
import { AccountModel } from "../data/models/user/accountModel";
import { useFeedbackStore } from "./feedback.store";
import { deleteAccount, fetchAllAccounts, loginAccount, registerAccount, updateAccount } from "../data/api/accountApi";
import { deleteAccount, fetchAllAccounts, getAccount, login, registerAccount, updateAccount } from "../data/api/accountApi";
import { fetchUserOrders } from "../data/api/orderApi";
import { BannerStateEnum } from "../data/enums/bannerStateEnum";
import { AddressModel } from "../data/models/user/addressModel";
@@ -17,10 +17,11 @@ export const useAccountStore = defineStore("accountStore", {
accounts: ref<Array<AccountApiModel>>([]),
/** Useraccount which is currently logged in */
userAccountToken: useLocalStorage("hackmycart/accountStore/userAccountToken", ""),
userAccount: useLocalStorage("hackmycart/accountStore/userAccount", new AccountApiModel()),
/** User input on login screen */
// todo: Remove JSON!
loginData: ref<{ username: String, password: String}>(
{ username: "", password: "" }
),
@@ -62,15 +63,36 @@ export const useAccountStore = defineStore("accountStore", {
}
else
{
await loginAccount(this.loginData.username, this.loginData.password)
await login(this.loginData.username, this.loginData.password)
.then(async result => {
this.userAccount = result.data
this.userAccountToken = result.data.token
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTLOGINSUCCESSFUL)
getAccount(this.userAccountToken)
.then(account => {
this.userAccount = account.data
this.fetchInProgress = false
return true
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTLOGINSUCCESSFUL)
this.fetchInProgress = false
})
})
// await loginAccount(this.loginData.username, this.loginData.password)
// .then(async result => {
// this.userAccountId = result.data.id
// this.userLoggedIn = true
// fetchAddresses(result.data.id)
// .then(addresses => {
// })
// feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTLOGINSUCCESSFUL)
// this.fetchInProgress = false
// return true
// })
.catch(error => {
if (error.status == 400) {
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTLOGINERROR)
@@ -129,10 +151,12 @@ export const useAccountStore = defineStore("accountStore", {
async updateAccount() {
const feedbackStore = useFeedbackStore()
await updateAccount(this.userAccount)
await updateAccount(this.userAccount, this.userAccountToken)
.then(res => {
if (res.status == 200) {
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTUPDATESUCCESSFUL)
this.userAccount = res.data
}
})
},
@@ -144,6 +168,7 @@ export const useAccountStore = defineStore("accountStore", {
const feedbackStore = useFeedbackStore()
this.userAccount = new AccountModel()
this.userAccountId = -1
this.loggedIn = false
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTLOGOUTSUCCESSFUL)
@@ -162,6 +187,10 @@ export const useAccountStore = defineStore("accountStore", {
})
},
async getAdresses() {
},
/**
* Remove an address from the user model
*