diff --git a/src/frontend/app/app-routing.module.ts b/src/frontend/app/app-routing.module.ts index 1ec529e1c917e75c15d004e9201c8f1ef96a710e..0794240505ef3234ef7d2f5ddeb133aba38480c6 100755 --- a/src/frontend/app/app-routing.module.ts +++ b/src/frontend/app/app-routing.module.ts @@ -21,12 +21,14 @@ import { AcknowledgementReceptionComponent } from './registeredMail/acknowledgem import { SearchComponent } from './search/search.component'; import { ProcessComponent } from './process/process.component'; import { IndexationComponent } from './indexation/indexation.component'; +import { AppLightGuard } from '@service/app-light.guard'; const routes: Routes = [ { path: 'resources/:resId/content', canActivate: [AppGuard], component: DocumentViewerPageComponent }, { path: 'install', + canActivate: [AppLightGuard], loadChildren: () => import('./installer/installer.module').then(m => m.InstallerModule) }, { path: 'signatureBook/users/:userId/groups/:groupId/baskets/:basketId/resources/:resId', canActivate: [AppGuard], component: SignatureBookComponent }, @@ -42,7 +44,7 @@ const routes: Routes = [ { path: 'about-us', canActivate: [AppGuard], component: AboutUsComponent }, { path: 'home', canActivate: [AppGuard], component: HomeComponent }, { path: 'basketList/users/:userSerialId/groups/:groupSerialId/baskets/:basketId', canActivate: [AppGuard], component: BasketListComponent }, - { path: 'login', component: LoginComponent }, + { path: 'login', canActivate: [AppLightGuard], component: LoginComponent }, { path: 'registeredMail/acknowledgement', canActivate: [AppGuard], component: AcknowledgementReceptionComponent }, { path: 'search', canActivate: [AppGuard], component: SearchComponent }, { diff --git a/src/frontend/app/app.component.ts b/src/frontend/app/app.component.ts index 0c8e86e26fee4ea7468845e70155d7f0650e6d41..a2eb64caac9cc8c8190189c7e60601ee68111f2f 100755 --- a/src/frontend/app/app.component.ts +++ b/src/frontend/app/app.component.ts @@ -10,6 +10,7 @@ import { AuthService } from '@service/auth.service'; import { environment } from '../environments/environment'; import { TranslateService } from '@ngx-translate/core'; import { DateAdapter } from '@angular/material/core'; +import { MatDialog } from '@angular/material/dialog'; /** Custom options the configure the tooltip's default show/hide delays. */ export const myCustomTooltipDefaults: MatTooltipDefaultOptions = { @@ -44,7 +45,8 @@ export class AppComponent implements OnInit, AfterViewInit { public appService: AppService, public headerService: HeaderService, public authService: AuthService, - private adapter: DateAdapter<any> + private adapter: DateAdapter<any>, + public dialog: MatDialog, ) { translate.setDefaultLang('fr'); @@ -60,8 +62,6 @@ export class AppComponent implements OnInit, AfterViewInit { } async ngOnInit() { - this.appService.checkAppSecurity(); - await this.appService.applyMinorUpdate(); this.loading = false; this.headerService.hideSideBar = true; setTimeout(() => { diff --git a/src/frontend/app/installer/install-action/install-action.component.ts b/src/frontend/app/installer/install-action/install-action.component.ts index 3236cf0b01d9e644a0311ff57ca3e46ed37578be..079db3feb9b5f084745b0b12504cf08918c3e244 100644 --- a/src/frontend/app/installer/install-action/install-action.component.ts +++ b/src/frontend/app/installer/install-action/install-action.component.ts @@ -6,6 +6,7 @@ import { of } from 'rxjs'; import { InstallerService } from '../installer.service'; import { NotificationService } from '@service/notification/notification.service'; import { catchError, tap } from 'rxjs/operators'; +import { AuthService } from '@service/auth.service'; @Component({ selector: 'app-install-action', @@ -25,7 +26,8 @@ export class InstallActionComponent implements OnInit, AfterViewInit { public dialogRef: MatDialogRef<InstallActionComponent>, public http: HttpClient, private installerService: InstallerService, - private notify: NotificationService + private notify: NotificationService, + private authService: AuthService, ) { } async ngOnInit(): Promise<void> { @@ -108,6 +110,7 @@ export class InstallActionComponent implements OnInit, AfterViewInit { goToInstance() { this.http.request('DELETE', '../rest/installer/lock', { body: { customId: this.customId } }).pipe( tap((res: any) => { + this.authService.noInstall = false; window.location.href = res.url; this.dialogRef.close('ok'); }), diff --git a/src/frontend/app/installer/installer.component.html b/src/frontend/app/installer/installer.component.html index d9c98310ff54920ef1af7d9eff2f25247147e0a9..a17d3a397c2076f3d58257191ad2fcf476748fda 100644 --- a/src/frontend/app/installer/installer.component.html +++ b/src/frontend/app/installer/installer.component.html @@ -6,13 +6,16 @@ </div> <div class="container" [class.fullContainer]="appService.getViewMode()"> <div class="container-content" style="overflow: hidden;"> - <mat-horizontal-stepper [@.disabled]="true" linear #stepper style="height: 100vh;overflow: auto;" (selectionChange)="initStep($event)"> + <mat-horizontal-stepper [@.disabled]="true" *ngIf="!loading" linear #stepper style="height: 100vh;overflow: auto;" (selectionChange)="initStep($event)"> <mat-step label="install"> <ng-template matStepLabel>Installation</ng-template> <div class="stepContainer"> <div class="stepContent"> <app-welcome #stepContent></app-welcome> </div> + <button *ngIf="!authService.noInstall" mat-fab [title]="'lang.home' | translate" class="previousStepButton" color="primary" (click)="gotToLogin()"> + <mat-icon class="fas fa-home"></mat-icon> + </button> <button mat-fab matStepperNext [title]="this.translate.instant('lang.next')" class="nextStepButton" color="primary"> <mat-icon class="fa fa-arrow-right"></mat-icon> </button> diff --git a/src/frontend/app/installer/installer.component.ts b/src/frontend/app/installer/installer.component.ts index f67b043f85d601c7f6dbe0a77e0f2439c41f5ec3..e07472edf5d0e478eda26cf8f13c33b3b19ea89a 100644 --- a/src/frontend/app/installer/installer.component.ts +++ b/src/frontend/app/installer/installer.component.ts @@ -1,5 +1,4 @@ import { Component, OnInit, ViewChild, AfterViewInit, ViewChildren } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; import { Router } from '@angular/router'; import { HeaderService } from '@service/header.service'; import { NotificationService } from '@service/notification/notification.service'; @@ -13,8 +12,9 @@ import { MatDialog } from '@angular/material/dialog'; import { InstallActionComponent } from './install-action/install-action.component'; import { of } from 'rxjs'; import { FunctionsService } from '@service/functions.service'; -import { InstallerService } from './installer.service'; import { catchError, filter, tap } from 'rxjs/operators'; +import { AuthService } from '@service/auth.service'; +import { PrivilegeService } from '@service/privileges.service'; @Component({ templateUrl: './installer.component.html', @@ -28,12 +28,12 @@ import { catchError, filter, tap } from 'rxjs/operators'; }) export class InstallerComponent implements OnInit, AfterViewInit { + loading: boolean = true; @ViewChildren('stepContent') stepContent: any; @ViewChild('stepper', { static: true }) stepper: MatStepper; constructor( public translate: TranslateService, - private http: HttpClient, private router: Router, private headerService: HeaderService, private notify: NotificationService, @@ -41,13 +41,25 @@ export class InstallerComponent implements OnInit, AfterViewInit { private sortPipe: SortPipe, public dialog: MatDialog, private functionService: FunctionsService, - private installerService: InstallerService + public privilegeService: PrivilegeService, + public authService: AuthService, ) { } ngOnInit(): void { this.headerService.hideSideBar = true; + + if (!this.authService.isAuth() && !this.authService.noInstall) { + this.router.navigate(['/login']); + this.notify.error(this.translate.instant('lang.mustConnectToInstall')); + } else if (this.authService.getToken() !== null && !this.privilegeService.hasCurrentUserPrivilege('create_custom')) { + this.router.navigate(['/login']); + this.notify.error(this.translate.instant('lang.mustPrivilegeToInstall')); + } else { + this.loading = false; + } } + ngAfterViewInit(): void { $('.mat-horizontal-stepper-header-container').insertBefore('.bg-head-content'); $('.mat-step-icon').css('background-color', 'white'); @@ -72,6 +84,10 @@ export class InstallerComponent implements OnInit, AfterViewInit { this.stepper.next(); } + gotToLogin() { + this.router.navigate(['/login']); + } + endInstall() { let installContent: StepAction[] = []; this.stepContent.toArray().forEach((component: any) => { @@ -80,10 +96,6 @@ export class InstallerComponent implements OnInit, AfterViewInit { installContent = this.sortPipe.transform(installContent, 'installPriority'); - console.log(installContent); - - // this.stepper.next(); - const dialogRef = this.dialog.open(InstallActionComponent, { panelClass: 'maarch-modal', disableClose: true, diff --git a/src/frontend/app/installer/welcome/welcome.component.html b/src/frontend/app/installer/welcome/welcome.component.html index 26f0c9ffb1b34ef9636ff7f4cf6b21629c464393..8405ca2f9890e6fdba1777a11681447758fa73cd 100644 --- a/src/frontend/app/installer/welcome/welcome.component.html +++ b/src/frontend/app/installer/welcome/welcome.component.html @@ -14,6 +14,17 @@ </mat-form-field> </form> <mat-divider></mat-divider> + <ng-container *ngIf="customs.length > 0"> + <mat-list> + <div mat-subheader>{{'lang.instancesList' | translate}} : + </div> + <mat-list-item *ngFor="let custom of customs"> + <mat-icon mat-list-icon color="primary" class="fas fa-box-open"></mat-icon> + <div mat-line>{{custom.label}} <small style="color: #666">{{custom.id}}</small></div> + </mat-list-item> + </mat-list> + <mat-divider></mat-divider> + </ng-container> <mat-list> <div mat-subheader>{{'lang.installDescription' | translate}} : </div> diff --git a/src/frontend/app/installer/welcome/welcome.component.ts b/src/frontend/app/installer/welcome/welcome.component.ts index 596accbe753303a59c5a7520803e49de8667507a..69e822fcf992bd50602dc2513dd47e77559e06d6 100644 --- a/src/frontend/app/installer/welcome/welcome.component.ts +++ b/src/frontend/app/installer/welcome/welcome.component.ts @@ -4,6 +4,9 @@ import { NotificationService } from '@service/notification/notification.service' import { TranslateService } from '@ngx-translate/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { environment } from '../../../environments/environment'; +import { catchError, tap } from 'rxjs/operators'; +import { of } from 'rxjs'; +import { AuthService } from '@service/auth.service'; @Component({ @@ -21,36 +24,39 @@ export class WelcomeComponent implements OnInit { steps: any[] = [ { - icon : 'fas fa-check-square', + icon: 'fas fa-check-square', desc: this.translate.instant('lang.prerequisiteCheck') }, { - icon : 'fa fa-database', + icon: 'fa fa-database', desc: this.translate.instant('lang.databaseCreation') }, { - icon : 'fa fa-database', + icon: 'fa fa-database', desc: this.translate.instant('lang.dataSampleCreation') }, { - icon : 'fa fa-hdd', + icon: 'fa fa-hdd', desc: this.translate.instant('lang.docserverCreation') }, { - icon : 'fas fa-tools', + icon: 'fas fa-tools', desc: this.translate.instant('lang.stepCustomizationActionDesc') }, { - icon : 'fa fa-user', + icon: 'fa fa-user', desc: this.translate.instant('lang.adminUserCreation') }, ]; + customs: any = []; + constructor( public translate: TranslateService, public http: HttpClient, private notify: NotificationService, - private _formBuilder: FormBuilder + private _formBuilder: FormBuilder, + private authService: AuthService ) { } ngOnInit(): void { @@ -59,6 +65,9 @@ export class WelcomeComponent implements OnInit { }); this.getLang(); + if (!this.authService.noInstall) { + this.getCustoms(); + } } getLang() { @@ -76,6 +85,18 @@ export class WelcomeComponent implements OnInit { ).subscribe();*/ } + getCustoms() { + this.http.get('../rest/installer/customs').pipe( + tap((data: any) => { + this.customs = data.customs; + }), + catchError((err: any) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + initStep() { return false; } diff --git a/src/frontend/app/login/login.component.html b/src/frontend/app/login/login.component.html index 7b35d8fe65483647110e0d2e868625a28f5920f1..f59708c6b501524e7dc9df3022a632ea91e082bc 100644 --- a/src/frontend/app/login/login.component.html +++ b/src/frontend/app/login/login.component.html @@ -3,8 +3,8 @@ <div class="login-form"> <form (ngSubmit)="onSubmit()" [formGroup]="loginForm"> <mat-icon svgIcon="maarchLogoFull" class="maarchLogo"></mat-icon> - <div style="color: white;font-size: 14px;" [innerHTML]="loginMessage | safeHtml"></div> - <p style="color: white;font-size: 14px;font-weight: bold;">{{applicationName}}</p> + <div style="color: white;font-size: 14px;" [innerHTML]="authService.loginMessage | safeHtml"></div> + <p style="color: white;font-size: 14px;font-weight: bold;">{{authService.applicationName}}</p> <div style="padding-left: 30px;padding-right: 30px;"> <mat-form-field *ngIf="['cas', 'keycloak'].indexOf(authService.authMode) === -1" class="input-row login" appearance="outline" style="padding-bottom: 0px;"> <input id="login" name="login" matInput [placeholder]="this.translate.instant('lang.id')" formControlName="login" diff --git a/src/frontend/app/login/login.component.ts b/src/frontend/app/login/login.component.ts index a2883d2da0b060a40ce71f4110e5a136c230cb2d..25cdd0a465ac19ad173a61f07ee9c11c658cc786 100644 --- a/src/frontend/app/login/login.component.ts +++ b/src/frontend/app/login/login.component.ts @@ -3,7 +3,7 @@ import { MatDialog } from '@angular/material/dialog'; import { HttpClient } from '@angular/common/http'; import { Router } from '@angular/router'; import { Validators, FormGroup, FormBuilder } from '@angular/forms'; -import { tap, catchError, finalize } from 'rxjs/operators'; +import { tap, catchError } from 'rxjs/operators'; import { AuthService } from '@service/auth.service'; import { NotificationService } from '@service/notification/notification.service'; import { environment } from '../../environments/environment'; @@ -11,7 +11,6 @@ import { of } from 'rxjs'; import { HeaderService } from '@service/header.service'; import { FunctionsService } from '@service/functions.service'; import { TimeLimitPipe } from '../../plugins/timeLimit.pipe'; -import { AlertComponent } from '../../plugins/modal/alert.component'; import { TranslateService } from '@ngx-translate/core'; import { LocalStorageService } from '@service/local-storage.service'; @@ -24,7 +23,7 @@ export class LoginComponent implements OnInit { loginForm: FormGroup; loading: boolean = false; - showForm: boolean = false; + showForm: boolean = true; environment: any; applicationName: string = ''; loginMessage: string = ''; @@ -59,7 +58,7 @@ export class LoginComponent implements OnInit { this.router.navigate(['/home']); } } else { - this.getLoginInformations(); + this.initConnection(); } } @@ -109,46 +108,6 @@ export class LoginComponent implements OnInit { ).subscribe(); } - getLoginInformations() { - this.http.get('../rest/authenticationInformations').pipe( - tap((data: any) => { - this.authService.setAppSession(data.instanceId); - this.authService.changeKey = data.changeKey; - this.applicationName = data.applicationName; - this.loginMessage = data.loginMessage; - this.authService.setEvent('authenticationInformations'); - this.authService.authMode = data.authMode; - this.authService.authUri = data.authUri; - - if (this.authService.authMode === 'keycloak') { - const keycloakState = this.localStorage.get('keycloakState'); - if (keycloakState === null || keycloakState === 'null') { - this.localStorage.save('keycloakState', data.keycloakState); - } - } - - this.initConnection(); - }), - finalize(() => this.showForm = true), - catchError((err: any) => { - this.http.get('../rest/validUrl').pipe( - tap((data: any) => { - if (!this.functionsService.empty(data.url)) { - window.location.href = data.url; - } else if (data.lang === 'moreOneCustom') { - this.dialog.open(AlertComponent, { panelClass: 'maarch-modal', autoFocus: false, disableClose: true, data: { title: this.translate.instant('lang.accessNotFound'), msg: this.translate.instant('lang.moreOneCustom'), hideButton: true } }); - } else if (data.lang === 'noConfiguration') { - this.router.navigate(['/install']); - } else { - this.notify.handleSoftErrors(err); - } - }) - ).subscribe(); - return of(false); - }) - ).subscribe(); - } - initConnection() { if (['cas', 'keycloak'].indexOf(this.authService.authMode) > -1) { this.loginForm.disable(); diff --git a/src/frontend/plugins/modal/alert.component.html b/src/frontend/plugins/modal/alert.component.html index 99f9a936632c46e8a21867891c3cd0aca2797c63..339f3327c54e8458c1cfc4bc9d09cde3b364caf7 100644 --- a/src/frontend/plugins/modal/alert.component.html +++ b/src/frontend/plugins/modal/alert.component.html @@ -4,7 +4,7 @@ <div class="alert-message {{data.mode}}" [innerHTML]="data.msg"></div> </div> <span class="divider-modal"></span> - <div mat-dialog-actions *ngIf="!data.hidebutton"> + <div mat-dialog-actions *ngIf="!data.hideButton"> <button id="alertComponentClose" class="actions" color="primary" mat-raised-button (click)="this.dialogRef.close();">{{'lang.ok' | translate}}</button> </div> </div> diff --git a/src/frontend/service/app-light.guard.ts b/src/frontend/service/app-light.guard.ts new file mode 100644 index 0000000000000000000000000000000000000000..5959a1238a9f7907f7c7d1045737023d35563fe3 --- /dev/null +++ b/src/frontend/service/app-light.guard.ts @@ -0,0 +1,51 @@ + +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, CanDeactivate, UrlTree } from '@angular/router'; +import { HttpClient } from '@angular/common/http'; +import { Observable, of } from 'rxjs'; +import { map, tap, catchError, exhaustMap, filter, finalize } from 'rxjs/operators'; +import { HeaderService } from './header.service'; +import { ProcessComponent } from '../app/process/process.component'; +import { PrivilegeService } from './privileges.service'; +import { AuthService } from './auth.service'; +import { LocalStorageService } from './local-storage.service'; +import { FunctionsService } from './functions.service'; +import { AlertComponent } from '../plugins/modal/alert.component'; +import { MatDialog } from '@angular/material/dialog'; +import { TranslateService } from '@ngx-translate/core'; + +@Injectable({ + providedIn: 'root' +}) +export class AppLightGuard implements CanActivate { + + constructor( + public translate: TranslateService, + public http: HttpClient, + private router: Router, + private authService: AuthService, + private localStorage: LocalStorageService, + private functionService: FunctionsService, + public headerService: HeaderService, + private privilegeService: PrivilegeService, + private dialog: MatDialog, + private functionsService: FunctionsService, + ) { } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> { + const urlArr = state.url.replace(/^\/+|\/+$/g, '').split('/'); + + console.log('== ROUTE LIGHT GUARD =='); + console.log(state.url); + + this.headerService.resetSideNavSelection(); + + return this.authService.getLoginInformations(state.url).pipe( + exhaustMap(() => this.authService.getToken() !== null ? this.authService.getCurrentUserInfo() : of(false)), + map(() => true), + catchError((err: any) => { + return of(true); + }) + ); + } +} diff --git a/src/frontend/service/app.guard.ts b/src/frontend/service/app.guard.ts index 6878488bfcb840af34ce8cba3488df8a8f37f66c..891e4f7d68f923449ba59686b7cb4dc937cef323 100644 --- a/src/frontend/service/app.guard.ts +++ b/src/frontend/service/app.guard.ts @@ -1,17 +1,12 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, CanDeactivate, UrlTree } from '@angular/router'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, CanDeactivate } from '@angular/router'; import { HttpClient } from '@angular/common/http'; import { Observable, of } from 'rxjs'; -import { map, tap, catchError, exhaustMap, filter } from 'rxjs/operators'; +import { map, catchError, exhaustMap, filter } from 'rxjs/operators'; import { HeaderService } from './header.service'; import { ProcessComponent } from '../app/process/process.component'; -import { PrivilegeService } from './privileges.service'; import { AuthService } from './auth.service'; -import { LocalStorageService } from './local-storage.service'; -import { FunctionsService } from './functions.service'; -import {AlertComponent} from '../plugins/modal/alert.component'; -import {MatDialog} from '@angular/material/dialog'; import { TranslateService } from '@ngx-translate/core'; @Injectable({ @@ -24,168 +19,59 @@ export class AppGuard implements CanActivate { public http: HttpClient, private router: Router, private authService: AuthService, - private localStorage: LocalStorageService, - private functionService: FunctionsService, public headerService: HeaderService, - private privilegeService: PrivilegeService, - private dialog: MatDialog, ) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> { const urlArr = state.url.replace(/^\/+|\/+$/g, '').split('/'); - console.log('== ROUTE GUARD =='); - this.headerService.resetSideNavSelection(); - - let tokenInfo = this.authService.getToken(); - - if (tokenInfo !== null) { - this.functionService.debug('Token trouvé !', route.url.join('/')); - - this.authService.setUrl(route.url.join('/')); - if (this.headerService.user.id === undefined) { - console.log('Récupération données user...'); - return this.http.get('../rest/currentUser/profile') - .pipe( - map((data: any) => { - this.headerService.user = { - mode: data.mode, - id: data.id, - status: data.status, - userId: data.user_id, - firstname: data.firstname, - lastname: data.lastname, - entities: data.entities, - groups: data.groups, - preferences: data.preferences, - privileges: data.privileges[0] === 'ALL_PRIVILEGES' ? this.privilegeService.getAllPrivileges() : data.privileges - }; - this.functionService.debug('', route.url.join('/')); - this.headerService.nbResourcesFollowed = data.nbFollowedResources; - this.privilegeService.resfreshUserShortcuts(); - - if (this.headerService.user.status === 'ABS') { - return this.router.navigate(['/activate-user']); - } else { - if (urlArr.filter((url: any) => ['signatureBook', 'content'].indexOf(url) > -1).length > 0) { - - this.headerService.hideSideBar = true; - } else { - this.headerService.hideSideBar = false; - } - if (urlArr.filter((url: any) => url === 'administration').length > 0 || urlArr.filter((url: any) => url === 'profile').length > 0) { - this.headerService.sideBarAdmin = true; - } else { - this.headerService.sideBarAdmin = false; - } - return true; - } + console.debug('== ROUTE GUARD =='); + console.debug(state.url); - }), - catchError((err: any) => { - console.log(err); - if (err.error.errors === 'User must change his password') { - return this.router.navigate(['/password-modification']); - } else { - return of(false); - } - }) - ); - } else { - console.log('Données user trouvées !'); + this.headerService.resetSideNavSelection(); - if (this.headerService.user.status === 'ABS') { - this.router.navigate(['/activate-user']); - return of(false); - } else { - if (urlArr.filter((url: any) => ['signatureBook', 'content'].indexOf(url) > -1).length > 0) { - this.headerService.hideSideBar = true; - } else { - this.headerService.hideSideBar = false; + return this.authService.getLoginInformations(state.url) + .pipe( + map((data: any) => { + if (!data) { + return false; + } else if (this.authService.getToken() === null) { + this.authService.setCachedUrl(state.url.replace(/^\//g, '')); + console.debug('Aucun token trouvé ! Redirection sur login ...'); + this.authService.logout(false); + return false; } - if (urlArr.filter((url: any) => url === 'administration').length > 0 || urlArr.filter((url: any) => url === 'profile').length > 0) { - this.headerService.sideBarAdmin = true; + }), + filter(() => this.authService.getToken() !== null), + exhaustMap(() => this.authService.getCurrentUserInfo()), + map((data: any) => { + if (this.headerService.user.status === 'ABS') { + this.router.navigate(['/activate-user']); + return false; } else { - this.headerService.sideBarAdmin = false; - } - return of(true); - } - } - } else { - console.log('Aucun token trouvé ! Recupération du token ...'); - - return this.http.get('../rest/authenticationInformations') - .pipe( - map((data: any) => { - this.authService.setAppSession(data.instanceId); - // this.authService.authMode = data.connection; - this.authService.changeKey = data.changeKey; - this.localStorage.setAppSession(data.instanceId); - tokenInfo = this.authService.getToken(); - - if (tokenInfo !== null) { - // this.authService.setUrl(route.url.join('/')); - this.functionService.debug('', route.url.join('/')); - - this.authService.setEvent('authenticationInformations'); - - return tokenInfo; - } else { - this.authService.setCachedUrl(state.url.replace(/^\//g, '')); - console.log('Aucun token trouvé ! Redirection sur login ...'); - this.authService.logout(false); - return false; - } - }), - filter((info: any) => info !== null), - tap(() => console.log('Récupération données user...')), - exhaustMap(() => this.http.get('../rest/currentUser/profile')), - map((dataUser: any) => { - this.headerService.user = { - mode: dataUser.mode, - id: dataUser.id, - status: dataUser.status, - userId: dataUser.user_id, - firstname: dataUser.firstname, - lastname: dataUser.lastname, - entities: dataUser.entities, - groups: dataUser.groups, - preferences: dataUser.preferences, - privileges: dataUser.privileges[0] === 'ALL_PRIVILEGES' ? this.privilegeService.getAllPrivileges() : dataUser.privileges - }; - this.functionService.debug('', route.url.join('/')); - this.headerService.nbResourcesFollowed = dataUser.nbFollowedResources; - this.privilegeService.resfreshUserShortcuts(); - if (this.headerService.user.status === 'ABS') { - return this.router.navigate(['/activate-user']); - } else if (this.localStorage.get('MaarchCourrierUrl_' + JSON.parse(atob(tokenInfo.split('.')[1])).user.id) !== route.url.join('/')) { - return this.router.navigate(['/' + this.localStorage.get('MaarchCourrierUrl_' + JSON.parse(atob(tokenInfo.split('.')[1])).user.id)]); + if (urlArr.filter((url: any) => ['signatureBook', 'content'].indexOf(url) > -1).length > 0) { + this.headerService.hideSideBar = true; } else { - if (urlArr.filter((url: any) => ['signatureBook', 'content'].indexOf(url) > -1).length > 0) { - this.headerService.hideSideBar = true; - } else { - this.headerService.hideSideBar = false; - } - if (urlArr.filter((url: any) => url === 'administration').length > 0 || urlArr.filter((url: any) => url === 'profile').length > 0) { - this.headerService.sideBarAdmin = true; - } else { - this.headerService.sideBarAdmin = false; - } - return true; + this.headerService.hideSideBar = false; } - }), - catchError((err: any) => { - console.log(err); - if (err.error.errors === 'User must change his password') { - return this.router.navigate(['/password-modification']); + if (urlArr.filter((url: any) => url === 'administration').length > 0 || urlArr.filter((url: any) => url === 'profile').length > 0) { + this.headerService.sideBarAdmin = true; } else { - this.authService.logout(); - return of(false); + this.headerService.sideBarAdmin = false; } - }) - ); + return true; + } - } + }), + catchError((err: any) => { + console.log(err); + if (err.error.errors === 'User must change his password') { + return this.router.navigate(['/password-modification']); + } else { + return of(false); + } + }) + ); } } diff --git a/src/frontend/service/app.service.ts b/src/frontend/service/app.service.ts index b8cafdc40e0035e7501d2a4e712510692dd27f87..abdcfbc5d87fc68ac63142254142cda88fbaf690 100644 --- a/src/frontend/service/app.service.ts +++ b/src/frontend/service/app.service.ts @@ -36,44 +36,4 @@ export class AppService { setScreenWidth(width: number) { this.screenWidth = width; } - - applyMinorUpdate() { - const loader = '<div id="updateLoading" style="position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width: 200px;text-align: center;"><img src="assets/spinner.gif"></div>'; - $('body').append(loader); - return new Promise((resolve) => { - this.http.put('../rest/versionsUpdateSQL', {}).pipe( - tap((data: any) => { - resolve(true); - $('#updateLoading').remove(); - }), - finalize(() => $('#updateLoading').remove()), - catchError((err: any) => { - this.notify.handleSoftErrors(err); - resolve(true); - return of(false); - }) - ).subscribe(); - }); - } - - checkAppSecurity() { - this.authService.catchEvent().subscribe((result: any) => { - if (result === 'authenticationInformations') { - if (this.authService.changeKey) { - setTimeout(() => { - this.dialog.open(AlertComponent, { - panelClass: 'maarch-modal', - autoFocus: false, - disableClose: true, - data: { - mode: 'danger', - title: this.translate.instant('lang.warnPrivateKeyTitle'), - msg: this.translate.instant('lang.warnPrivateKey') - } - }); - }, 1000); - } - } - }); - } } diff --git a/src/frontend/service/auth-interceptor.service.ts b/src/frontend/service/auth-interceptor.service.ts index 2da88ea702668ce987cb6505696b61e70c175c8a..b172fe2762d11f369035cb2589075493d3f1311d 100644 --- a/src/frontend/service/auth-interceptor.service.ts +++ b/src/frontend/service/auth-interceptor.service.ts @@ -5,7 +5,6 @@ import { catchError, switchMap } from 'rxjs/operators'; import { NotificationService } from './notification/notification.service'; import { AuthService } from './auth.service'; import { Router } from '@angular/router'; -import { FunctionsService } from './functions.service'; import { Observable, of } from 'rxjs'; @Injectable() @@ -48,7 +47,6 @@ export class AuthInterceptor implements HttpInterceptor { private router: Router, public notificationService: NotificationService, public authService: AuthService, - private functionsService: FunctionsService ) { } addAuthHeader(request: HttpRequest<any>) { @@ -68,7 +66,6 @@ export class AuthInterceptor implements HttpInterceptor { } intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> { - if (this.byPassToken.filter(url => request.url.indexOf(url.route) > -1 && url.method.indexOf(request.method) > -1).length > 0) { return next.handle(request); } else { @@ -87,14 +84,14 @@ export class AuthInterceptor implements HttpInterceptor { if (this.byPassHandleErrors.filter(url => request.url.indexOf(url.route) > -1 && url.method.indexOf(request.method) > -1).length > 0) { return next.handle(request); } else if (error.status === 401) { - this.functionsService.debug('Auth error', request.url); + console.debug('Auth error', request.url); return this.http.get('../rest/authenticate/token', { params: { refreshToken: this.authService.getRefreshToken() } }).pipe( switchMap((data: any) => { - this.functionsService.debug('Attempt get token ... !', request.url); + console.debug('Attempt get token ... !', request.url); // Update stored token this.authService.setToken(data.token); @@ -108,7 +105,12 @@ export class AuthInterceptor implements HttpInterceptor { catchError(err => { // Disconnect user if bad token process if (err.status === 401) { - this.logout(); + if (this.router.url !== '/login' && this.router.url !== '/') { + this.logout(); + } else { + return next.handle(request); + } + return of(false); } }) @@ -118,8 +120,12 @@ export class AuthInterceptor implements HttpInterceptor { catchError(err => { // Disconnect user if bad token process if (err.status === 401) { - this.functionsService.debug('Refresh token failed !', request.url); - this.logout(); + console.debug('Refresh token failed !', request.url); + if (this.router.url !== '/login' && this.router.url !== '/') { + this.logout(); + } else { + return next.handle(request); + } } return of(false); }) diff --git a/src/frontend/service/auth.service.ts b/src/frontend/service/auth.service.ts index d980b410e40914e7ecb2926b077b5da6dc4860b6..6f4344e46f15683e9ee239c3bf68db98c91bcff1 100644 --- a/src/frontend/service/auth.service.ts +++ b/src/frontend/service/auth.service.ts @@ -4,25 +4,38 @@ import { Router } from '@angular/router'; import { LocalStorageService } from './local-storage.service'; import { NotificationService } from './notification/notification.service'; import { HeaderService } from './header.service'; -import { Observable, Subject } from 'rxjs'; -import { tap } from 'rxjs/operators'; +import { Observable, of, Subject } from 'rxjs'; +import { catchError, finalize, map, tap } from 'rxjs/operators'; +import { PrivilegeService } from './privileges.service'; +import { AlertComponent } from '@plugins/modal/alert.component'; +import { FunctionsService } from './functions.service'; +import { MatDialog } from '@angular/material/dialog'; +import { TranslateService } from '@ngx-translate/core'; @Injectable({ providedIn: 'root' }) export class AuthService { + applicationName: string = ''; + loginMessage: string = ''; authMode: string = 'default'; authUri: string = ''; changeKey: boolean = null; user: any = {}; + noInstall: boolean = false; private eventAction = new Subject<any>(); constructor(public http: HttpClient, private router: Router, private headerService: HeaderService, private notify: NotificationService, - private localStorage: LocalStorageService) { } + private localStorage: LocalStorageService, + private privilegeService: PrivilegeService, + private functionsService: FunctionsService, + public dialog: MatDialog, + public translate: TranslateService, + ) { } catchEvent(): Observable<any> { return this.eventAction.asObservable(); @@ -121,7 +134,7 @@ export class AuthService { } isAuth(): boolean { - return this.getToken() !== null; + return this.headerService.user.id !== undefined; } updateUserInfo(token: string) { @@ -152,4 +165,125 @@ export class AuthService { setUser(value: any) { this.user = value; } + + applyMinorUpdate() { + console.debug('applyMinorUpdate'); + const loader = '<div id="updateLoading" style="position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width: 200px;text-align: center;"><img src="assets/spinner.gif"></div>'; + $('body').append(loader); + this.http.put('../rest/versionsUpdateSQL', {}).pipe( + finalize(() => $('#updateLoading').remove()), + catchError((err: any) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); + } + + checkAppSecurity() { + console.debug('checkAppSecurity'); + if (this.changeKey) { + setTimeout(() => { + this.dialog.open(AlertComponent, { + panelClass: 'maarch-modal', + autoFocus: false, + disableClose: true, + data: { + mode: 'danger', + title: this.translate.instant('lang.warnPrivateKeyTitle'), + msg: this.translate.instant('lang.warnPrivateKey') + } + }); + }, 1000); + } + } + + getLoginInformations(currentRoute: string): Observable<any> { + if (this.noInstall) { + if (currentRoute === '/install') { + return of(true); + } else { + this.router.navigate(['/install']); + return of(false); + } + } else if (this.getAppSession() !== null) { + return of(true); + } else { + return this.http + .get('../rest/authenticationInformations') + .pipe( + tap((data: any) => { + console.debug('getLoginInformations'); + this.setAppSession(data.instanceId); + this.changeKey = data.changeKey; + this.applicationName = data.applicationName; + this.loginMessage = data.loginMessage; + this.setEvent('authenticationInformations'); + this.authMode = data.authMode; + this.authUri = data.authUri; + + if (this.authMode === 'keycloak') { + const keycloakState = this.localStorage.get('keycloakState'); + if (keycloakState === null || keycloakState === 'null') { + this.localStorage.save('keycloakState', data.keycloakState); + } + } + this.applyMinorUpdate(); + this.checkAppSecurity(); + }), + catchError((err: any) => { + console.log(err); + return this.http.get('../rest/validUrl').pipe( + map((data: any) => { + if (!this.functionsService.empty(data.url)) { + window.location.href = data.url; + return false; + } else if (data.lang === 'moreOneCustom') { + this.dialog.open(AlertComponent, { panelClass: 'maarch-modal', autoFocus: false, disableClose: true, data: { title: this.translate.instant('lang.accessNotFound'), msg: this.translate.instant('lang.moreOneCustom'), hideButton: true } }); + return false; + } else if (data.lang === 'noConfiguration') { + this.noInstall = true; + if (currentRoute === '/install') { + return true; + } else { + console.log(this.router.url, 'navigate to install'); + this.router.navigate(['/install']); + return false; + } + } else { + // this.notify.handleSoftErrors(err); + } + }) + ); + }) + ); + } + } + + getCurrentUserInfo(): Observable<any> { + if (this.isAuth()) { + return of(true); + } else { + return this.http + .get('../rest/currentUser/profile') + .pipe( + tap((data: any) => { + console.debug('getCurrentUserInfo'); + this.headerService.user = { + mode: data.mode, + id: data.id, + status: data.status, + userId: data.user_id, + firstname: data.firstname, + lastname: data.lastname, + entities: data.entities, + groups: data.groups, + preferences: data.preferences, + privileges: data.privileges[0] === 'ALL_PRIVILEGES' ? this.privilegeService.getAllPrivileges() : data.privileges + }; + this.headerService.nbResourcesFollowed = data.nbFollowedResources; + this.privilegeService.resfreshUserShortcuts(); + }) + ); + } + } } diff --git a/src/frontend/service/functions.service.ts b/src/frontend/service/functions.service.ts index a0aeeead9c068f2b62f6c453110d32efafceb371..a37c40cd32968a188f6462bff7c1dfcd244e7420 100644 --- a/src/frontend/service/functions.service.ts +++ b/src/frontend/service/functions.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { LatinisePipe } from 'ngx-pipes'; -import { AuthService } from './auth.service'; import { HeaderService } from './header.service'; import { TimeLimitPipe } from '../plugins/timeLimit.pipe'; @@ -12,7 +11,6 @@ export class FunctionsService { constructor( public translate: TranslateService, - private authService: AuthService, private headerService: HeaderService, private latinisePipe: LatinisePipe, ) { } @@ -126,35 +124,6 @@ export class FunctionsService { return filterReturn; } - debug(msg: string, route: string) { - let info: any = { - route : route, - session : 'No user logged !', - refreshSession : 'No user logged !', - user : 'No user logged !' - }; - if (this.authService.getToken() != null) { - info = { - route : route, - session : { - id : this.authService.getAppSession(), - expireIn : new Date((JSON.parse(atob(this.authService.getToken().split('.')[1])).exp) * 1000) - }, - refreshSession : { - id : this.authService.getAppSession(), - expireIn : new Date((JSON.parse(atob(this.authService.getRefreshToken().split('.')[1])).exp) * 1000) - }, - user : this.headerService.user, - }; - } - - if (msg !== '') { - console.log(msg, info); - } else { - console.log(info); - } - } - formatBytes(bytes: number, decimals = 2) { if (typeof bytes === 'number') { if (bytes === 0) { return '0 Octet'; } diff --git a/src/frontend/service/local-storage.service.ts b/src/frontend/service/local-storage.service.ts index 30b095de667315ad26bb08712340ae3bfde860d3..dc49bfa38fb0a392a1c6c718c04e4a0984b475e8 100644 --- a/src/frontend/service/local-storage.service.ts +++ b/src/frontend/service/local-storage.service.ts @@ -4,7 +4,7 @@ import { Injectable } from '@angular/core'; providedIn: 'root' }) export class LocalStorageService { - appSession: any; + appSession: any = null; constructor() { } diff --git a/src/frontend/service/privileges.service.ts b/src/frontend/service/privileges.service.ts index 9f56d95eedc9a1613b6f578c37ee41be4a4705f5..ee3f3066405f8c65ff31edbe6a11654926dabc62 100755 --- a/src/frontend/service/privileges.service.ts +++ b/src/frontend/service/privileges.service.ts @@ -572,6 +572,16 @@ export class PrivilegeService { 'unit': 'registeredMails', 'angular': true, 'shortcut': false + }, + { + 'id': 'create_custom', + 'label': 'lang.installNewCustom', + 'comment': 'lang.installNewCustom', + 'route': '/install', + 'style': 'far fa-window-restore', + 'unit': 'application', + 'angular': true, + 'shortcut': false } ]; diff --git a/src/frontend/tslint.json b/src/frontend/tslint.json index f6dab386bff89bef5a21127389dfaee084aed807..bca6bbf21de88cc6cfb8677d43f1745a519d3b8a 100644 --- a/src/frontend/tslint.json +++ b/src/frontend/tslint.json @@ -49,7 +49,6 @@ "no-bitwise": true, "no-console": [ true, - "debug", "info", "time", "timeEnd", diff --git a/src/lang/lang-fr.json b/src/lang/lang-fr.json index 1627e5b7730470a0dd4c022e4aefe4aec943ed2b..4a5eb3fed9362e65fb1512b63f4923e507776c7e 100644 --- a/src/lang/lang-fr.json +++ b/src/lang/lang-fr.json @@ -2125,5 +2125,9 @@ "restrictAccess": "Accès retreint", "transfer": "Transfert SAE + destruction", "copy": "Transfert SAE + accès restreint", - "deleteAction": "Suppression" + "deleteAction": "Suppression", + "installNewCustom": "Créer une nouvelle instance", + "mustConnectToInstall" : "Veuillez vous connecter pour accéder à l'installeur", + "mustPrivilegeToInstall" : "Vous n'avez pas le droit de créer une nouvelle instance", + "instancesList" : "Instances présentes" }