diff --git a/package-lock.json b/package-lock.json index 5807647..bdd0e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -13982,6 +13983,15 @@ "node": ">=10.13.0" } }, + "node_modules/sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==", + "funding": { + "type": "individual", + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -26196,6 +26206,11 @@ "stable": "^0.1.8" } }, + "sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package-lock.json b/package-lock.json index 5807647..bdd0e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -13982,6 +13983,15 @@ "node": ">=10.13.0" } }, + "node_modules/sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==", + "funding": { + "type": "individual", + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -26196,6 +26206,11 @@ "stable": "^0.1.8" } }, + "sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 2c9a675..4b2ebf5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/package-lock.json b/package-lock.json index 5807647..bdd0e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -13982,6 +13983,15 @@ "node": ">=10.13.0" } }, + "node_modules/sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==", + "funding": { + "type": "individual", + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -26196,6 +26206,11 @@ "stable": "^0.1.8" } }, + "sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 2c9a675..4b2ebf5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/src/app/account.service.ts b/src/app/account.service.ts index 2b64b6a..7f602df 100644 --- a/src/app/account.service.ts +++ b/src/app/account.service.ts @@ -3,6 +3,7 @@ import { environment } from 'src/environments/environment'; import { sha256 } from 'sha.js'; import { Router } from '@angular/router'; +import Swal from 'sweetalert2'; class Answer { success: Boolean = false @@ -32,14 +33,17 @@ }) export class AccountService { public username = '?'; - private userId: bigint = BigInt(0); - private userToken: bigint = BigInt(0); + private callbacks: Function[] = [] + + subscribe(f: Function) { + this.callbacks.push(f) + } constructor(private http: HttpClient, private router: Router) { this.updateUserInfo() } - public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + getPresalt(then: Function, onFail: Function = () => {}, onConnectionError: Function = () => {}, username: string = this.username) { this.http.post(`${environment.apiURL}/users/presalt`, {username}) .subscribe((answer: Object) => { var presaltAnswer = Object.assign(new PresaltAnswer(), answer) @@ -47,50 +51,86 @@ onFail() return } - // this presalting and hashing is done to protect the user's password in case of a man-in-the-middle-attack, - // where an attacker might be able to intercept a password if not hashed before. The password still has to be - // hashed on the server-side to be sure but this method prevents dumb users from having their passweord - // they use everywhere easily leaked, because you still need to crack this one layer first - // also, typical automated hacking software will hopefully be confused by my weird code - var passwordHash = new sha256().update(password + presaltAnswer.presalt).digest('hex') + then(presaltAnswer.presalt) + }, () => { + onConnectionError() + }) + } + + public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + this.setUser('') + localStorage.removeItem('userToken') + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(password + presalt).digest('hex') this.http.post(`${environment.apiURL}/users/authenticate`, {username, passwordHash}) .subscribe((answer) => { var authenticateAnswer = Object.assign(new AuthenticateAnswer(), answer) - var loginSuccess = authenticateAnswer.success == true + var loginSuccess = authenticateAnswer.success == true && authenticateAnswer.token.length > 0 if (loginSuccess) { localStorage.setItem('userToken', authenticateAnswer.token) + this.setUser(username) this.updateUserInfo() - this.username = username this.router.navigate(['admin']) } else { onFail() } - }, (error) => {onConnectionError()}) - }, (error) => {onConnectionError()}) + }, () => {onConnectionError()}) + }, onFail, onConnectionError, username) } - updateUserInfo() { + updateUserInfo(then: Function = () => {}) { var token = localStorage.getItem('userToken') if (token == null) { - this.username = '' + this.setUser('') return } - this.http.post(`${environment.apiURL}/users/checkToken`, {token}).subscribe((answer: Object) => { + var data = JSON.parse(atob(token?.split('.')[1] as string)) + var tokenData = Object.assign(new TokenData(), data) + this.setUser(tokenData.usr) + this.http.post(`${environment.apiURL}/users/checkToken`, {token, username: this.username}) + .subscribe((answer: Object) => { var checkTokenAnswer = Object.assign(new CheckTokenAnswer(), answer) - if (checkTokenAnswer.valid == true) { - var data = JSON.parse(atob(token?.split('.')[1] as string)) - var tokenData = Object.assign(new TokenData(), data) - this.username = tokenData.usr + if (checkTokenAnswer.valid) { } else { - this.username = '' + this.router.navigate(['/']) + this.setUser('') } + then() }, () => { - this.username = '' + this.router.navigate(['/']) + this.setUser('') }) } logout() { - this.username = '' + this.setUser('') localStorage.removeItem('userToken') } + + changePassword(newPassword: string, then: Function, onConnectionError: Function) { + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(newPassword + presalt).digest('hex') + this.http.post(`${environment.apiURL}/users/changePassword`, + {username: this.username, passwordHash, token: localStorage.getItem('userToken')}).subscribe((answer) => { + var status = Object.assign(new Answer(), answer) + if (status.success) { + Swal.fire('Password changed!', 'Watch out, \nyour old password won\'t work anymore', 'success') + } else { + Swal.fire('Error!', 'Something went wrong, bad user token?', 'error') + } + then() + }) + }, onConnectionError = onConnectionError) + // if you get a on Error, you have done something wrong + } + + setUser(username: string) { + if (username == this.username) { + return + } + this.username = username + this.callbacks.forEach(f => { + f(this.username) + }); + } } diff --git a/package-lock.json b/package-lock.json index 5807647..bdd0e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -13982,6 +13983,15 @@ "node": ">=10.13.0" } }, + "node_modules/sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==", + "funding": { + "type": "individual", + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -26196,6 +26206,11 @@ "stable": "^0.1.8" } }, + "sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 2c9a675..4b2ebf5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/src/app/account.service.ts b/src/app/account.service.ts index 2b64b6a..7f602df 100644 --- a/src/app/account.service.ts +++ b/src/app/account.service.ts @@ -3,6 +3,7 @@ import { environment } from 'src/environments/environment'; import { sha256 } from 'sha.js'; import { Router } from '@angular/router'; +import Swal from 'sweetalert2'; class Answer { success: Boolean = false @@ -32,14 +33,17 @@ }) export class AccountService { public username = '?'; - private userId: bigint = BigInt(0); - private userToken: bigint = BigInt(0); + private callbacks: Function[] = [] + + subscribe(f: Function) { + this.callbacks.push(f) + } constructor(private http: HttpClient, private router: Router) { this.updateUserInfo() } - public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + getPresalt(then: Function, onFail: Function = () => {}, onConnectionError: Function = () => {}, username: string = this.username) { this.http.post(`${environment.apiURL}/users/presalt`, {username}) .subscribe((answer: Object) => { var presaltAnswer = Object.assign(new PresaltAnswer(), answer) @@ -47,50 +51,86 @@ onFail() return } - // this presalting and hashing is done to protect the user's password in case of a man-in-the-middle-attack, - // where an attacker might be able to intercept a password if not hashed before. The password still has to be - // hashed on the server-side to be sure but this method prevents dumb users from having their passweord - // they use everywhere easily leaked, because you still need to crack this one layer first - // also, typical automated hacking software will hopefully be confused by my weird code - var passwordHash = new sha256().update(password + presaltAnswer.presalt).digest('hex') + then(presaltAnswer.presalt) + }, () => { + onConnectionError() + }) + } + + public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + this.setUser('') + localStorage.removeItem('userToken') + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(password + presalt).digest('hex') this.http.post(`${environment.apiURL}/users/authenticate`, {username, passwordHash}) .subscribe((answer) => { var authenticateAnswer = Object.assign(new AuthenticateAnswer(), answer) - var loginSuccess = authenticateAnswer.success == true + var loginSuccess = authenticateAnswer.success == true && authenticateAnswer.token.length > 0 if (loginSuccess) { localStorage.setItem('userToken', authenticateAnswer.token) + this.setUser(username) this.updateUserInfo() - this.username = username this.router.navigate(['admin']) } else { onFail() } - }, (error) => {onConnectionError()}) - }, (error) => {onConnectionError()}) + }, () => {onConnectionError()}) + }, onFail, onConnectionError, username) } - updateUserInfo() { + updateUserInfo(then: Function = () => {}) { var token = localStorage.getItem('userToken') if (token == null) { - this.username = '' + this.setUser('') return } - this.http.post(`${environment.apiURL}/users/checkToken`, {token}).subscribe((answer: Object) => { + var data = JSON.parse(atob(token?.split('.')[1] as string)) + var tokenData = Object.assign(new TokenData(), data) + this.setUser(tokenData.usr) + this.http.post(`${environment.apiURL}/users/checkToken`, {token, username: this.username}) + .subscribe((answer: Object) => { var checkTokenAnswer = Object.assign(new CheckTokenAnswer(), answer) - if (checkTokenAnswer.valid == true) { - var data = JSON.parse(atob(token?.split('.')[1] as string)) - var tokenData = Object.assign(new TokenData(), data) - this.username = tokenData.usr + if (checkTokenAnswer.valid) { } else { - this.username = '' + this.router.navigate(['/']) + this.setUser('') } + then() }, () => { - this.username = '' + this.router.navigate(['/']) + this.setUser('') }) } logout() { - this.username = '' + this.setUser('') localStorage.removeItem('userToken') } + + changePassword(newPassword: string, then: Function, onConnectionError: Function) { + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(newPassword + presalt).digest('hex') + this.http.post(`${environment.apiURL}/users/changePassword`, + {username: this.username, passwordHash, token: localStorage.getItem('userToken')}).subscribe((answer) => { + var status = Object.assign(new Answer(), answer) + if (status.success) { + Swal.fire('Password changed!', 'Watch out, \nyour old password won\'t work anymore', 'success') + } else { + Swal.fire('Error!', 'Something went wrong, bad user token?', 'error') + } + then() + }) + }, onConnectionError = onConnectionError) + // if you get a on Error, you have done something wrong + } + + setUser(username: string) { + if (username == this.username) { + return + } + this.username = username + this.callbacks.forEach(f => { + f(this.username) + }); + } } diff --git a/src/app/admin/admin.component.html b/src/app/admin/admin.component.html index f949c13..726150d 100644 --- a/src/app/admin/admin.component.html +++ b/src/app/admin/admin.component.html @@ -1,6 +1,25 @@ -

Hello {{account.username}}!

-

Welcome to the admin panel

- -

There is nothing to do here yet...

- - \ No newline at end of file + + +

Hello {{account.username}}!

+

Welcome to the admin panel

+
+ +

Change password

+
+ + New password + this field must not be empty + + + + Retype new password + this field must not be empty + + +
+ please type your new password the same way twice. +
+ +
+
+
\ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5807647..bdd0e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -13982,6 +13983,15 @@ "node": ">=10.13.0" } }, + "node_modules/sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==", + "funding": { + "type": "individual", + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -26196,6 +26206,11 @@ "stable": "^0.1.8" } }, + "sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 2c9a675..4b2ebf5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/src/app/account.service.ts b/src/app/account.service.ts index 2b64b6a..7f602df 100644 --- a/src/app/account.service.ts +++ b/src/app/account.service.ts @@ -3,6 +3,7 @@ import { environment } from 'src/environments/environment'; import { sha256 } from 'sha.js'; import { Router } from '@angular/router'; +import Swal from 'sweetalert2'; class Answer { success: Boolean = false @@ -32,14 +33,17 @@ }) export class AccountService { public username = '?'; - private userId: bigint = BigInt(0); - private userToken: bigint = BigInt(0); + private callbacks: Function[] = [] + + subscribe(f: Function) { + this.callbacks.push(f) + } constructor(private http: HttpClient, private router: Router) { this.updateUserInfo() } - public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + getPresalt(then: Function, onFail: Function = () => {}, onConnectionError: Function = () => {}, username: string = this.username) { this.http.post(`${environment.apiURL}/users/presalt`, {username}) .subscribe((answer: Object) => { var presaltAnswer = Object.assign(new PresaltAnswer(), answer) @@ -47,50 +51,86 @@ onFail() return } - // this presalting and hashing is done to protect the user's password in case of a man-in-the-middle-attack, - // where an attacker might be able to intercept a password if not hashed before. The password still has to be - // hashed on the server-side to be sure but this method prevents dumb users from having their passweord - // they use everywhere easily leaked, because you still need to crack this one layer first - // also, typical automated hacking software will hopefully be confused by my weird code - var passwordHash = new sha256().update(password + presaltAnswer.presalt).digest('hex') + then(presaltAnswer.presalt) + }, () => { + onConnectionError() + }) + } + + public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + this.setUser('') + localStorage.removeItem('userToken') + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(password + presalt).digest('hex') this.http.post(`${environment.apiURL}/users/authenticate`, {username, passwordHash}) .subscribe((answer) => { var authenticateAnswer = Object.assign(new AuthenticateAnswer(), answer) - var loginSuccess = authenticateAnswer.success == true + var loginSuccess = authenticateAnswer.success == true && authenticateAnswer.token.length > 0 if (loginSuccess) { localStorage.setItem('userToken', authenticateAnswer.token) + this.setUser(username) this.updateUserInfo() - this.username = username this.router.navigate(['admin']) } else { onFail() } - }, (error) => {onConnectionError()}) - }, (error) => {onConnectionError()}) + }, () => {onConnectionError()}) + }, onFail, onConnectionError, username) } - updateUserInfo() { + updateUserInfo(then: Function = () => {}) { var token = localStorage.getItem('userToken') if (token == null) { - this.username = '' + this.setUser('') return } - this.http.post(`${environment.apiURL}/users/checkToken`, {token}).subscribe((answer: Object) => { + var data = JSON.parse(atob(token?.split('.')[1] as string)) + var tokenData = Object.assign(new TokenData(), data) + this.setUser(tokenData.usr) + this.http.post(`${environment.apiURL}/users/checkToken`, {token, username: this.username}) + .subscribe((answer: Object) => { var checkTokenAnswer = Object.assign(new CheckTokenAnswer(), answer) - if (checkTokenAnswer.valid == true) { - var data = JSON.parse(atob(token?.split('.')[1] as string)) - var tokenData = Object.assign(new TokenData(), data) - this.username = tokenData.usr + if (checkTokenAnswer.valid) { } else { - this.username = '' + this.router.navigate(['/']) + this.setUser('') } + then() }, () => { - this.username = '' + this.router.navigate(['/']) + this.setUser('') }) } logout() { - this.username = '' + this.setUser('') localStorage.removeItem('userToken') } + + changePassword(newPassword: string, then: Function, onConnectionError: Function) { + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(newPassword + presalt).digest('hex') + this.http.post(`${environment.apiURL}/users/changePassword`, + {username: this.username, passwordHash, token: localStorage.getItem('userToken')}).subscribe((answer) => { + var status = Object.assign(new Answer(), answer) + if (status.success) { + Swal.fire('Password changed!', 'Watch out, \nyour old password won\'t work anymore', 'success') + } else { + Swal.fire('Error!', 'Something went wrong, bad user token?', 'error') + } + then() + }) + }, onConnectionError = onConnectionError) + // if you get a on Error, you have done something wrong + } + + setUser(username: string) { + if (username == this.username) { + return + } + this.username = username + this.callbacks.forEach(f => { + f(this.username) + }); + } } diff --git a/src/app/admin/admin.component.html b/src/app/admin/admin.component.html index f949c13..726150d 100644 --- a/src/app/admin/admin.component.html +++ b/src/app/admin/admin.component.html @@ -1,6 +1,25 @@ -

