Upgrade to proper user management
All checks were successful
Jenkins Production Deployment

This commit is contained in:
Patrick Müller 2023-05-14 21:08:55 +02:00
parent c772b01e7d
commit 9ed6f9968e
Signed by: Paddy
GPG Key ID: 37ABC11275CAABCE
6 changed files with 179 additions and 26 deletions

View File

@ -0,0 +1,4 @@
export interface Session {
sessionId: number;
sessionKey: string;
}

7
src/app/models/user.ts Normal file
View File

@ -0,0 +1,7 @@
export interface User {
userId: number;
fullName: string;
passwordHash: string;
email: string;
isActive: boolean;
}

View File

@ -1,12 +1,30 @@
<div *ngIf="!isLoggedIn"> <div *ngIf="!isLoggedIn">
<p>Please log in:</p> <p>Please log in:</p>
<label for="email">Your @nachklang.art email: </label>
<input id="email" type="text" aria-label="Your Email" [(ngModel)]="email"><br>
<label for="password">Password: </label> <label for="password">Password: </label>
<input id="password" type="password" aria-label="Password" (keyup.enter)="login()" [(ngModel)]="password"><br> <input id="password" type="password" aria-label="Password" (keyup.enter)="login()" [(ngModel)]="password"><br>
<button (click)="login()">Login</button>
<br><br>
<p>If you dont' have an account yet, please use the following form to register:</p>
<label for="name">Your full name: </label> <label for="name">Your full name: </label>
<input id="name" type="text" aria-label="Your Name" (keyup.enter)="login()" [(ngModel)]="name"> <input id="name" type="text" aria-label="Your Name" [(ngModel)]="name"><br>
<label for="registerEmail">Your @nachklang.art email: </label>
<input id="registerEmail" type="text" aria-label="Your Email" [(ngModel)]="registerEmail"><br>
<label for="registerPassword">Password: </label>
<input id="registerPassword" type="password" aria-label="Password" [(ngModel)]="registerPassword"><br>
<label for="registerPasswordConfirm">Confirm password: </label>
<input id="registerPasswordConfirm" type="password" aria-label="Password" (keyup.enter)="register()" [(ngModel)]="registerPasswordConfirm"><br>
<p *ngIf="!checkPasswordPolicy()">Passwords have to use uppercase and lowercase letters, numbers and must have at least 12 characters!</p>
<p *ngIf="!checkPasswordsMatch()">Passwords do not match!</p>
<button (click)="register()">Register</button>
</div> </div>
<div *ngIf="isLoggedIn"> <div *ngIf="isLoggedIn">
<span>Logged in as {{getUserName()}}&nbsp; | &nbsp;</span> <span>Logged in as {{getUserName()}}</span>
<span *ngIf="checkUserInactive()">&nbsp;(inactive)</span>
<span>&nbsp;</span>
<button (click)="logout()">Logout</button>
<span>&nbsp; | &nbsp;</span>
<span>Calendar: </span> <span>Calendar: </span>
<select [(ngModel)]="selectedCalendar" (change)="handleCalendarChange()"> <select [(ngModel)]="selectedCalendar" (change)="handleCalendarChange()">
<option value="" disabled selected hidden>Select calendar</option> <option value="" disabled selected hidden>Select calendar</option>

View File

