Refonte du stockage des données utilisateur en sessionStorage et non avec des cookies

This commit is contained in:
guams 2025-04-17 22:35:39 +02:00
parent 3644414873
commit 3c4053ffcc
14 changed files with 118 additions and 98 deletions

View File

@ -30,12 +30,7 @@
"styles": [ "styles": [
"src/styles.css" "src/styles.css"
], ],
"scripts": [], "scripts": []
"server": "src/main.server.ts",
"prerender": true,
"ssr": {
"entry": "server.ts"
}
}, },
"configurations": { "configurations": {
"production": { "production": {

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import {Injectable} from '@angular/core';
import { CookieService } from 'ngx-cookie-service'; import {CookieService} from 'ngx-cookie-service';
import { Author } from './models/author'; import {Author} from './models/author';
import { BehaviorSubject } from 'rxjs'; import {BehaviorSubject} from 'rxjs';
import {DateTime} from 'luxon'; import {DateTime} from 'luxon';
@Injectable({ @Injectable({
@ -16,28 +16,33 @@ export class AuthService {
} }
isAuthenticated(): boolean { isAuthenticated(): boolean {
return this.cookieService.check("author") && return sessionStorage.getItem("author") !== null &&
this.cookieService.check("token") && sessionStorage.getItem("token") !== null &&
this.cookieService.check("token-expiration-date") && sessionStorage.getItem("token-expiration-date") !== null;
this.cookieService.get("author") !== '' &&
this.cookieService.get("token-expiration-date") !== '' &&
this.cookieService.get("token") !== '';
} }
getTokenExpirationDate(): DateTime { getTokenExpirationDate(): string | null {
return DateTime.fromISO(this.cookieService.get("token-expiration-date")); return sessionStorage.getItem("token-expiration-date");
} }
isSessionExpired(): boolean { isSessionExpired(): boolean {
return this.getTokenExpirationDate() < DateTime.now() && this.isAuthenticated(); const tokenExpirationDate = this.getTokenExpirationDate();
if (tokenExpirationDate) {
return DateTime.fromISO(tokenExpirationDate) < DateTime.now() && this.isAuthenticated();
}
return true
} }
getAuthenticatedAuthor(): Author { getAuthenticatedAuthor(): Author | null {
return JSON.parse(this.cookieService.get('author')); const authorStr = sessionStorage.getItem('author')
if (authorStr) {
return JSON.parse(authorStr);
}
return null;
} }
getAuthenticatedAuthorToken(): string { getAuthenticatedAuthorToken(): string | null{
return this.cookieService.get('token'); return sessionStorage.getItem('token');
} }
setSessionExpired(expired: boolean) { setSessionExpired(expired: boolean) {

View File

@ -36,8 +36,8 @@ export class CommentFormComponent {
} }
onSubmit() { onSubmit() {
let token: string = this.authService.getAuthenticatedAuthorToken(); let token = this.authService.getAuthenticatedAuthorToken();
let author: Author = this.authService.getAuthenticatedAuthor(); let author = this.authService.getAuthenticatedAuthor();
if (this.commentForm.valid && author && token && this.commentForm.value.content) { if (this.commentForm.valid && author && token && this.commentForm.value.content) {
// get l'image de profile après avoir créé le commentaire // get l'image de profile après avoir créé le commentaire
this.subs.push(this.commentService.create(this.commentForm.value.content, this.postId, author.id, token).subscribe({ this.subs.push(this.commentService.create(this.commentForm.value.content, this.postId, author.id, token).subscribe({

View File

@ -25,8 +25,9 @@ export class HeaderComponent {
} }
private initializeMenu(): void { private initializeMenu(): void {
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) { const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
this.actualAuthor = this.authService.getAuthenticatedAuthor(); if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated() && authenticatedAuthor) {
this.actualAuthor = authenticatedAuthor;
} }
if (this.actualAuthor) { if (this.actualAuthor) {

View File

@ -105,9 +105,9 @@ export class PostFormComponent implements OnDestroy {
if (this.isUpdateMode && this.postId) { if (this.isUpdateMode && this.postId) {
this.subs.push( this.subs.push(
this.postService.updatePost(this.postId, postData, this.authService.getAuthenticatedAuthorToken()).pipe( this.postService.updatePost(this.postId, postData, this.authService.getAuthenticatedAuthorToken()!).pipe(
mergeMap((_) => { mergeMap((_) => {
return this.postService.changeIllustration(this.postId, this.uploadedFile, this.authService.getAuthenticatedAuthorToken()); return this.postService.changeIllustration(this.postId, this.uploadedFile, this.authService.getAuthenticatedAuthorToken()!);
}) })
).subscribe({ ).subscribe({
next: (_) => { next: (_) => {
@ -118,11 +118,11 @@ export class PostFormComponent implements OnDestroy {
); );
} else { } else {
this.subs.push( this.subs.push(
this.postService.createPost(postData, this.authService.getAuthenticatedAuthorToken()).pipe( this.postService.createPost(postData, this.authService.getAuthenticatedAuthorToken()!).pipe(
mergeMap(post => mergeMap(post =>
this.authorService.attributePost(this.actualAuthor?.id, post.id, this.authService.getAuthenticatedAuthorToken()).pipe( this.authorService.attributePost(this.actualAuthor?.id, post.id, this.authService.getAuthenticatedAuthorToken()!).pipe(
mergeMap((_) => mergeMap((_) =>
this.postService.changeIllustration(post.id, this.uploadedFile, this.authService.getAuthenticatedAuthorToken()), this.postService.changeIllustration(post.id, this.uploadedFile, this.authService.getAuthenticatedAuthorToken()!),
) )
) )
) )

View File

@ -37,7 +37,7 @@ export class RegisterFormComponent implements OnDestroy {
]; ];
subs: Subscription[] = []; subs: Subscription[] = [];
form: FormGroup; form: FormGroup;
actualAuthor: Author | undefined; actualAuthor: string | undefined;
constructor(private formBuilder: FormBuilder, constructor(private formBuilder: FormBuilder,
private authorService: AuthorService, private authorService: AuthorService,
@ -45,8 +45,9 @@ export class RegisterFormComponent implements OnDestroy {
private messageService: MessageService, private messageService: MessageService,
private authService: AuthService, private authService: AuthService,
) { ) {
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) { const authenticatedAuthor = this.authService.getAuthenticatedAuthorToken();
this.actualAuthor = this.authService.getAuthenticatedAuthor(); if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated() && authenticatedAuthor) {
this.actualAuthor = authenticatedAuthor;
} }
this.form = this.formBuilder.group({ this.form = this.formBuilder.group({
username: ['', [Validators.required, Validators.maxLength(255)]], username: ['', [Validators.required, Validators.maxLength(255)]],
@ -83,7 +84,7 @@ export class RegisterFormComponent implements OnDestroy {
this.username, this.username,
this.password, this.password,
this.role, this.role,
this.authService.getAuthenticatedAuthorToken()).subscribe({ this.authService.getAuthenticatedAuthorToken()!).subscribe({
next: (author: Author) => { next: (author: Author) => {
this.successMessage('Succès', `Auteur ${author.name} créé avec succès`); this.successMessage('Succès', `Auteur ${author.name} créé avec succès`);
this.createdAuthor.emit(author); this.createdAuthor.emit(author);

View File

@ -80,7 +80,7 @@ export class UpdateProfileFormComponent implements OnDestroy {
} }
onSubmit() { onSubmit() {
const token: string = this.authService.getAuthenticatedAuthorToken(); const token = this.authService.getAuthenticatedAuthorToken();
if (this.form.valid && token && this.password === this.passwordConfirm) { if (this.form.valid && token && this.password === this.passwordConfirm) {
const newUsername = this.form.value.username; const newUsername = this.form.value.username;
if (this.uploadedFile) { if (this.uploadedFile) {

View File

@ -1,17 +1,18 @@
import {CanActivateFn, Router} from '@angular/router'; import {CanActivateFn, Router} from '@angular/router';
import {inject} from '@angular/core'; import {inject} from '@angular/core';
import {CookieService} from 'ngx-cookie-service';
import {AuthService} from '../auth.service'; import {AuthService} from '../auth.service';
import {Role} from '../models/role'; import {Role} from '../models/role';
export const writerGuard: CanActivateFn = (route, state) => { export const writerGuard: CanActivateFn = (route, state) => {
const router = inject(Router); const router = inject(Router);
const cookieService = inject(CookieService);
const authService = inject(AuthService); const authService = inject(AuthService);
const authorStr = sessionStorage.getItem("author");
if ((authService.isAuthenticated() && JSON.parse(cookieService.get("author")).role !== Role.WRITER) || !authService.isAuthenticated()) { if (authorStr) {
router.navigate(['/']); if ((authService.isAuthenticated() && JSON.parse(authorStr).role !== Role.WRITER) || !authService.isAuthenticated()) {
router.navigate(['/']);
}
return true;
} }
return false;
return true;
}; };

View File

@ -29,9 +29,9 @@ export class HomeComponent implements OnDestroy {
constructor( constructor(
private postService: PostService, private postService: PostService,
private authService: AuthService) { private authService: AuthService) {
const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) { if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated() && authenticatedAuthor) {
this.actualAuthor = this.authService.getAuthenticatedAuthor(); this.actualAuthor = authenticatedAuthor;
} else { } else {
this.authService.checkSessionExpiration(); this.authService.checkSessionExpiration();
} }

View File

@ -35,9 +35,9 @@ export class LoginComponent implements OnDestroy {
constructor(private authorService: AuthorService, constructor(private authorService: AuthorService,
private messageService: MessageService, private messageService: MessageService,
private cookieService: CookieService,
private router: Router, private router: Router,
private configurationService: ConfigurationService,) {} private configurationService: ConfigurationService,) {
}
sendLogins(): void { sendLogins(): void {
if (this.password === this.confirmPassword) { if (this.password === this.confirmPassword) {
@ -45,26 +45,15 @@ export class LoginComponent implements OnDestroy {
( (
this.authorService.login(this.name, this.password).pipe( this.authorService.login(this.name, this.password).pipe(
switchMap((tokenObj: any) => { switchMap((tokenObj: any) => {
this.cookieService.delete('token', '/', this.configurationService.getDomain()) // sessionStorage.removeItem('token');
this.cookieService.set("token", tokenObj.token, { sessionStorage.setItem('token', tokenObj.token);
domain: this.configurationService.getDomain(),
secure: true,
path: '/'
});
return this.authorService.me(tokenObj.token) return this.authorService.me(tokenObj.token)
})) }))
.subscribe({ .subscribe({
next: (author: Author) => { next: (author: Author) => {
this.cookieService.delete('author', '/', this.configurationService.getDomain()) // sessionStorage.removeItem('author');
this.cookieService.set("author", JSON.stringify(author), { sessionStorage.setItem('author', JSON.stringify(author));
domain: this.configurationService.getDomain(), sessionStorage.setItem('token-expiration-date', DateTime.now().plus({millisecond: this.configurationService.getTokenTTL()}).toISO())
secure : true,
path: '/' });
this.cookieService.set('token-expiration-date', DateTime.now().plus({millisecond: this.configurationService.getTokenTTL()}).toISO(), {
domain: this.configurationService.getDomain(),
secure: true,
path: '/',
})
this.getAuthorCookie(); this.getAuthorCookie();
this.router.navigate(['/']).then(() => { this.router.navigate(['/']).then(() => {
this.successMessage('Connecté avec succès', 'Heureux de vous revoir ' + this.actualAuthor?.name) this.successMessage('Connecté avec succès', 'Heureux de vous revoir ' + this.actualAuthor?.name)
@ -79,7 +68,10 @@ export class LoginComponent implements OnDestroy {
} }
getAuthorCookie(): void { getAuthorCookie(): void {
this.actualAuthor = JSON.parse(this.cookieService.get("author")); const authorStr = sessionStorage.getItem('author');
if (authorStr) {
this.actualAuthor = JSON.parse(authorStr);
}
} }
successMessage(summary: string, detail: string): void { successMessage(summary: string, detail: string): void {

View File

@ -37,14 +37,17 @@ export class MyPostsComponent implements OnDestroy {
updateDialogVisibility: boolean[] = []; updateDialogVisibility: boolean[] = [];
deleteDialogVisibility: boolean[] = []; deleteDialogVisibility: boolean[] = [];
posts: Post[] = []; posts: Post[] = [];
actualAuthor: Author; actualAuthor: Author | undefined;
constructor(private authService: AuthService, constructor(private authService: AuthService,
private postService: PostService, private postService: PostService,
private authorService: AuthorService, private authorService: AuthorService,
private messageService: MessageService) { private messageService: MessageService) {
this.actualAuthor = this.authService.getAuthenticatedAuthor(); const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
if (authenticatedAuthor) {
this.actualAuthor = authenticatedAuthor;
}
this.updatePosts(); this.updatePosts();
} }
@ -59,8 +62,9 @@ export class MyPostsComponent implements OnDestroy {
} }
updatePosts(): void { updatePosts(): void {
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) { const authorToken = this.authService.getAuthenticatedAuthorToken()
this.authorService.getAuthorsPosts(this.actualAuthor?.id, this.authService.getAuthenticatedAuthorToken()).subscribe({ if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated() && authorToken) {
this.authorService.getAuthorsPosts(this.actualAuthor?.id, authorToken).subscribe({
next: posts => this.posts = posts, next: posts => this.posts = posts,
error: error => this.failureMessage("Erreur", error.error.message), error: error => this.failureMessage("Erreur", error.error.message),
} }
@ -71,13 +75,16 @@ export class MyPostsComponent implements OnDestroy {
} }
deletePost(id: bigint, rowIndex: number) { deletePost(id: bigint, rowIndex: number) {
this.postService.deletePost(id, this.authService.getAuthenticatedAuthorToken()).subscribe({ const authorToken = this.authService.getAuthenticatedAuthorToken()
next: (_) => { if (authorToken) {
this.updatePosts() this.postService.deletePost(id, authorToken).subscribe({
this.successMessage("Post supprimé", "Ce post a été supprimé avec succès") next: (_) => {
}, this.updatePosts()
error: error => this.failureMessage("Erreur", error.error.message), this.successMessage("Post supprimé", "Ce post a été supprimé avec succès")
}); },
error: error => this.failureMessage("Erreur", error.error.message),
});
}
this.closeDialog(this.deleteDialogVisibility, rowIndex) this.closeDialog(this.deleteDialogVisibility, rowIndex)
} }

View File

@ -40,7 +40,7 @@ export class NewPostComponent implements OnDestroy {
private postService: PostService, private postService: PostService,
private authorService: AuthorService, private authorService: AuthorService,
private messageService: MessageService, private messageService: MessageService,
private authService : AuthService, private authService: AuthService,
private router: Router) { private router: Router) {
this.form = this.formBuilder.group({ this.form = this.formBuilder.group({
description: ['', [Validators.required, Validators.maxLength(512)]], description: ['', [Validators.required, Validators.maxLength(512)]],
@ -49,7 +49,10 @@ export class NewPostComponent implements OnDestroy {
category: ['', [Validators.required, Validators.maxLength(50)]], category: ['', [Validators.required, Validators.maxLength(50)]],
}); });
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) { if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
this.actualAuthor = this.authService.getAuthenticatedAuthor(); const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
if (authenticatedAuthor) {
this.actualAuthor = authenticatedAuthor;
}
} else { } else {
this.authService.checkSessionExpiration(); this.authService.checkSessionExpiration();
} }
@ -72,26 +75,31 @@ export class NewPostComponent implements OnDestroy {
category: formData.category as string category: formData.category as string
}; };
this.subs.push( const authenticatedAuthor = this.authService.getAuthenticatedAuthorToken();
this.postService.createPost(postToPost, this.authService.getAuthenticatedAuthorToken()).pipe( if (authenticatedAuthor) {
mergeMap(post => this.subs.push(
this.authorService.attributePost(this.actualAuthor?.id, post.id, this.authService.getAuthenticatedAuthorToken()).pipe( this.postService.createPost(postToPost, authenticatedAuthor).pipe(
mergeMap((_) => mergeMap(post =>
this.postService.changeIllustration(post.id, this.uploadedFile, this.authService.getAuthenticatedAuthorToken()), this.authorService.attributePost(this.actualAuthor?.id, post.id, authenticatedAuthor).pipe(
mergeMap((_) =>
this.postService.changeIllustration(post.id, this.uploadedFile, authenticatedAuthor),
)
) )
) )
) ).subscribe({
).subscribe({ next: () => {
next: () => { this.router.navigate(['/']).then(() => {
this.router.navigate(['/']).then(() => { this.successMessage('Succès', 'Post créé avec succès')
this.successMessage('Succès', 'Post créé avec succès') });
}); },
}, error: (err) => {
error: (err) => { this.failureMessage('Erreur', err.error.message);
this.failureMessage('Erreur', err.error.message); }
} })
}) );
); } else {
console.error("Profil mal chargé")
}
} }
} }

View File

@ -49,7 +49,12 @@ export class PostComponent {
private authService: AuthService,) { private authService: AuthService,) {
this.route.paramMap.subscribe(params => { this.route.paramMap.subscribe(params => {
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) { if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
this.actualAuthor = this.authService.getAuthenticatedAuthor(); const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
if (authenticatedAuthor) {
this.actualAuthor = authenticatedAuthor;
} else {
console.error('Profil mal chargé');
}
} else { } else {
this.authService.checkSessionExpiration(); this.authService.checkSessionExpiration();
} }

View File

@ -43,7 +43,12 @@ export class ProfileComponent implements OnDestroy {
})); }));
}) })
if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) { if (!(this.authService.isSessionExpired()) && this.authService.isAuthenticated()) {
this.actualAuthor = this.authService.getAuthenticatedAuthor(); const authenticatedAuthor = this.authService.getAuthenticatedAuthor();
if (authenticatedAuthor) {
this.actualAuthor = authenticatedAuthor;
} else {
console.error("Profil mal chargé");
}
} else { } else {
this.authService.checkSessionExpiration(); this.authService.checkSessionExpiration();
} }