Hello {{account.username}}!

-

Welcome to the admin panel

- -

There is nothing to do here yet...

- - \ No newline at end of file + + +

Hello {{account.username}}!

+

Welcome to the admin panel

+
+ +

Change password

+
+ + New password + this field must not be empty + + + + Retype new password + this field must not be empty + + +
+ please type your new password the same way twice. +
+ +
+
+
\ No newline at end of file diff --git a/src/app/admin/admin.component.scss b/src/app/admin/admin.component.scss index e69de29..8286984 100644 --- a/src/app/admin/admin.component.scss +++ b/src/app/admin/admin.component.scss @@ -0,0 +1,3 @@ +.full-width { + width: 100%; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5807647..bdd0e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -13982,6 +13983,15 @@ "node": ">=10.13.0" } }, + "node_modules/sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==", + "funding": { + "type": "individual", + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -26196,6 +26206,11 @@ "stable": "^0.1.8" } }, + "sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 2c9a675..4b2ebf5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/src/app/account.service.ts b/src/app/account.service.ts index 2b64b6a..7f602df 100644 --- a/src/app/account.service.ts +++ b/src/app/account.service.ts @@ -3,6 +3,7 @@ import { environment } from 'src/environments/environment'; import { sha256 } from 'sha.js'; import { Router } from '@angular/router'; +import Swal from 'sweetalert2'; class Answer { success: Boolean = false @@ -32,14 +33,17 @@ }) export class AccountService { public username = '?'; - private userId: bigint = BigInt(0); - private userToken: bigint = BigInt(0); + private callbacks: Function[] = [] + + subscribe(f: Function) { + this.callbacks.push(f) + } constructor(private http: HttpClient, private router: Router) { this.updateUserInfo() } - public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + getPresalt(then: Function, onFail: Function = () => {}, onConnectionError: Function = () => {}, username: string = this.username) { this.http.post(`${environment.apiURL}/users/presalt`, {username}) .subscribe((answer: Object) => { var presaltAnswer = Object.assign(new PresaltAnswer(), answer) @@ -47,50 +51,86 @@ onFail() return } - // this presalting and hashing is done to protect the user's password in case of a man-in-the-middle-attack, - // where an attacker might be able to intercept a password if not hashed before. The password still has to be - // hashed on the server-side to be sure but this method prevents dumb users from having their passweord - // they use everywhere easily leaked, because you still need to crack this one layer first - // also, typical automated hacking software will hopefully be confused by my weird code - var passwordHash = new sha256().update(password + presaltAnswer.presalt).digest('hex') + then(presaltAnswer.presalt) + }, () => { + onConnectionError() + }) + } + + public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + this.setUser('') + localStorage.removeItem('userToken') + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(password + presalt).digest('hex') this.http.post(`${environment.apiURL}/users/authenticate`, {username, passwordHash}) .subscribe((answer) => { var authenticateAnswer = Object.assign(new AuthenticateAnswer(), answer) - var loginSuccess = authenticateAnswer.success == true + var loginSuccess = authenticateAnswer.success == true && authenticateAnswer.token.length > 0 if (loginSuccess) { localStorage.setItem('userToken', authenticateAnswer.token) + this.setUser(username) this.updateUserInfo() - this.username = username this.router.navigate(['admin']) } else { onFail() } - }, (error) => {onConnectionError()}) - }, (error) => {onConnectionError()}) + }, () => {onConnectionError()}) + }, onFail, onConnectionError, username) } - updateUserInfo() { + updateUserInfo(then: Function = () => {}) { var token = localStorage.getItem('userToken') if (token == null) { - this.username = '' + this.setUser('') return } - this.http.post(`${environment.apiURL}/users/checkToken`, {token}).subscribe((answer: Object) => { + var data = JSON.parse(atob(token?.split('.')[1] as string)) + var tokenData = Object.assign(new TokenData(), data) + this.setUser(tokenData.usr) + this.http.post(`${environment.apiURL}/users/checkToken`, {token, username: this.username}) + .subscribe((answer: Object) => { var checkTokenAnswer = Object.assign(new CheckTokenAnswer(), answer) - if (checkTokenAnswer.valid == true) { - var data = JSON.parse(atob(token?.split('.')[1] as string)) - var tokenData = Object.assign(new TokenData(), data) - this.username = tokenData.usr + if (checkTokenAnswer.valid) { } else { - this.username = '' + this.router.navigate(['/']) + this.setUser('') } + then() }, () => { - this.username = '' + this.router.navigate(['/']) + this.setUser('') }) } logout() { - this.username = '' + this.setUser('') localStorage.removeItem('userToken') } + + changePassword(newPassword: string, then: Function, onConnectionError: Function) { + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(newPassword + presalt).digest('hex') + this.http.post(`${environment.apiURL}/users/changePassword`, + {username: this.username, passwordHash, token: localStorage.getItem('userToken')}).subscribe((answer) => { + var status = Object.assign(new Answer(), answer) + if (status.success) { + Swal.fire('Password changed!', 'Watch out, \nyour old password won\'t work anymore', 'success') + } else { + Swal.fire('Error!', 'Something went wrong, bad user token?', 'error') + } + then() + }) + }, onConnectionError = onConnectionError) + // if you get a on Error, you have done something wrong + } + + setUser(username: string) { + if (username == this.username) { + return + } + this.username = username + this.callbacks.forEach(f => { + f(this.username) + }); + } } diff --git a/src/app/admin/admin.component.html b/src/app/admin/admin.component.html index f949c13..726150d 100644 --- a/src/app/admin/admin.component.html +++ b/src/app/admin/admin.component.html @@ -1,6 +1,25 @@ -