@ -2,6 +2,8 @@ import {Component, OnInit} from '@angular/core';
import {ApiService} from '../../services/api.service'; import {ApiService} from '../../services/api.service';
import {UtilsService} from '../../services/utils.service'; import {UtilsService} from '../../services/utils.service';
import {Event} from '../../models/event'; import {Event} from '../../models/event';
import {Session} from '../../models/session';
import {User} from '../../models/user';
@Component({ @Component({
selector: 'app-admin', selector: 'app-admin',
@ -15,8 +17,13 @@ export class AdminComponent implements OnInit {
selectedCalendar: string = ''; selectedCalendar: string = '';
password: string = ''; password: string = '';
name: string = ''; name: string = '';
email: string = '';
eventFilter: string = 'all'; eventFilter: string = 'all';
eventSorting: string = 'start_asc'; eventSorting: string = 'start_asc';
isActive: boolean = false;
registerEmail: string = '';
registerPassword: string = '';
registerPasswordConfirm: string = '';
constructor( constructor(
private api: ApiService private api: ApiService
@ -24,9 +31,15 @@ export class AdminComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
if (UtilsService.getPasswordFromLocalStorage() !== '') { if (UtilsService.getSessionInfoFromLocalStorage().sessionId !== -1) {
this.isLoggedIn = true; this.api.checkSession(UtilsService.getSessionInfoFromLocalStorage()).subscribe((user: User) => {
this.getEvents(); if(user.userId != null && user.userId !== -1) {
this.isLoggedIn = true;
this.name = user.fullName;
this.isActive = user.isActive;
this.getEvents();
}
});
} }
} }
@ -51,12 +64,6 @@ export class AdminComponent implements OnInit {
}); });
} }
login(): void {
UtilsService.saveUserInfoToLocalStorage(this.password, this.name);
this.isLoggedIn = true;
this.getEvents();
}
handleCalendarChange() { handleCalendarChange() {
this.getEvents(); this.getEvents();
} }
@ -105,7 +112,7 @@ export class AdminComponent implements OnInit {
} }
getUserName(): string { getUserName(): string {
return UtilsService.getNameFromLocalStorage(); return this.name;
} }
sortEvents(): void { sortEvents(): void {
@ -127,4 +134,54 @@ export class AdminComponent implements OnInit {
return 1; return 1;
}); });
} }
login(): void {
this.api.login(this.email, this.password).subscribe((session: Session): void => {
console.log(session);
if(session.sessionId != null && session.sessionId !== -1) {
UtilsService.saveSessionInfoToLocalStorage(session.sessionId, session.sessionKey);
this.isLoggedIn = true;
this.getEvents();
}
});
}
register(): void {
this.api.register(this.registerEmail, this.name, this.registerPassword).subscribe((session: Session): void => {
if(session.sessionId != null && session.sessionId !== -1) {
UtilsService.saveSessionInfoToLocalStorage(session.sessionId, session.sessionKey);
this.isLoggedIn = true;
this.getEvents();
confirm('Please talk to Patrick to activate your account. You can\'t use this application before that.')
}
});
}
logout(): void {
UtilsService.clearSessionInfo();
this.isLoggedIn = false;
}
checkUserInactive(): boolean {
return !this.isActive;
}
checkPasswordsMatch(): boolean {
return this.registerPassword === this.registerPasswordConfirm;
}
checkPasswordPolicy(): boolean {
let isLongEnough = this.registerPassword.length >= 12;
var lowercaseRegex = /[a-z]/g
let hasLowercase = lowercaseRegex.test(this.registerPassword);
var uppercaseRegex = /[A-Z]/g
let hasUppercase = uppercaseRegex.test(this.registerPassword);
var numberRegex = /[0-9]/g
let hasNumbers = numberRegex.test(this.registerPassword);
return isLongEnough && hasLowercase && hasUppercase && hasNumbers;
}
} }

View File

