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