Hello {{account.username}}!

-

Welcome to the admin panel

- -

There is nothing to do here yet...

- - \ No newline at end of file + + +

Hello {{account.username}}!

+

Welcome to the admin panel

+
+ +

Change password

+
+ + New password + this field must not be empty + + + + Retype new password + this field must not be empty + + +
+ please type your new password the same way twice. +
+ +
+
+
\ No newline at end of file diff --git a/src/app/admin/admin.component.scss b/src/app/admin/admin.component.scss index e69de29..8286984 100644 --- a/src/app/admin/admin.component.scss +++ b/src/app/admin/admin.component.scss @@ -0,0 +1,3 @@ +.full-width { + width: 100%; +} \ No newline at end of file diff --git a/src/app/admin/admin.component.ts b/src/app/admin/admin.component.ts index 85273be..189d680 100644 --- a/src/app/admin/admin.component.ts +++ b/src/app/admin/admin.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { AccountService } from '../account.service'; @@ -8,13 +9,38 @@ styleUrls: ['./admin.component.scss'] }) export class AdminComponent implements OnInit { + public passwordForm: FormGroup + public passwordError = false + public username = '' - constructor(public account: AccountService, private router: Router) { - if (account.username.length == 0) { - router.navigate(['login']) - } + constructor(public account: AccountService, router: Router, private formBuilder: FormBuilder) { + + this.passwordForm = this.formBuilder.group({ + password: ['', Validators.required], + password_repeat: ['', Validators.required] + }) + account.subscribe((username: string) => { + if (account.username.length == 0) { + router.navigate(['login']) + } + this.username = username + }) } ngOnInit(): void { } + + onPasswordSubmit() { + if (this.passwordForm.invalid) { + return + } + this.passwordError = this.passwordForm.controls['password'].value != + this.passwordForm.controls['password_repeat'].value + if (this.passwordError) { + return + } + this.account.changePassword(this.passwordForm.controls['password'].value, () => { + this.passwordForm.reset() + }, () => {}) + } } diff --git a/package-lock.json b/package-lock.json index 5807647..bdd0e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -13982,6 +13983,15 @@ "node": ">=10.13.0" } }, + "node_modules/sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==", + "funding": { + "type": "individual", + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -26196,6 +26206,11 @@ "stable": "^0.1.8" } }, + "sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 2c9a675..4b2ebf5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/src/app/account.service.ts b/src/app/account.service.ts index 2b64b6a..7f602df 100644 --- a/src/app/account.service.ts +++ b/src/app/account.service.ts @@ -3,6 +3,7 @@ import { environment } from 'src/environments/environment'; import { sha256 } from 'sha.js'; import { Router } from '@angular/router'; +import Swal from 'sweetalert2'; class Answer { success: Boolean = false @@ -32,14 +33,17 @@ }) export class AccountService { public username = '?'; - private userId: bigint = BigInt(0); - private userToken: bigint = BigInt(0); + private callbacks: Function[] = [] + + subscribe(f: Function) { + this.callbacks.push(f) + } constructor(private http: HttpClient, private router: Router) { this.updateUserInfo() } - public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + getPresalt(then: Function, onFail: Function = () => {}, onConnectionError: Function = () => {}, username: string = this.username) { this.http.post(`${environment.apiURL}/users/presalt`, {username}) .subscribe((answer: Object) => { var presaltAnswer = Object.assign(new PresaltAnswer(), answer) @@ -47,50 +51,86 @@ onFail() return } - // this presalting and hashing is done to protect the user's password in case of a man-in-the-middle-attack, - // where an attacker might be able to intercept a password if not hashed before. The password still has to be - // hashed on the server-side to be sure but this method prevents dumb users from having their passweord - // they use everywhere easily leaked, because you still need to crack this one layer first - // also, typical automated hacking software will hopefully be confused by my weird code - var passwordHash = new sha256().update(password + presaltAnswer.presalt).digest('hex') + then(presaltAnswer.presalt) + }, () => { + onConnectionError() + }) + } + + public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + this.setUser('') + localStorage.removeItem('userToken') + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(password + presalt).digest('hex') this.http.post(`${environment.apiURL}/users/authenticate`, {username, passwordHash}) .subscribe((answer) => { var authenticateAnswer = Object.assign(new AuthenticateAnswer(), answer) - var loginSuccess = authenticateAnswer.success == true + var loginSuccess = authenticateAnswer.success == true && authenticateAnswer.token.length > 0 if (loginSuccess) { localStorage.setItem('userToken', authenticateAnswer.token) + this.setUser(username) this.updateUserInfo() - this.username = username this.router.navigate(['admin']) } else { onFail() } - }, (error) => {onConnectionError()}) - }, (error) => {onConnectionError()}) + }, () => {onConnectionError()}) + }, onFail, onConnectionError, username) } - updateUserInfo() { + updateUserInfo(then: Function = () => {}) { var token = localStorage.getItem('userToken') if (token == null) { - this.username = '' + this.setUser('') return } - this.http.post(`${environment.apiURL}/users/checkToken`, {token}).subscribe((answer: Object) => { + var data = JSON.parse(atob(token?.split('.')[1] as string)) + var tokenData = Object.assign(new TokenData(), data) + this.setUser(tokenData.usr) + this.http.post(`${environment.apiURL}/users/checkToken`, {token, username: this.username}) + .subscribe((answer: Object) => { var checkTokenAnswer = Object.assign(new CheckTokenAnswer(), answer) - if (checkTokenAnswer.valid == true) { - var data = JSON.parse(atob(token?.split('.')[1] as string)) - var tokenData = Object.assign(new TokenData(), data) - this.username = tokenData.usr + if (checkTokenAnswer.valid) { } else { - this.username = '' + this.router.navigate(['/']) + this.setUser('') } + then() }, () => { - this.username = '' + this.router.navigate(['/']) + this.setUser('') }) } logout() { - this.username = '' + this.setUser('') localStorage.removeItem('userToken') } + + changePassword(newPassword: string, then: Function, onConnectionError: Function) { + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(newPassword + presalt).digest('hex') + this.http.post(`${environment.apiURL}/users/changePassword`, + {username: this.username, passwordHash, token: localStorage.getItem('userToken')}).subscribe((answer) => { + var status = Object.assign(new Answer(), answer) + if (status.success) { + Swal.fire('Password changed!', 'Watch out, \nyour old password won\'t work anymore', 'success') + } else { + Swal.fire('Error!', 'Something went wrong, bad user token?', 'error') + } + then() + }) + }, onConnectionError = onConnectionError) + // if you get a on Error, you have done something wrong + } + + setUser(username: string) { + if (username == this.username) { + return + } + this.username = username + this.callbacks.forEach(f => { + f(this.username) + }); + } } diff --git a/src/app/admin/admin.component.html b/src/app/admin/admin.component.html index f949c13..726150d 100644 --- a/src/app/admin/admin.component.html +++ b/src/app/admin/admin.component.html @@ -1,6 +1,25 @@ -