@ -4,24 +4,66 @@ import {Observable} from 'rxjs';
import {Event} from '../models/event'; import {Event} from '../models/event';
import {UtilsService} from './utils.service'; import {UtilsService} from './utils.service';
import { environment } from './../../environments/environment'; import { environment } from './../../environments/environment';
import {Session} from '../models/session';
import {User} from '../models/user';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class ApiService { export class ApiService {
apiUrl = environment.apiUrl + '/calendar/events/'; apiUrl = environment.apiUrl + '/calendar/events/';
userApiUrl = environment.apiUrl + '/calendar/users/';
constructor( constructor(
private http: HttpClient private http: HttpClient
) { ) {
} }
register(email: string, fullName: string, password: string): Observable<Session> {
try {
let registerEvent: any = {
"email": email,
"fullName": fullName,
"password": password
};
return this.http.post<Session>(this.userApiUrl + 'register', registerEvent);
} catch (exception) {
console.log('Error fetching events from API');
}
return new Observable<Session>();
}
login(email: string, password: string): Observable<Session> {
try {
let loginEvent: any = {
"email": email,
"password": password
};
return this.http.post<Session>(this.userApiUrl + 'login', loginEvent);
} catch (exception) {
console.log('Error fetching events from API');
}
return new Observable<Session>();
}
checkSession(session: Session): Observable<User> {
try {
return this.http.post<User>(this.userApiUrl + 'checkSessionValid', session);
} catch (exception) {
console.log('Error fetching events from API');
}
return new Observable<User>();
}
getEvents(calendar: string): Observable<Event[]> { getEvents(calendar: string): Observable<Event[]> {
try { try {
let password = UtilsService.getPasswordFromLocalStorage(); let session = UtilsService.getSessionInfoFromLocalStorage();
let params = new HttpParams(); let params = new HttpParams();
params = params.append('password', password); params = params.append('sessionId', session.sessionId);
params = params.append('sessionKey', session.sessionKey);
return this.http.get<Event[]>((this.apiUrl + calendar + '/json'), {params}); return this.http.get<Event[]>((this.apiUrl + calendar + '/json'), {params});
} catch (exception) { } catch (exception) {
console.log('Error fetching events from API'); console.log('Error fetching events from API');
@ -31,13 +73,15 @@ export class ApiService {
updateEvent(event: Event): Observable<any> { updateEvent(event: Event): Observable<any> {
try { try {
let password = UtilsService.getPasswordFromLocalStorage(); let session = UtilsService.getSessionInfoFromLocalStorage();
let params = new HttpParams();
params = params.append('sessionId', session.sessionId);
params = params.append('sessionKey', session.sessionKey);
let updateEvent: any = event; let updateEvent: any = event;
updateEvent.password = password; return this.http.put(this.apiUrl + updateEvent.eventId, updateEvent, {params});
return this.http.put(this.apiUrl + updateEvent.eventId, updateEvent);
} catch (exception) { } catch (exception) {
console.log('Error updating event'); console.log('Error updating event');
} }
@ -46,13 +90,15 @@ export class ApiService {
createEvent(event: Event): Observable<any> { createEvent(event: Event): Observable<any> {
try { try {
let password = UtilsService.getPasswordFromLocalStorage(); let session = UtilsService.getSessionInfoFromLocalStorage();
let params = new HttpParams();
params = params.append('sessionId', session.sessionId);
params = params.append('sessionKey', session.sessionKey);
let createEvent: any = event; let createEvent: any = event;
createEvent.password = password; return this.http.post(this.apiUrl, createEvent, {params});
return this.http.post(this.apiUrl, createEvent);
} catch (exception) { } catch (exception) {
console.log('Error creating event'); console.log('Error creating event');
} }
@ -61,17 +107,20 @@ export class ApiService {
deleteEvent(event: Event): Observable<any> { deleteEvent(event: Event): Observable<any> {
try { try {
let password = UtilsService.getPasswordFromLocalStorage(); let session = UtilsService.getSessionInfoFromLocalStorage();
let params = new HttpParams();
params = params.append('sessionId', session.sessionId);
params = params.append('sessionKey', session.sessionKey);
let deleteEvent: any = event; let deleteEvent: any = event;
deleteEvent.password = password;
return this.http.delete(this.apiUrl + deleteEvent.eventId, { return this.http.delete(this.apiUrl + deleteEvent.eventId, {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: deleteEvent body: deleteEvent,
params
}); });
} catch (exception) { } catch (exception) {
console.log('Error deleting event'); console.log('Error deleting event');

View File

@ -1,4 +1,5 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {Session} from '../models/session';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -20,4 +21,21 @@ export class UtilsService {
static getNameFromLocalStorage(): string { static getNameFromLocalStorage(): string {
return localStorage.getItem('name') ?? ''; return localStorage.getItem('name') ?? '';
} }
static saveSessionInfoToLocalStorage(sessionId: number, sessionKey: string): void {
localStorage.setItem('sessionId', sessionId.toString());
localStorage.setItem('sessionKey', sessionKey);
}
static getSessionInfoFromLocalStorage(): Session {
return {
sessionId: parseInt((localStorage.getItem('sessionId') ?? '-1'), 10),
sessionKey: localStorage.getItem('sessionKey') ?? ''
}
}
static clearSessionInfo(): void {
localStorage.setItem('sessionId', '-1');
localStorage.setItem('sessionKey', '');
}
} }