forked from sh-edraft.de/sh_discord_bot
		
	Added verify-login #70
This commit is contained in:
		| @@ -6,6 +6,8 @@ from flask import request, jsonify, Response | ||||
|  | ||||
| from bot_api.abc.auth_service_abc import AuthServiceABC | ||||
| from bot_api.api import Api | ||||
| from bot_api.exception.service_error_code_enum import ServiceErrorCode | ||||
| from bot_api.exception.service_exception import ServiceException | ||||
| from bot_api.filter.auth_user_select_criteria import AuthUserSelectCriteria | ||||
| from bot_api.json_processor import JSONProcessor | ||||
| from bot_api.logging.api_logger import ApiLogger | ||||
| @@ -76,6 +78,19 @@ class AuthController: | ||||
|         result = await self._auth_service.login_async(dto) | ||||
|         return jsonify(result.to_dict()) | ||||
|  | ||||
|     @Route.get(f'{BasePath}/verify-login') | ||||
|     async def verify_login(self): | ||||
|         token = None | ||||
|         result = False | ||||
|         if 'Authorization' in request.headers: | ||||
|             bearer = request.headers.get('Authorization') | ||||
|             token = bearer.split()[1] | ||||
|  | ||||
|         if token is not None: | ||||
|             result = self._auth_service.verify_login(token) | ||||
|  | ||||
|         return jsonify(result) | ||||
|  | ||||
|     @Route.post(f'{BasePath}/forgot-password/<email>') | ||||
|     async def forgot_password(self, email: str): | ||||
|         await self._auth_service.forgot_password_async(email) | ||||
| @@ -108,7 +123,6 @@ class AuthController: | ||||
|         return jsonify(result.to_dict()) | ||||
|  | ||||
|     @Route.post(f'{BasePath}/revoke') | ||||
|     @Route.authorize | ||||
|     async def revoke(self): | ||||
|         dto: TokenDTO = JSONProcessor.process(TokenDTO, request.get_json(force=True, silent=True)) | ||||
|         await self._auth_service.revoke_async(dto) | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| from enum import Enum | ||||
|  | ||||
| from werkzeug.exceptions import Unauthorized | ||||
|  | ||||
|  | ||||
| class ServiceErrorCode(Enum): | ||||
|  | ||||
| @@ -17,3 +19,5 @@ class ServiceErrorCode(Enum): | ||||
|     ConnectionFailed = 8 | ||||
|     Timeout = 9 | ||||
|     MailError = 10 | ||||
|  | ||||
|     Unauthorized = 11 | ||||
|   | ||||
| @@ -1,12 +1,13 @@ | ||||
| from functools import wraps | ||||
| from typing import Optional | ||||
|  | ||||
| from flask import request | ||||
| from flask import request, jsonify | ||||
| from flask_cors import cross_origin | ||||
|  | ||||
| from bot_api.abc.auth_service_abc import AuthServiceABC | ||||
| from bot_api.exception.service_error_code_enum import ServiceErrorCode | ||||
| from bot_api.exception.service_exception import ServiceException | ||||
| from bot_api.model.error_dto import ErrorDTO | ||||
| from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC | ||||
|  | ||||
|  | ||||
| @@ -30,14 +31,20 @@ class Route: | ||||
|                 bearer = request.headers.get('Authorization') | ||||
|                 token = bearer.split()[1] | ||||
|  | ||||
|             if not token: | ||||
|                 raise ServiceException(ServiceErrorCode.InvalidData, f'Token not set') | ||||
|             if token is None: | ||||
|                 ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token not set') | ||||
|                 error = ErrorDTO(ex.error_code, ex.message) | ||||
|                 return jsonify(error.to_dict()), 401 | ||||
|  | ||||
|             if cls._auth_users is None or cls._auth is None: | ||||
|                 raise ServiceException(ServiceErrorCode.InvalidDependencies, f'Authorize is not initialized') | ||||
|                 ex = ServiceException(ServiceErrorCode.Unauthorized, f'Authorize is not initialized') | ||||
|                 error = ErrorDTO(ex.error_code, ex.message) | ||||
|                 return jsonify(error.to_dict()), 401 | ||||
|  | ||||
|             if not cls._auth.verify_login(token): | ||||
|                 raise ServiceException(ServiceErrorCode.InvalidUser, f'Token expired') | ||||
|                 ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token expired') | ||||
|                 error = ErrorDTO(ex.error_code, ex.message) | ||||
|                 return jsonify(error.to_dict()), 401 | ||||
|  | ||||
|             return await f(*args, **kwargs) | ||||
|  | ||||
|   | ||||
| @@ -12,5 +12,6 @@ export enum ServiceErrorCode { | ||||
|  | ||||
|     ConnectionFailed = 8, | ||||
|     Timeout = 9, | ||||
|     MailError = 10 | ||||
|     MailError = 10, | ||||
|     Unauthorized = 11 | ||||
| } | ||||
|   | ||||
| @@ -136,7 +136,7 @@ export class AuthUserComponent implements OnInit { | ||||
|   loadNextPage() { | ||||
|     this.authService.getFilteredUsers(this.searchCriterions).pipe(catchError(err => { | ||||
|       this.loading = false; | ||||
|       return throwError(err); | ||||
|       return throwError(() => err); | ||||
|     })).subscribe(list => { | ||||
|       this.totalRecords = list.totalCount; | ||||
|       this.users = list.users; | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { Router } from '@angular/router'; | ||||
| import { JwtHelperService } from '@auth0/angular-jwt'; | ||||
| import { Observable, Subscription } from 'rxjs'; | ||||
| import { firstValueFrom, Observable, Subscription } from 'rxjs'; | ||||
| import { catchError } from 'rxjs/operators'; | ||||
| import { AdminUpdateUserDTO } from 'src/app/models/auth/admin-update-user.dto'; | ||||
| import { AuthRoles } from 'src/app/models/auth/auth-roles.enum'; | ||||
| @@ -91,6 +91,14 @@ export class AuthService { | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   verifyLogin(): Observable<boolean> { | ||||
|     return this.http.get<boolean>(`${this.appsettings.getApiURL()}/api/auth/verify-login`, { | ||||
|       headers: new HttpHeaders({ | ||||
|         'Content-Type': 'application/json' | ||||
|       }) | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   forgotPassword(email: string): Observable<unknown> { | ||||
|     const emailJson = JSON.stringify(email); | ||||
|     return this.http.post(`${this.appsettings.getApiURL()}/api/auth/forgot-password`, emailJson, { | ||||
| @@ -141,27 +149,6 @@ export class AuthService { | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   logout(): Subscription | null { | ||||
|     const token = this.getToken(); | ||||
|     this.isLoggedIn = false; | ||||
|     localStorage.removeItem('jwt'); | ||||
|     localStorage.removeItem('rjwt'); | ||||
|     this.router.navigate(['/auth/login']); | ||||
|  | ||||
|     if (token && token.token && token.refreshToken) { | ||||
|       return this.http.post<TokenDTO>(`${this.appsettings.getApiURL()}/api/auth/revoke`, token, { | ||||
|         headers: new HttpHeaders({ | ||||
|           'Content-Type': 'application/json' | ||||
|         }) | ||||
|       }).pipe(catchError((error: any) => { | ||||
|         error.error = null; | ||||
|         throw error; | ||||
|       })).subscribe(); | ||||
|     } | ||||
|  | ||||
|     return null | ||||
|   } | ||||
|  | ||||
|   deleteUserByMail(mail: string) { | ||||
|     return this.http.post(`${this.appsettings.getApiURL()}/api/auth/delete-user-by-mail/${mail}`, { | ||||
|       headers: new HttpHeaders({ | ||||
| @@ -190,39 +177,55 @@ export class AuthService { | ||||
|     return this.jwtHelper.decodeToken(this.getToken().token); | ||||
|   } | ||||
|  | ||||
|   async isUserLoggedInAsync(): Promise<boolean> { | ||||
|     this.isLoggedIn = await this._isUserLoggedInAsync(); | ||||
|     return this.isLoggedIn; | ||||
|   logout(): Subscription | null { | ||||
|     const token = this.getToken(); | ||||
|  | ||||
|     if (token && token.token && token.refreshToken) { | ||||
|       return this.http.post<TokenDTO>(`${this.appsettings.getApiURL()}/api/auth/revoke`, token, { | ||||
|         headers: new HttpHeaders({ | ||||
|           'Content-Type': 'application/json' | ||||
|         }) | ||||
|       }).pipe(catchError((error: any) => { | ||||
|         error.error = null; | ||||
|         throw error; | ||||
|       })).subscribe(() => { | ||||
|         this.isLoggedIn = false; | ||||
|         localStorage.removeItem('jwt'); | ||||
|         localStorage.removeItem('rjwt'); | ||||
|         this.router.navigate(['/auth/login']); | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     return null | ||||
|   } | ||||
|  | ||||
|   private async _isUserLoggedInAsync(): Promise<boolean> { | ||||
|   async isUserLoggedInAsync(): Promise<boolean> { | ||||
|     const token = this.getToken(); | ||||
|  | ||||
|     if (!token || !token.refreshToken) { | ||||
|       this.isLoggedIn = false; | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     if (token.token && !this.jwtHelper.isTokenExpired(token.token)) { | ||||
|     const verifiedLogin = await firstValueFrom(await this.verifyLogin()); | ||||
|  | ||||
|     if (verifiedLogin) { | ||||
|       this.isLoggedIn = true; | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     if (this.isLoggedIn !== false) { | ||||
|     if (this.isLoggedIn) { | ||||
|       this.spinnerService.showSpinner(); | ||||
|       const resfreshedToken = await this.refresh(token) | ||||
|         .pipe(catchError((err: Error) => { | ||||
|           this.logout(); | ||||
|           this.spinnerService.hideSpinner(); | ||||
|           throw err; | ||||
|         })) | ||||
|         .toPromise(); | ||||
|  | ||||
|       const resfreshedToken = await firstValueFrom(await this.refresh(token)); | ||||
|  | ||||
|       this.spinnerService.hideSpinner(); | ||||
|       if (resfreshedToken) { | ||||
|         this.saveToken(resfreshedToken); | ||||
|         return true; | ||||
|       } | ||||
|     } | ||||
|     this.isLoggedIn = false; | ||||
|  | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -2,6 +2,8 @@ import { HttpErrorResponse } from '@angular/common/http'; | ||||
| import { ErrorHandler, Injectable, Injector } from '@angular/core'; | ||||
| import { Observable, throwError } from 'rxjs'; | ||||
| import { ErrorDTO } from 'src/app/models/error/error-dto'; | ||||
| import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum'; | ||||
| import { AuthService } from '../auth/auth.service'; | ||||
| import { ToastService } from '../toast/toast.service'; | ||||
|  | ||||
| @Injectable() | ||||
| @@ -17,6 +19,11 @@ export class ErrorHandlerService implements ErrorHandler { | ||||
|       let header = 'Fehler'; | ||||
|       const errorDto: ErrorDTO = error.error; | ||||
|  | ||||
|       if (errorDto.errorCode === ServiceErrorCode.Unauthorized) { | ||||
|         this.injector.get(AuthService).logout(); | ||||
|         return throwError(() => error); | ||||
|       } | ||||
|  | ||||
|       if (errorDto.errorCode !== undefined) { | ||||
|         header = 'Fehlercode: ' + errorDto.errorCode; | ||||
|       } | ||||
| @@ -29,6 +36,10 @@ export class ErrorHandlerService implements ErrorHandler { | ||||
|  | ||||
|       this.injector.get(ToastService).error(header, message); | ||||
|     } | ||||
|     return throwError(error); | ||||
|  | ||||
|     if (error.status === 401) { | ||||
|       this.injector.get(AuthService).logout(); | ||||
|     } | ||||
|     return throwError(() => error); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -22,7 +22,7 @@ export class SettingsService { | ||||
|       this.http.get<Appsettings>('../../assets/config.json') | ||||
|         .pipe(catchError(error => { | ||||
|           reject(error); | ||||
|           return throwError(error); | ||||
|           return throwError(() => error); | ||||
|         })).subscribe(settings => { | ||||
|           this.appsettings = settings; | ||||
|           resolve(settings); | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { Subject } from 'rxjs'; | ||||
| import { Themes } from 'src/app/models/view/themes.enum'; | ||||
| import { AuthService } from '../auth/auth.service'; | ||||
|  | ||||
| @@ -13,9 +14,14 @@ export class ThemeService { | ||||
|   isSidebarOpen = false; | ||||
|   hasLangChanged = false; | ||||
|  | ||||
|   isSidebarOpen$ = new Subject<boolean>(); | ||||
|  | ||||
|   constructor( | ||||
|     private authService: AuthService | ||||
|   ) { | ||||
|     this.isSidebarOpen$.subscribe(isSidebarOpen => { | ||||
|       this.isSidebarOpen = isSidebarOpen | ||||
|     }); | ||||
|     this.loadTheme(); | ||||
|     this.loadMenu(); | ||||
|     this.themeName = null; | ||||
| @@ -93,6 +99,6 @@ export class ThemeService { | ||||
|  | ||||
|   setSideWidth($event: any): void { | ||||
|     this.sidebarWidth = $event ? '150px' : '50px'; | ||||
|     this.isSidebarOpen = $event; | ||||
|     this.isSidebarOpen$.next($event) | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user