Hello {{account.username}}!

-

Welcome to the admin panel

- -

There is nothing to do here yet...

- - \ No newline at end of file + + +

Hello {{account.username}}!

+

Welcome to the admin panel

+
+ +

Change password

+
+ + New password + this field must not be empty + + + + Retype new password + this field must not be empty + + +
+ please type your new password the same way twice. +
+ +
+
+
\ No newline at end of file diff --git a/src/app/admin/admin.component.scss b/src/app/admin/admin.component.scss index e69de29..8286984 100644 --- a/src/app/admin/admin.component.scss +++ b/src/app/admin/admin.component.scss @@ -0,0 +1,3 @@ +.full-width { + width: 100%; +} \ No newline at end of file diff --git a/src/app/admin/admin.component.ts b/src/app/admin/admin.component.ts index 85273be..189d680 100644 --- a/src/app/admin/admin.component.ts +++ b/src/app/admin/admin.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { AccountService } from '../account.service'; @@ -8,13 +9,38 @@ styleUrls: ['./admin.component.scss'] }) export class AdminComponent implements OnInit { + public passwordForm: FormGroup + public passwordError = false + public username = '' - constructor(public account: AccountService, private router: Router) { - if (account.username.length == 0) { - router.navigate(['login']) - } + constructor(public account: AccountService, router: Router, private formBuilder: FormBuilder) { + + this.passwordForm = this.formBuilder.group({ + password: ['', Validators.required], + password_repeat: ['', Validators.required] + }) + account.subscribe((username: string) => { + if (account.username.length == 0) { + router.navigate(['login']) + } + this.username = username + }) } ngOnInit(): void { } + + onPasswordSubmit() { + if (this.passwordForm.invalid) { + return + } + this.passwordError = this.passwordForm.controls['password'].value != + this.passwordForm.controls['password_repeat'].value + if (this.passwordError) { + return + } + this.account.changePassword(this.passwordForm.controls['password'].value, () => { + this.passwordForm.reset() + }, () => {}) + } } diff --git a/src/app/grid/grid.component.scss b/src/app/grid/grid.component.scss index c26b381..b35b5cc 100644 --- a/src/app/grid/grid.component.scss +++ b/src/app/grid/grid.component.scss @@ -102,6 +102,6 @@ } } -:host ::ng-deep a:hover>img { +:host ::ng-deep mat-card:hover>img { filter: grayscale(0); } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5807647..bdd0e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -13982,6 +13983,15 @@ "node": ">=10.13.0" } }, + "node_modules/sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==", + "funding": { + "type": "individual", + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -26196,6 +26206,11 @@ "stable": "^0.1.8" } }, + "sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 2c9a675..4b2ebf5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/src/app/account.service.ts b/src/app/account.service.ts index 2b64b6a..7f602df 100644 --- a/src/app/account.service.ts +++ b/src/app/account.service.ts @@ -3,6 +3,7 @@ import { environment } from 'src/environments/environment'; import { sha256 } from 'sha.js'; import { Router } from '@angular/router'; +import Swal from 'sweetalert2'; class Answer { success: Boolean = false @@ -32,14 +33,17 @@ }) export class AccountService { public username = '?'; - private userId: bigint = BigInt(0); - private userToken: bigint = BigInt(0); + private callbacks: Function[] = [] + + subscribe(f: Function) { + this.callbacks.push(f) + } constructor(private http: HttpClient, private router: Router) { this.updateUserInfo() } - public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + getPresalt(then: Function, onFail: Function = () => {}, onConnectionError: Function = () => {}, username: string = this.username) { this.http.post(`${environment.apiURL}/users/presalt`, {username}) .subscribe((answer: Object) => { var presaltAnswer = Object.assign(new PresaltAnswer(), answer) @@ -47,50 +51,86 @@ onFail() return } - // this presalting and hashing is done to protect the user's password in case of a man-in-the-middle-attack, - // where an attacker might be able to intercept a password if not hashed before. The password still has to be - // hashed on the server-side to be sure but this method prevents dumb users from having their passweord - // they use everywhere easily leaked, because you still need to crack this one layer first - // also, typical automated hacking software will hopefully be confused by my weird code - var passwordHash = new sha256().update(password + presaltAnswer.presalt).digest('hex') + then(presaltAnswer.presalt) + }, () => { + onConnectionError() + }) + } + + public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + this.setUser('') + localStorage.removeItem('userToken') + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(password + presalt).digest('hex') this.http.post(`${environment.apiURL}/users/authenticate`, {username, passwordHash}) .subscribe((answer) => { var authenticateAnswer = Object.assign(new AuthenticateAnswer(), answer) - var loginSuccess = authenticateAnswer.success == true + var loginSuccess = authenticateAnswer.success == true && authenticateAnswer.token.length > 0 if (loginSuccess) { localStorage.setItem('userToken', authenticateAnswer.token) + this.setUser(username) this.updateUserInfo() - this.username = username this.router.navigate(['admin']) } else { onFail() } - }, (error) => {onConnectionError()}) - }, (error) => {onConnectionError()}) + }, () => {onConnectionError()}) + }, onFail, onConnectionError, username) } - updateUserInfo() { + updateUserInfo(then: Function = () => {}) { var token = localStorage.getItem('userToken') if (token == null) { - this.username = '' + this.setUser('') return } - this.http.post(`${environment.apiURL}/users/checkToken`, {token}).subscribe((answer: Object) => { + var data = JSON.parse(atob(token?.split('.')[1] as string)) + var tokenData = Object.assign(new TokenData(), data) + this.setUser(tokenData.usr) + this.http.post(`${environment.apiURL}/users/checkToken`, {token, username: this.username}) + .subscribe((answer: Object) => { var checkTokenAnswer = Object.assign(new CheckTokenAnswer(), answer) - if (checkTokenAnswer.valid == true) { - var data = JSON.parse(atob(token?.split('.')[1] as string)) - var tokenData = Object.assign(new TokenData(), data) - this.username = tokenData.usr + if (checkTokenAnswer.valid) { } else { - this.username = '' + this.router.navigate(['/']) + this.setUser('') } + then() }, () => { - this.username = '' + this.router.navigate(['/']) + this.setUser('') }) } logout() { - this.username = '' + this.setUser('') localStorage.removeItem('userToken') } + + changePassword(newPassword: string, then: Function, onConnectionError: Function) { + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(newPassword + presalt).digest('hex') + this.http.post(`${environment.apiURL}/users/changePassword`, + {username: this.username, passwordHash, token: localStorage.getItem('userToken')}).subscribe((answer) => { + var status = Object.assign(new Answer(), answer) + if (status.success) { + Swal.fire('Password changed!', 'Watch out, \nyour old password won\'t work anymore', 'success') + } else { + Swal.fire('Error!', 'Something went wrong, bad user token?', 'error') + } + then() + }) + }, onConnectionError = onConnectionError) + // if you get a on Error, you have done something wrong + } + + setUser(username: string) { + if (username == this.username) { + return + } + this.username = username + this.callbacks.forEach(f => { + f(this.username) + }); + } } diff --git a/src/app/admin/admin.component.html b/src/app/admin/admin.component.html index f949c13..726150d 100644 --- a/src/app/admin/admin.component.html +++ b/src/app/admin/admin.component.html @@ -1,6 +1,25 @@ -

Hello {{account.username}}!

-

Welcome to the admin panel

- -

There is nothing to do here yet...

- - \ No newline at end of file + + +

Hello {{account.username}}!

+

Welcome to the admin panel

+
+ +

Change password

+
+ + New password + this field must not be empty + + + + Retype new password + this field must not be empty + + +
+ please type your new password the same way twice. +
+ +
+
+
\ No newline at end of file diff --git a/src/app/admin/admin.component.scss b/src/app/admin/admin.component.scss index e69de29..8286984 100644 --- a/src/app/admin/admin.component.scss +++ b/src/app/admin/admin.component.scss @@ -0,0 +1,3 @@ +.full-width { + width: 100%; +} \ No newline at end of file diff --git a/src/app/admin/admin.component.ts b/src/app/admin/admin.component.ts index 85273be..189d680 100644 --- a/src/app/admin/admin.component.ts +++ b/src/app/admin/admin.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { AccountService } from '../account.service'; @@ -8,13 +9,38 @@ styleUrls: ['./admin.component.scss'] }) export class AdminComponent implements OnInit { + public passwordForm: FormGroup + public passwordError = false + public username = '' - constructor(public account: AccountService, private router: Router) { - if (account.username.length == 0) { - router.navigate(['login']) - } + constructor(public account: AccountService, router: Router, private formBuilder: FormBuilder) { + + this.passwordForm = this.formBuilder.group({ + password: ['', Validators.required], + password_repeat: ['', Validators.required] + }) + account.subscribe((username: string) => { + if (account.username.length == 0) { + router.navigate(['login']) + } + this.username = username + }) } ngOnInit(): void { } + + onPasswordSubmit() { + if (this.passwordForm.invalid) { + return + } + this.passwordError = this.passwordForm.controls['password'].value != + this.passwordForm.controls['password_repeat'].value + if (this.passwordError) { + return + } + this.account.changePassword(this.passwordForm.controls['password'].value, () => { + this.passwordForm.reset() + }, () => {}) + } } diff --git a/src/app/grid/grid.component.scss b/src/app/grid/grid.component.scss index c26b381..b35b5cc 100644 --- a/src/app/grid/grid.component.scss +++ b/src/app/grid/grid.component.scss @@ -102,6 +102,6 @@ } } -:host ::ng-deep a:hover>img { +:host ::ng-deep mat-card:hover>img { filter: grayscale(0); } \ No newline at end of file diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index f264a1d..6bbb28e 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -12,8 +12,8 @@

Tree-os

-
+

Tree os is a hobby operating system I mainly develop just for the learning experience. As of now, it supports some multitasking and I am currently in the process of adding a file system and can already read files! @@ -28,8 +28,8 @@

Traffic simulator

- +

For school, I have to write a scientific article about something regarding "everyday physics". I am focusing on the design of urban junctions and need a simulation for cars traversing different designs. You can find the current code on my git. diff --git a/package-lock.json b/package-lock.json index 5807647..bdd0e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -13982,6 +13983,15 @@ "node": ">=10.13.0" } }, + "node_modules/sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==", + "funding": { + "type": "individual", + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -26196,6 +26206,11 @@ "stable": "^0.1.8" } }, + "sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 2c9a675..4b2ebf5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/src/app/account.service.ts b/src/app/account.service.ts index 2b64b6a..7f602df 100644 --- a/src/app/account.service.ts +++ b/src/app/account.service.ts @@ -3,6 +3,7 @@ import { environment } from 'src/environments/environment'; import { sha256 } from 'sha.js'; import { Router } from '@angular/router'; +import Swal from 'sweetalert2'; class Answer { success: Boolean = false @@ -32,14 +33,17 @@ }) export class AccountService { public username = '?'; - private userId: bigint = BigInt(0); - private userToken: bigint = BigInt(0); + private callbacks: Function[] = [] + + subscribe(f: Function) { + this.callbacks.push(f) + } constructor(private http: HttpClient, private router: Router) { this.updateUserInfo() } - public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + getPresalt(then: Function, onFail: Function = () => {}, onConnectionError: Function = () => {}, username: string = this.username) { this.http.post(`${environment.apiURL}/users/presalt`, {username}) .subscribe((answer: Object) => { var presaltAnswer = Object.assign(new PresaltAnswer(), answer) @@ -47,50 +51,86 @@ onFail() return } - // this presalting and hashing is done to protect the user's password in case of a man-in-the-middle-attack, - // where an attacker might be able to intercept a password if not hashed before. The password still has to be - // hashed on the server-side to be sure but this method prevents dumb users from having their passweord - // they use everywhere easily leaked, because you still need to crack this one layer first - // also, typical automated hacking software will hopefully be confused by my weird code - var passwordHash = new sha256().update(password + presaltAnswer.presalt).digest('hex') + then(presaltAnswer.presalt) + }, () => { + onConnectionError() + }) + } + + public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + this.setUser('') + localStorage.removeItem('userToken') + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(password + presalt).digest('hex') this.http.post(`${environment.apiURL}/users/authenticate`, {username, passwordHash}) .subscribe((answer) => { var authenticateAnswer = Object.assign(new AuthenticateAnswer(), answer) - var loginSuccess = authenticateAnswer.success == true + var loginSuccess = authenticateAnswer.success == true && authenticateAnswer.token.length > 0 if (loginSuccess) { localStorage.setItem('userToken', authenticateAnswer.token) + this.setUser(username) this.updateUserInfo() - this.username = username this.router.navigate(['admin']) } else { onFail() } - }, (error) => {onConnectionError()}) - }, (error) => {onConnectionError()}) + }, () => {onConnectionError()}) + }, onFail, onConnectionError, username) } - updateUserInfo() { + updateUserInfo(then: Function = () => {}) { var token = localStorage.getItem('userToken') if (token == null) { - this.username = '' + this.setUser('') return } - this.http.post(`${environment.apiURL}/users/checkToken`, {token}).subscribe((answer: Object) => { + var data = JSON.parse(atob(token?.split('.')[1] as string)) + var tokenData = Object.assign(new TokenData(), data) + this.setUser(tokenData.usr) + this.http.post(`${environment.apiURL}/users/checkToken`, {token, username: this.username}) + .subscribe((answer: Object) => { var checkTokenAnswer = Object.assign(new CheckTokenAnswer(), answer) - if (checkTokenAnswer.valid == true) { - var data = JSON.parse(atob(token?.split('.')[1] as string)) - var tokenData = Object.assign(new TokenData(), data) - this.username = tokenData.usr + if (checkTokenAnswer.valid) { } else { - this.username = '' + this.router.navigate(['/']) + this.setUser('') } + then() }, () => { - this.username = '' + this.router.navigate(['/']) + this.setUser('') }) } logout() { - this.username = '' + this.setUser('') localStorage.removeItem('userToken') } + + changePassword(newPassword: string, then: Function, onConnectionError: Function) { + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(newPassword + presalt).digest('hex') + this.http.post(`${environment.apiURL}/users/changePassword`, + {username: this.username, passwordHash, token: localStorage.getItem('userToken')}).subscribe((answer) => { + var status = Object.assign(new Answer(), answer) + if (status.success) { + Swal.fire('Password changed!', 'Watch out, \nyour old password won\'t work anymore', 'success') + } else { + Swal.fire('Error!', 'Something went wrong, bad user token?', 'error') + } + then() + }) + }, onConnectionError = onConnectionError) + // if you get a on Error, you have done something wrong + } + + setUser(username: string) { + if (username == this.username) { + return + } + this.username = username + this.callbacks.forEach(f => { + f(this.username) + }); + } } diff --git a/src/app/admin/admin.component.html b/src/app/admin/admin.component.html index f949c13..726150d 100644 --- a/src/app/admin/admin.component.html +++ b/src/app/admin/admin.component.html @@ -1,6 +1,25 @@ -

Hello {{account.username}}!

-

Welcome to the admin panel

- -

There is nothing to do here yet...

- - \ No newline at end of file + + +

Hello {{account.username}}!

+

Welcome to the admin panel

+
+ +

Change password

+
+ + New password + this field must not be empty + + + + Retype new password + this field must not be empty + + +
+ please type your new password the same way twice. +
+ +
+
+
\ No newline at end of file diff --git a/src/app/admin/admin.component.scss b/src/app/admin/admin.component.scss index e69de29..8286984 100644 --- a/src/app/admin/admin.component.scss +++ b/src/app/admin/admin.component.scss @@ -0,0 +1,3 @@ +.full-width { + width: 100%; +} \ No newline at end of file diff --git a/src/app/admin/admin.component.ts b/src/app/admin/admin.component.ts index 85273be..189d680 100644 --- a/src/app/admin/admin.component.ts +++ b/src/app/admin/admin.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { AccountService } from '../account.service'; @@ -8,13 +9,38 @@ styleUrls: ['./admin.component.scss'] }) export class AdminComponent implements OnInit { + public passwordForm: FormGroup + public passwordError = false + public username = '' - constructor(public account: AccountService, private router: Router) { - if (account.username.length == 0) { - router.navigate(['login']) - } + constructor(public account: AccountService, router: Router, private formBuilder: FormBuilder) { + + this.passwordForm = this.formBuilder.group({ + password: ['', Validators.required], + password_repeat: ['', Validators.required] + }) + account.subscribe((username: string) => { + if (account.username.length == 0) { + router.navigate(['login']) + } + this.username = username + }) } ngOnInit(): void { } + + onPasswordSubmit() { + if (this.passwordForm.invalid) { + return + } + this.passwordError = this.passwordForm.controls['password'].value != + this.passwordForm.controls['password_repeat'].value + if (this.passwordError) { + return + } + this.account.changePassword(this.passwordForm.controls['password'].value, () => { + this.passwordForm.reset() + }, () => {}) + } } diff --git a/src/app/grid/grid.component.scss b/src/app/grid/grid.component.scss index c26b381..b35b5cc 100644 --- a/src/app/grid/grid.component.scss +++ b/src/app/grid/grid.component.scss @@ -102,6 +102,6 @@ } } -:host ::ng-deep a:hover>img { +:host ::ng-deep mat-card:hover>img { filter: grayscale(0); } \ No newline at end of file diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index f264a1d..6bbb28e 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -12,8 +12,8 @@

Tree-os

-
+

Tree os is a hobby operating system I mainly develop just for the learning experience. As of now, it supports some multitasking and I am currently in the process of adding a file system and can already read files! @@ -28,8 +28,8 @@

Traffic simulator

- +

For school, I have to write a scientific article about something regarding "everyday physics". I am focusing on the design of urban junctions and need a simulation for cars traversing different designs. You can find the current code on my git. diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts index 450a8f0..c8f045f 100644 --- a/src/app/login/login.component.ts +++ b/src/app/login/login.component.ts @@ -5,7 +5,6 @@ class LoginInformation { constructor(public username: string, public password: string) { - } } diff --git a/package-lock.json b/package-lock.json index 5807647..bdd0e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -13982,6 +13983,15 @@ "node": ">=10.13.0" } }, + "node_modules/sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==", + "funding": { + "type": "individual", + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -26196,6 +26206,11 @@ "stable": "^0.1.8" } }, + "sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 2c9a675..4b2ebf5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/src/app/account.service.ts b/src/app/account.service.ts index 2b64b6a..7f602df 100644 --- a/src/app/account.service.ts +++ b/src/app/account.service.ts @@ -3,6 +3,7 @@ import { environment } from 'src/environments/environment'; import { sha256 } from 'sha.js'; import { Router } from '@angular/router'; +import Swal from 'sweetalert2'; class Answer { success: Boolean = false @@ -32,14 +33,17 @@ }) export class AccountService { public username = '?'; - private userId: bigint = BigInt(0); - private userToken: bigint = BigInt(0); + private callbacks: Function[] = [] + + subscribe(f: Function) { + this.callbacks.push(f) + } constructor(private http: HttpClient, private router: Router) { this.updateUserInfo() } - public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + getPresalt(then: Function, onFail: Function = () => {}, onConnectionError: Function = () => {}, username: string = this.username) { this.http.post(`${environment.apiURL}/users/presalt`, {username}) .subscribe((answer: Object) => { var presaltAnswer = Object.assign(new PresaltAnswer(), answer) @@ -47,50 +51,86 @@ onFail() return } - // this presalting and hashing is done to protect the user's password in case of a man-in-the-middle-attack, - // where an attacker might be able to intercept a password if not hashed before. The password still has to be - // hashed on the server-side to be sure but this method prevents dumb users from having their passweord - // they use everywhere easily leaked, because you still need to crack this one layer first - // also, typical automated hacking software will hopefully be confused by my weird code - var passwordHash = new sha256().update(password + presaltAnswer.presalt).digest('hex') + then(presaltAnswer.presalt) + }, () => { + onConnectionError() + }) + } + + public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + this.setUser('') + localStorage.removeItem('userToken') + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(password + presalt).digest('hex') this.http.post(`${environment.apiURL}/users/authenticate`, {username, passwordHash}) .subscribe((answer) => { var authenticateAnswer = Object.assign(new AuthenticateAnswer(), answer) - var loginSuccess = authenticateAnswer.success == true + var loginSuccess = authenticateAnswer.success == true && authenticateAnswer.token.length > 0 if (loginSuccess) { localStorage.setItem('userToken', authenticateAnswer.token) + this.setUser(username) this.updateUserInfo() - this.username = username this.router.navigate(['admin']) } else { onFail() } - }, (error) => {onConnectionError()}) - }, (error) => {onConnectionError()}) + }, () => {onConnectionError()}) + }, onFail, onConnectionError, username) } - updateUserInfo() { + updateUserInfo(then: Function = () => {}) { var token = localStorage.getItem('userToken') if (token == null) { - this.username = '' + this.setUser('') return } - this.http.post(`${environment.apiURL}/users/checkToken`, {token}).subscribe((answer: Object) => { + var data = JSON.parse(atob(token?.split('.')[1] as string)) + var tokenData = Object.assign(new TokenData(), data) + this.setUser(tokenData.usr) + this.http.post(`${environment.apiURL}/users/checkToken`, {token, username: this.username}) + .subscribe((answer: Object) => { var checkTokenAnswer = Object.assign(new CheckTokenAnswer(), answer) - if (checkTokenAnswer.valid == true) { - var data = JSON.parse(atob(token?.split('.')[1] as string)) - var tokenData = Object.assign(new TokenData(), data) - this.username = tokenData.usr + if (checkTokenAnswer.valid) { } else { - this.username = '' + this.router.navigate(['/']) + this.setUser('') } + then() }, () => { - this.username = '' + this.router.navigate(['/']) + this.setUser('') }) } logout() { - this.username = '' + this.setUser('') localStorage.removeItem('userToken') } + + changePassword(newPassword: string, then: Function, onConnectionError: Function) { + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(newPassword + presalt).digest('hex') + this.http.post(`${environment.apiURL}/users/changePassword`, + {username: this.username, passwordHash, token: localStorage.getItem('userToken')}).subscribe((answer) => { + var status = Object.assign(new Answer(), answer) + if (status.success) { + Swal.fire('Password changed!', 'Watch out, \nyour old password won\'t work anymore', 'success') + } else { + Swal.fire('Error!', 'Something went wrong, bad user token?', 'error') + } + then() + }) + }, onConnectionError = onConnectionError) + // if you get a on Error, you have done something wrong + } + + setUser(username: string) { + if (username == this.username) { + return + } + this.username = username + this.callbacks.forEach(f => { + f(this.username) + }); + } } diff --git a/src/app/admin/admin.component.html b/src/app/admin/admin.component.html index f949c13..726150d 100644 --- a/src/app/admin/admin.component.html +++ b/src/app/admin/admin.component.html @@ -1,6 +1,25 @@ -

Hello {{account.username}}!

-

Welcome to the admin panel

- -

There is nothing to do here yet...

- - \ No newline at end of file + + +

Hello {{account.username}}!

+

Welcome to the admin panel

+
+ +

Change password

+
+ + New password + this field must not be empty + + + + Retype new password + this field must not be empty + + +
+ please type your new password the same way twice. +
+ +
+
+
\ No newline at end of file diff --git a/src/app/admin/admin.component.scss b/src/app/admin/admin.component.scss index e69de29..8286984 100644 --- a/src/app/admin/admin.component.scss +++ b/src/app/admin/admin.component.scss @@ -0,0 +1,3 @@ +.full-width { + width: 100%; +} \ No newline at end of file diff --git a/src/app/admin/admin.component.ts b/src/app/admin/admin.component.ts index 85273be..189d680 100644 --- a/src/app/admin/admin.component.ts +++ b/src/app/admin/admin.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { AccountService } from '../account.service'; @@ -8,13 +9,38 @@ styleUrls: ['./admin.component.scss'] }) export class AdminComponent implements OnInit { + public passwordForm: FormGroup + public passwordError = false + public username = '' - constructor(public account: AccountService, private router: Router) { - if (account.username.length == 0) { - router.navigate(['login']) - } + constructor(public account: AccountService, router: Router, private formBuilder: FormBuilder) { + + this.passwordForm = this.formBuilder.group({ + password: ['', Validators.required], + password_repeat: ['', Validators.required] + }) + account.subscribe((username: string) => { + if (account.username.length == 0) { + router.navigate(['login']) + } + this.username = username + }) } ngOnInit(): void { } + + onPasswordSubmit() { + if (this.passwordForm.invalid) { + return + } + this.passwordError = this.passwordForm.controls['password'].value != + this.passwordForm.controls['password_repeat'].value + if (this.passwordError) { + return + } + this.account.changePassword(this.passwordForm.controls['password'].value, () => { + this.passwordForm.reset() + }, () => {}) + } } diff --git a/src/app/grid/grid.component.scss b/src/app/grid/grid.component.scss index c26b381..b35b5cc 100644 --- a/src/app/grid/grid.component.scss +++ b/src/app/grid/grid.component.scss @@ -102,6 +102,6 @@ } } -:host ::ng-deep a:hover>img { +:host ::ng-deep mat-card:hover>img { filter: grayscale(0); } \ No newline at end of file diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index f264a1d..6bbb28e 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -12,8 +12,8 @@

Tree-os

-
+

Tree os is a hobby operating system I mainly develop just for the learning experience. As of now, it supports some multitasking and I am currently in the process of adding a file system and can already read files! @@ -28,8 +28,8 @@

Traffic simulator

- +

For school, I have to write a scientific article about something regarding "everyday physics". I am focusing on the design of urban junctions and need a simulation for cars traversing different designs. You can find the current code on my git. diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts index 450a8f0..c8f045f 100644 --- a/src/app/login/login.component.ts +++ b/src/app/login/login.component.ts @@ -5,7 +5,6 @@ class LoginInformation { constructor(public username: string, public password: string) { - } } diff --git a/src/app/toolbar/toolbar.component.html b/src/app/toolbar/toolbar.component.html index b39e10b..4fa2bf0 100644 --- a/src/app/toolbar/toolbar.component.html +++ b/src/app/toolbar/toolbar.component.html @@ -1,7 +1,7 @@ Lukas Eisenhauer - Log In + Log In diff --git a/package-lock.json b/package-lock.json index 5807647..bdd0e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -13982,6 +13983,15 @@ "node": ">=10.13.0" } }, + "node_modules/sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==", + "funding": { + "type": "individual", + "url": "https://sweetalert2.github.io/#donations" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -26196,6 +26206,11 @@ "stable": "^0.1.8" } }, + "sweetalert2": { + "version": "11.4.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.8.tgz", + "integrity": "sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/package.json b/package.json index 2c9a675..4b2ebf5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "hammerjs": "^2.0.8", "rxjs": "~6.6.0", "sha.js": "^2.4.11", + "sweetalert2": "^11.4.8", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/src/app/account.service.ts b/src/app/account.service.ts index 2b64b6a..7f602df 100644 --- a/src/app/account.service.ts +++ b/src/app/account.service.ts @@ -3,6 +3,7 @@ import { environment } from 'src/environments/environment'; import { sha256 } from 'sha.js'; import { Router } from '@angular/router'; +import Swal from 'sweetalert2'; class Answer { success: Boolean = false @@ -32,14 +33,17 @@ }) export class AccountService { public username = '?'; - private userId: bigint = BigInt(0); - private userToken: bigint = BigInt(0); + private callbacks: Function[] = [] + + subscribe(f: Function) { + this.callbacks.push(f) + } constructor(private http: HttpClient, private router: Router) { this.updateUserInfo() } - public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + getPresalt(then: Function, onFail: Function = () => {}, onConnectionError: Function = () => {}, username: string = this.username) { this.http.post(`${environment.apiURL}/users/presalt`, {username}) .subscribe((answer: Object) => { var presaltAnswer = Object.assign(new PresaltAnswer(), answer) @@ -47,50 +51,86 @@ onFail() return } - // this presalting and hashing is done to protect the user's password in case of a man-in-the-middle-attack, - // where an attacker might be able to intercept a password if not hashed before. The password still has to be - // hashed on the server-side to be sure but this method prevents dumb users from having their passweord - // they use everywhere easily leaked, because you still need to crack this one layer first - // also, typical automated hacking software will hopefully be confused by my weird code - var passwordHash = new sha256().update(password + presaltAnswer.presalt).digest('hex') + then(presaltAnswer.presalt) + }, () => { + onConnectionError() + }) + } + + public login(username: string, password: string, onFail: Function, onConnectionError: Function) { + this.setUser('') + localStorage.removeItem('userToken') + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(password + presalt).digest('hex') this.http.post(`${environment.apiURL}/users/authenticate`, {username, passwordHash}) .subscribe((answer) => { var authenticateAnswer = Object.assign(new AuthenticateAnswer(), answer) - var loginSuccess = authenticateAnswer.success == true + var loginSuccess = authenticateAnswer.success == true && authenticateAnswer.token.length > 0 if (loginSuccess) { localStorage.setItem('userToken', authenticateAnswer.token) + this.setUser(username) this.updateUserInfo() - this.username = username this.router.navigate(['admin']) } else { onFail() } - }, (error) => {onConnectionError()}) - }, (error) => {onConnectionError()}) + }, () => {onConnectionError()}) + }, onFail, onConnectionError, username) } - updateUserInfo() { + updateUserInfo(then: Function = () => {}) { var token = localStorage.getItem('userToken') if (token == null) { - this.username = '' + this.setUser('') return } - this.http.post(`${environment.apiURL}/users/checkToken`, {token}).subscribe((answer: Object) => { + var data = JSON.parse(atob(token?.split('.')[1] as string)) + var tokenData = Object.assign(new TokenData(), data) + this.setUser(tokenData.usr) + this.http.post(`${environment.apiURL}/users/checkToken`, {token, username: this.username}) + .subscribe((answer: Object) => { var checkTokenAnswer = Object.assign(new CheckTokenAnswer(), answer) - if (checkTokenAnswer.valid == true) { - var data = JSON.parse(atob(token?.split('.')[1] as string)) - var tokenData = Object.assign(new TokenData(), data) - this.username = tokenData.usr + if (checkTokenAnswer.valid) { } else { - this.username = '' + this.router.navigate(['/']) + this.setUser('') } + then() }, () => { - this.username = '' + this.router.navigate(['/']) + this.setUser('') }) } logout() { - this.username = '' + this.setUser('') localStorage.removeItem('userToken') } + + changePassword(newPassword: string, then: Function, onConnectionError: Function) { + this.getPresalt((presalt: string) => { + var passwordHash = new sha256().update(newPassword + presalt).digest('hex') + this.http.post(`${environment.apiURL}/users/changePassword`, + {username: this.username, passwordHash, token: localStorage.getItem('userToken')}).subscribe((answer) => { + var status = Object.assign(new Answer(), answer) + if (status.success) { + Swal.fire('Password changed!', 'Watch out, \nyour old password won\'t work anymore', 'success') + } else { + Swal.fire('Error!', 'Something went wrong, bad user token?', 'error') + } + then() + }) + }, onConnectionError = onConnectionError) + // if you get a on Error, you have done something wrong + } + + setUser(username: string) { + if (username == this.username) { + return + } + this.username = username + this.callbacks.forEach(f => { + f(this.username) + }); + } } diff --git a/src/app/admin/admin.component.html b/src/app/admin/admin.component.html index f949c13..726150d 100644 --- a/src/app/admin/admin.component.html +++ b/src/app/admin/admin.component.html @@ -1,6 +1,25 @@ -

Hello {{account.username}}!

-

Welcome to the admin panel

- -

There is nothing to do here yet...

- - \ No newline at end of file + + +

Hello {{account.username}}!

+

Welcome to the admin panel

+
+ +

Change password

+
+ + New password + this field must not be empty + + + + Retype new password + this field must not be empty + + +
+ please type your new password the same way twice. +
+ +
+
+
\ No newline at end of file diff --git a/src/app/admin/admin.component.scss b/src/app/admin/admin.component.scss index e69de29..8286984 100644 --- a/src/app/admin/admin.component.scss +++ b/src/app/admin/admin.component.scss @@ -0,0 +1,3 @@ +.full-width { + width: 100%; +} \ No newline at end of file diff --git a/src/app/admin/admin.component.ts b/src/app/admin/admin.component.ts index 85273be..189d680 100644 --- a/src/app/admin/admin.component.ts +++ b/src/app/admin/admin.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { AccountService } from '../account.service'; @@ -8,13 +9,38 @@ styleUrls: ['./admin.component.scss'] }) export class AdminComponent implements OnInit { + public passwordForm: FormGroup + public passwordError = false + public username = '' - constructor(public account: AccountService, private router: Router) { - if (account.username.length == 0) { - router.navigate(['login']) - } + constructor(public account: AccountService, router: Router, private formBuilder: FormBuilder) { + + this.passwordForm = this.formBuilder.group({ + password: ['', Validators.required], + password_repeat: ['', Validators.required] + }) + account.subscribe((username: string) => { + if (account.username.length == 0) { + router.navigate(['login']) + } + this.username = username + }) } ngOnInit(): void { } + + onPasswordSubmit() { + if (this.passwordForm.invalid) { + return + } + this.passwordError = this.passwordForm.controls['password'].value != + this.passwordForm.controls['password_repeat'].value + if (this.passwordError) { + return + } + this.account.changePassword(this.passwordForm.controls['password'].value, () => { + this.passwordForm.reset() + }, () => {}) + } } diff --git a/src/app/grid/grid.component.scss b/src/app/grid/grid.component.scss index c26b381..b35b5cc 100644 --- a/src/app/grid/grid.component.scss +++ b/src/app/grid/grid.component.scss @@ -102,6 +102,6 @@ } } -:host ::ng-deep a:hover>img { +:host ::ng-deep mat-card:hover>img { filter: grayscale(0); } \ No newline at end of file diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index f264a1d..6bbb28e 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -12,8 +12,8 @@

Tree-os

-
+

Tree os is a hobby operating system I mainly develop just for the learning experience. As of now, it supports some multitasking and I am currently in the process of adding a file system and can already read files! @@ -28,8 +28,8 @@

Traffic simulator

- +

For school, I have to write a scientific article about something regarding "everyday physics". I am focusing on the design of urban junctions and need a simulation for cars traversing different designs. You can find the current code on my git. diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts index 450a8f0..c8f045f 100644 --- a/src/app/login/login.component.ts +++ b/src/app/login/login.component.ts @@ -5,7 +5,6 @@ class LoginInformation { constructor(public username: string, public password: string) { - } } diff --git a/src/app/toolbar/toolbar.component.html b/src/app/toolbar/toolbar.component.html index b39e10b..4fa2bf0 100644 --- a/src/app/toolbar/toolbar.component.html +++ b/src/app/toolbar/toolbar.component.html @@ -1,7 +1,7 @@ Lukas Eisenhauer - Log In + Log In diff --git a/src/app/toolbar/toolbar.component.ts b/src/app/toolbar/toolbar.component.ts index 400ceb5..f05f027 100644 --- a/src/app/toolbar/toolbar.component.ts +++ b/src/app/toolbar/toolbar.component.ts @@ -8,8 +8,13 @@ styleUrls: ['./toolbar.component.scss'] }) export class ToolbarComponent implements OnInit { - - constructor(public account: AccountService) { } + public username: string + constructor(public account: AccountService) { + account.subscribe((username: string) => { + this.username = username + }) + this.username = account.username + } ngOnInit(): void { }