Merge pull request '12-user-info-jwt' (#4) from 12-user-info-jwt into develop
continuous-integration/drone/push Build was killed Details
continuous-integration/drone/pr Build is passing Details

Reviewed-on: #4
This commit is contained in:
William Nolin 2021-12-15 00:24:14 -05:00
commit 24a713160a
23 changed files with 280 additions and 134 deletions

View File

@ -40,7 +40,7 @@
"vendorChunk": true, "vendorChunk": true,
"extractLicenses": false, "extractLicenses": false,
"buildOptimizer": false, "buildOptimizer": false,
"sourceMap": false, "sourceMap": true,
"optimization": false, "optimization": false,
"namedChunks": true "namedChunks": true
}, },

View File

@ -3,13 +3,16 @@
"version": "0.0.0", "version": "0.0.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json", "start": "ng serve --host 0.0.0.0 --proxy-config proxy.conf.json",
"build": "ng build", "build": "ng build",
"test": "ng test", "test": "ng test",
"lint": "ng lint", "lint": "ng lint",
"e2e": "ng e2e" "e2e": "ng e2e"
}, },
"private": true, "private": true,
"browser": {
"fs": false
},
"dependencies": { "dependencies": {
"@angular/animations": "~12.2.14", "@angular/animations": "~12.2.14",
"@angular/cdk": "^12.2.13", "@angular/cdk": "^12.2.13",
@ -21,10 +24,11 @@
"@angular/platform-browser": "~12.2.14", "@angular/platform-browser": "~12.2.14",
"@angular/platform-browser-dynamic": "~12.2.14", "@angular/platform-browser-dynamic": "~12.2.14",
"@angular/router": "~12.2.14", "@angular/router": "~12.2.14",
"@js-joda/core": "^4.3.1",
"@mdi/angular-material": "^6.5.95", "@mdi/angular-material": "^6.5.95",
"bootstrap": "^4.5.2", "bootstrap": "^4.5.2",
"copy-webpack-plugin": "^10.0.0", "copy-webpack-plugin": "^10.0.0",
"@js-joda/core": "^4.3.1", "jwt-decode": "^3.1.2",
"material-design-icons": "^3.0.1", "material-design-icons": "^3.0.1",
"ngx-material-file-input": "^2.1.1", "ngx-material-file-input": "^2.1.1",
"rxjs": "^7.4.0", "rxjs": "^7.4.0",

View File

@ -1,10 +1,18 @@
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core'
import {Routes, RouterModule} from '@angular/router'; import {RouterModule, Routes} from '@angular/router'
import {LogoutComponent} from './pages/logout/logout.component'
import {Login} from './accounts'
import {LoginComponent} from './pages/login/login.component'; const routes: Routes = [{
import {LogoutComponent} from "./pages/logout/logout.component"; path: 'login',
component: Login
const routes: Routes = [{path: 'login', component: LoginComponent}, {path: 'logout', component: LogoutComponent}, {path: '', redirectTo: 'login'}]; }, {
path: 'logout',
component: LogoutComponent
}, {
path: '',
redirectTo: 'login'
}]
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],

View File

@ -1,18 +1,25 @@
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core'
import {AccountsRoutingModule} from './accounts-routing.module'; import {AccountsRoutingModule} from './accounts-routing.module'
import {LoginComponent} from './pages/login/login.component'; import {LoginComponent} from './pages/login/login.component'
import {SharedModule} from "../shared/shared.module"; import {SharedModule} from '../shared/shared.module'
import {LogoutComponent} from './pages/logout/logout.component'; import {LogoutComponent} from './pages/logout/logout.component'
import {CommonModule} from "@angular/common"; import {Login} from './accounts'
import {BrowserModule} from "@angular/platform-browser"; import {CreInputsModule} from '../shared/components/inputs/inputs.module'
import {CreButtonsModule} from '../shared/components/buttons/buttons.module'
@NgModule({ @NgModule({
declarations: [LoginComponent, LogoutComponent], declarations: [
LoginComponent,
LogoutComponent,
Login
],
imports: [ imports: [
SharedModule, SharedModule,
AccountsRoutingModule, AccountsRoutingModule,
CreInputsModule,
CreButtonsModule,
] ]
}) })
export class AccountsModule { export class AccountsModule {

View File

@ -0,0 +1,63 @@
import {Component, HostListener, ViewChild} from '@angular/core'
import {FormControl, Validators} from '@angular/forms'
import {ErrorHandlingComponent} from '../shared/components/subscribing.component'
import {AccountService} from './services/account.service'
import {AppState} from '../shared/app-state'
import {ErrorHandler, ErrorService} from '../shared/service/error.service'
import {ActivatedRoute, Router} from '@angular/router'
import {CreForm, ICreForm} from "../shared/components/forms/forms";
import {take, takeUntil} from "rxjs/operators";
import {AlertService} from "../shared/service/alert.service";
@Component({
selector: 'cre-login',
templateUrl: 'login.html',
styles: [
'cre-form { min-width: 25rem; margin-top: 50vh; transform: translateY(-70%) }'
]
})
export class Login extends ErrorHandlingComponent {
@ViewChild(CreForm) form: ICreForm
userIdControl = new FormControl(null, Validators.compose([Validators.required, Validators.pattern(new RegExp('^[0-9]+$'))]))
passwordControl = new FormControl(null, Validators.required)
errorHandlers: ErrorHandler[] = [{
filter: error => error.status === 403,
messageProducer: () => 'Les identifiants entrés sont invalides'
}]
constructor(
private accountService: AccountService,
private alertService: AlertService,
private appState: AppState,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) {
super(errorService, activatedRoute, router)
this.appState.title = 'Connexion'
}
// Allows to send the form by pressing Enter
@HostListener('window:keyup.enter', ['$event'])
onEnterKeyEvent() {
if (this.form.formGroup) {
this.submit()
}
}
submit() {
this.subscribeAndNavigate(
this.accountService.login(this.userIdControl.value, this.passwordControl.value),
'/color/list'
)
}
get controls(): { userId: FormControl, password: FormControl } {
return {
userId: this.userIdControl,
password: this.passwordControl
}
}
}

View File

@ -0,0 +1,28 @@
<cre-form #form [formControls]="controls" class="mx-auto">
<cre-form-title>Connexion au système</cre-form-title>
<cre-form-content>
<cre-input
[control]="userIdControl"
label="Numéro d'utilisateur"
icon="account">
<ng-template let-errors="errors">
<span *ngIf="errors && errors.pattern">Le numéro d'utilisateur doit être un nombre</span>
</ng-template>
</cre-input>
<cre-input
[control]="passwordControl"
type="password"
label="Mot de passe"
icon="lock">
</cre-input>
</cre-form-content>
<cre-form-actions>
<cre-accent-button
type="submit"
[disabled]="!form.valid"
(click)="submit()">
Connexion
</cre-accent-button>
</cre-form-actions>
</cre-form>

View File

@ -31,7 +31,7 @@ export class LoginComponent extends ErrorHandlingComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.errorService.activeErrorHandler = this this.errorService.activeErrorHandler = this
if (this.accountService.isLoggedIn()) { if (this.appState.isAuthenticated) {
this.router.navigate(['/color']) this.router.navigate(['/color'])
} }
@ -44,10 +44,12 @@ export class LoginComponent extends ErrorHandlingComponent implements OnInit {
} }
submit() { submit() {
this.accountService.login( this.subscribe(
this.idFormControl.value, this.accountService.login(
this.passwordFormControl.value, this.idFormControl.value,
() => this.router.navigate(['/color']) this.passwordFormControl.value
),
response => console.log(response)
) )
} }
} }

View File

@ -1,28 +1,35 @@
import {Component, OnInit} from '@angular/core'; import {Component} from '@angular/core';
import {AccountService} from "../../services/account.service"; import {AccountService} from "../../services/account.service";
import {Router} from "@angular/router"; import {ActivatedRoute, Router} from "@angular/router";
import {AppState} from "../../../shared/app-state";
import {SubscribingComponent} from "../../../shared/components/subscribing.component";
import {ErrorService} from "../../../shared/service/error.service";
@Component({ @Component({
selector: 'cre-logout', selector: 'cre-logout',
templateUrl: './logout.component.html', templateUrl: './logout.component.html',
styleUrls: ['./logout.component.sass'] styleUrls: ['./logout.component.sass']
}) })
export class LogoutComponent implements OnInit { export class LogoutComponent extends SubscribingComponent {
constructor( constructor(
private accountService: AccountService, private accountService: AccountService,
private router: Router private appState: AppState,
errorService: ErrorService,
router: Router,
activatedRoute: ActivatedRoute
) { ) {
super(errorService, activatedRoute, router)
} }
ngOnInit(): void { ngOnInit(): void {
if (!this.accountService.isLoggedIn()) { if (!this.appState.isAuthenticated) {
this.router.navigate(['/account/login']) this.urlUtils.navigateTo('/account/login')
} }
this.accountService.logout(() => { this.subscribeAndNavigate(
this.router.navigate(['/account/login']) this.accountService.logout(),
}) '/account/login'
)
} }
} }

View File

@ -1,14 +1,14 @@
import {Injectable, OnDestroy} from '@angular/core' import {Injectable, OnDestroy} from '@angular/core'
import {Subject} from 'rxjs' import {Observable, Subject} from 'rxjs'
import {take, takeUntil} from 'rxjs/operators' import {take, takeUntil} from 'rxjs/operators'
import {AppState} from '../../shared/app-state' import {AppState} from '../../shared/app-state'
import {HttpClient, HttpResponse} from '@angular/common/http' import {HttpClient, HttpResponse} from '@angular/common/http'
import {environment} from '../../../../environments/environment' import {environment} from '../../../../environments/environment'
import {ApiService} from '../../shared/service/api.service' import {ApiService} from '../../shared/service/api.service'
import {User, Permission} from '../../shared/model/user' import {Permission, User} from '../../shared/model/user'
import {ErrorService} from '../../shared/service/error.service' import {ErrorService} from '../../shared/service/error.service'
import {globalLoadingWheel} from '../../shared/components/loading-wheel/loading-wheel.component'
import {AlertService} from '../../shared/service/alert.service' import {AlertService} from '../../shared/service/alert.service'
import {JwtService} from "./jwt.service";
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -20,6 +20,7 @@ export class AccountService implements OnDestroy {
private http: HttpClient, private http: HttpClient,
private api: ApiService, private api: ApiService,
private appState: AppState, private appState: AppState,
private jwtService: JwtService,
private errorService: ErrorService, private errorService: ErrorService,
private alertService: AlertService private alertService: AlertService
) { ) {
@ -30,20 +31,16 @@ export class AccountService implements OnDestroy {
this.destroy$.complete() this.destroy$.complete()
} }
isLoggedIn(): boolean {
return this.appState.isAuthenticated
}
checkAuthenticationStatus() { checkAuthenticationStatus() {
if (!this.appState.authenticatedUser) { if (!this.appState.isAuthenticated) {
// Try to get current default group user // Try to get current default group user
this.http.get<User>(`${environment.apiUrl}/user/current`, {withCredentials: true}) this.http.get<User>(`${environment.apiUrl}/user/group/currentuser`, {withCredentials: true})
.pipe( .pipe(
take(1), take(1),
takeUntil(this.destroy$), takeUntil(this.destroy$),
).subscribe( ).subscribe(
{ {
next: user => this.appState.authenticatedUser = user, next: user => this.appState.authenticateGroupUser(user),
error: err => { error: err => {
if (err.status === 404 || err.status === 403) { if (err.status === 404 || err.status === 403) {
console.warn('No default user is defined on this computer') console.warn('No default user is defined on this computer')
@ -55,67 +52,74 @@ export class AccountService implements OnDestroy {
} }
} }
login(id: number, password: string, success: () => void) { login(userId: number, password: string): Observable<any> {
const loginForm = {id, password} const subject = new Subject<void>()
globalLoadingWheel.show()
this.http.post<any>(`${environment.apiUrl}/login`, loginForm, { this.http.post<any>(`${environment.apiUrl}/login`, {id: userId, password}, {
withCredentials: true, withCredentials: true,
observe: 'response' as 'body' observe: 'response' as 'body'
}) }).pipe(
.pipe(
take(1),
takeUntil(this.destroy$)
)
.subscribe({
next: (response: HttpResponse<any>) => {
this.appState.authenticationExpiration = parseInt(response.headers.get('X-Authentication-Expiration'))
this.appState.isAuthenticated = true
this.setLoggedInUserFromApi()
success()
},
error: err => {
globalLoadingWheel.hide()
if (err.status === 401 || err.status === 403) {
this.alertService.pushError('Les identifiants entrés sont invalides')
} else {
this.errorService.handleError(err)
}
}
})
}
logout(success: () => void) {
this.api.get<void>('/logout', true).pipe(
take(1), take(1),
takeUntil(this.destroy$) takeUntil(this.destroy$)
) ).subscribe({
.subscribe({ next: (response: HttpResponse<void>) => {
next: () => { this.loginUser(response)
this.appState.resetAuthenticatedUser()
this.checkAuthenticationStatus() subject.next()
success() subject.complete()
}, },
error: err => this.errorService.handleError(err) error: error => {
}) if (error.status === 403) {
this.alertService.pushError('Les identifiants entrés sont invalides')
} else {
this.errorService.handleError(error)
}
subject.next()
subject.complete()
}
})
return subject
}
private loginUser(response: HttpResponse<void>) {
const authorization = response.headers.get("Authorization")
const user = this.jwtService.parseUser(authorization)
this.appState.authenticateUser(user)
}
logout(): Observable<void> {
const subject = new Subject<void>()
this.api.get<void>('/logout').pipe(
take(1),
takeUntil(this.destroy$)
).subscribe({
next: () => {
this.logoutUser()
subject.next()
subject.complete()
},
error: error => {
this.errorService.handleError(error)
subject.next()
subject.complete()
}
})
return subject
}
private logoutUser() {
this.appState.resetAuthenticatedUser()
this.checkAuthenticationStatus()
} }
hasPermission(permission: Permission): boolean { hasPermission(permission: Permission): boolean {
return this.appState.authenticatedUser && this.appState.authenticatedUser.permissions.indexOf(permission) >= 0 return this.appState.authenticatedUser && this.appState.authenticatedUser.permissions.indexOf(permission) >= 0
} }
private setLoggedInUserFromApi() {
this.api.get<User>('/user/current', true)
.pipe(
take(1),
takeUntil(this.destroy$)
)
.subscribe({
next: user => {
this.appState.authenticatedUser = user
// At this point the loading wheel should be visible
globalLoadingWheel.hide()
},
error: err => this.errorService.handleError(err)
})
}
} }

View File

@ -0,0 +1,13 @@
import {Injectable} from "@angular/core";
import jwtDecode from "jwt-decode";
import { User } from "../../shared/model/user";
@Injectable({
providedIn: 'root'
})
export class JwtService {
parseUser(jwt: string): User {
const decoded = jwtDecode(jwt) as any
return JSON.parse(decoded.user)
}
}

View File

@ -1,6 +1,6 @@
<cre-action-bar [reverse]="true"> <cre-action-bar [reverse]="true">
<cre-action-group> <cre-action-group>
<cre-accent-button routerLink="/catalog/company/add">Ajouter</cre-accent-button> <cre-accent-button *ngIf="hasEditPermission" routerLink="/catalog/company/add">Ajouter</cre-accent-button>
</cre-action-group> </cre-action-group>
</cre-action-bar> </cre-action-bar>

View File

@ -1,6 +1,6 @@
<cre-action-bar [reverse]="true"> <cre-action-bar [reverse]="true">
<cre-action-group> <cre-action-group>
<cre-accent-button routerLink="/catalog/materialtype/add">Ajouter</cre-accent-button> <cre-accent-button *ngIf="hasEditPermission" routerLink="/catalog/materialtype/add">Ajouter</cre-accent-button>
</cre-action-group> </cre-action-group>
</cre-action-bar> </cre-action-bar>

View File

@ -3,7 +3,7 @@
<cre-primary-button routerLink="/color/list">Retour</cre-primary-button> <cre-primary-button routerLink="/color/list">Retour</cre-primary-button>
</cre-action-group> </cre-action-group>
<cre-action-group> <cre-action-group>
<cre-submit-button [form]="recipeForm.creForm" (submit)="recipeForm.submit()"></cre-submit-button> <cre-form-submit-button [form]="recipeForm.creForm" (submit)="recipeForm.submit()"></cre-form-submit-button>
</cre-action-group> </cre-action-group>
</cre-action-bar> </cre-action-bar>

View File

@ -46,7 +46,7 @@ export class RecipeForm extends SubscribingComponent {
} }
ngOnInit() { ngOnInit() {
super.ngOnInit(); super.ngOnInit()
this.fetchCompanies() this.fetchCompanies()

View File

@ -8,7 +8,7 @@ import {Title} from '@angular/platform-browser'
}) })
export class AppState { export class AppState {
private readonly KEY_AUTHENTICATED = 'authenticated' private readonly KEY_AUTHENTICATED = 'authenticated'
private readonly KEY_AUTHENTICATION_EXPIRATION = 'authentication-expiration' private readonly KEY_DEFAULT_GROUP_USER_AUTHENTICATED = 'default-group-user-authenticated'
private readonly KEY_LOGGED_IN_USER = 'logged-in-user' private readonly KEY_LOGGED_IN_USER = 'logged-in-user'
authenticatedUser$ = new Subject<{ authenticated: boolean, authenticatedUser: User }>() authenticatedUser$ = new Subject<{ authenticated: boolean, authenticatedUser: User }>()
@ -19,9 +19,19 @@ export class AppState {
) { ) {
} }
authenticateUser(user: User) {
this.authenticatedUser = user
this.isAuthenticated = true
}
authenticateGroupUser(user: User) {
this.authenticatedUser = user
this.isDefaultGroupUserAuthenticated = true
}
resetAuthenticatedUser() { resetAuthenticatedUser() {
this.isAuthenticated = false this.isAuthenticated = false
this.authenticationExpiration = -1 this.isDefaultGroupUserAuthenticated = false
this.authenticatedUser = null this.authenticatedUser = null
} }
@ -36,7 +46,7 @@ export class AppState {
return sessionStorage.getItem(this.KEY_AUTHENTICATED) === 'true' return sessionStorage.getItem(this.KEY_AUTHENTICATED) === 'true'
} }
set isAuthenticated(value: boolean) { private set isAuthenticated(value: boolean) {
sessionStorage.setItem(this.KEY_AUTHENTICATED, value.toString()) sessionStorage.setItem(this.KEY_AUTHENTICATED, value.toString())
this.authenticatedUser$.next({ this.authenticatedUser$.next({
authenticated: value, authenticated: value,
@ -44,12 +54,16 @@ export class AppState {
}) })
} }
get authenticationExpiration(): number { get isDefaultGroupUserAuthenticated(): boolean {
return parseInt(sessionStorage.getItem(this.KEY_AUTHENTICATION_EXPIRATION)) return sessionStorage.getItem(this.KEY_DEFAULT_GROUP_USER_AUTHENTICATED) === 'true'
} }
set authenticationExpiration(value: number) { private set isDefaultGroupUserAuthenticated(value: boolean) {
sessionStorage.setItem(this.KEY_AUTHENTICATION_EXPIRATION, value.toString()) sessionStorage.setItem(this.KEY_DEFAULT_GROUP_USER_AUTHENTICATED, value.toString())
}
get hasCredentials(): boolean {
return this.isAuthenticated || this.isDefaultGroupUserAuthenticated
} }
get authenticatedUser(): User { get authenticatedUser(): User {
@ -57,9 +71,9 @@ export class AppState {
return userString ? JSON.parse(userString) : null return userString ? JSON.parse(userString) : null
} }
set authenticatedUser(value: User) { private set authenticatedUser(value: User) {
if (value === null) { if (value === null) {
sessionStorage.removeItem(this.KEY_LOGGED_IN_USER) // sessionStorage.removeItem(this.KEY_LOGGED_IN_USER)
} else { } else {
sessionStorage.setItem(this.KEY_LOGGED_IN_USER, JSON.stringify(value)) sessionStorage.setItem(this.KEY_LOGGED_IN_USER, JSON.stringify(value))
} }

View File

@ -1,13 +1,14 @@
import {Component, EventEmitter, Input, Output} from '@angular/core'; import {Component, ContentChild, EventEmitter, Input, Output} from '@angular/core'
import {ICreForm} from './forms'; import {ICreForm} from './forms';
@Component({ @Component({
selector: 'cre-submit-button', selector: 'cre-form-submit-button',
templateUrl: 'submit-button.html' templateUrl: 'submit-button.html'
}) })
export class CreSubmitButton { export class CreSubmitButton {
@Input() form: ICreForm @Input() form: ICreForm
@Input() valid: boolean | null @Input() valid: boolean | null
@Input() text = 'Enregistrer'
@Output() submit = new EventEmitter<void>() @Output() submit = new EventEmitter<void>()

View File

@ -5,7 +5,7 @@
</mat-card-title> </mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content [class.no-action]="!hasActions"> <mat-card-content [class.no-action]="!hasActions">
<form [formGroup]="form"> <form [formGroup]="formGroup">
<ng-content select="cre-form-content"></ng-content> <ng-content select="cre-form-content"></ng-content>
</form> </form>
</mat-card-content> </mat-card-content>

View File

@ -2,7 +2,7 @@ import {Component, ContentChild, Directive, Input, OnInit, ViewEncapsulation} fr
import {FormBuilder, FormGroup} from '@angular/forms' import {FormBuilder, FormGroup} from '@angular/forms'
export interface ICreForm { export interface ICreForm {
form: FormGroup formGroup: FormGroup
valid: boolean valid: boolean
invalid: boolean invalid: boolean
} }
@ -35,7 +35,7 @@ export class CreForm implements ICreForm, OnInit {
@ContentChild(CreFormActions) formActions: CreFormActions @ContentChild(CreFormActions) formActions: CreFormActions
@Input() formControls: { [key: string]: any } @Input() formControls: { [key: string]: any }
form: FormGroup formGroup: FormGroup
constructor( constructor(
private formBuilder: FormBuilder private formBuilder: FormBuilder
@ -43,7 +43,7 @@ export class CreForm implements ICreForm, OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.form = this.formBuilder.group(this.formControls) this.formGroup = this.formBuilder.group(this.formControls)
} }
get hasActions(): boolean { get hasActions(): boolean {
@ -51,10 +51,10 @@ export class CreForm implements ICreForm, OnInit {
} }
get valid(): boolean { get valid(): boolean {
return this.form && this.form.valid return this.formGroup && this.formGroup.valid
} }
get invalid(): boolean { get invalid(): boolean {
return !this.form || this.form.invalid return !this.formGroup || this.formGroup.invalid
} }
} }

View File

@ -1 +1 @@
<cre-accent-button [disabled]="disableButton" (click)="submit.emit()">Enregistrer</cre-accent-button> <cre-accent-button [disabled]="disableButton" (click)="submit.emit()">{{text}}</cre-accent-button>

View File

@ -58,9 +58,10 @@ export class HeaderComponent extends SubscribingComponent {
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.accountService.logout(() => { this.subscribe(
console.log('Successfully logged out') this.accountService.logout(),
}) () => console.info('Successfully logged out')
)
super.ngOnDestroy() super.ngOnDestroy()
} }
@ -71,7 +72,7 @@ export class HeaderComponent extends SubscribingComponent {
set activeLink(link: string) { set activeLink(link: string) {
this._activeLink = link this._activeLink = link
this.router.navigate([link]) this.urlUtils.navigateTo(link)
} }
get activeLink() { get activeLink() {

View File

@ -1,5 +1,6 @@
import { import {
AfterViewInit, ChangeDetectorRef, AfterViewInit,
ChangeDetectorRef,
Component, Component,
ContentChild, ContentChild,
Directive, Directive,
@ -16,7 +17,7 @@ import {
import {AbstractControl, FormControl, ValidationErrors, ValidatorFn, Validators} from '@angular/forms' import {AbstractControl, FormControl, ValidationErrors, ValidatorFn, Validators} from '@angular/forms'
import {COMMA, ENTER} from '@angular/cdk/keycodes' import {COMMA, ENTER} from '@angular/cdk/keycodes'
import {isObservable, Observable, Subject} from 'rxjs' import {isObservable, Observable, Subject} from 'rxjs'
import {map, startWith, takeUntil} from 'rxjs/operators' import {map, takeUntil} from 'rxjs/operators'
import {MatChipInputEvent} from '@angular/material/chips' import {MatChipInputEvent} from '@angular/material/chips'
import {MatAutocomplete, MatAutocompleteSelectedEvent} from '@angular/material/autocomplete' import {MatAutocomplete, MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'

View File

@ -4,10 +4,9 @@ import {Observable, Subject} from 'rxjs'
import {environment} from '../../../../environments/environment' import {environment} from '../../../../environments/environment'
import {AppState} from '../app-state' import {AppState} from '../app-state'
import {Router} from '@angular/router' import {Router} from '@angular/router'
import {map, share, takeUntil, tap} from 'rxjs/operators' import {map, share, takeUntil} from 'rxjs/operators'
import {valueOr} from '../utils/utils' import {valueOr} from '../utils/utils'
import {ErrorService} from './error.service' import {ErrorService} from './error.service'
import {globalLoadingWheel} from '../components/loading-wheel/loading-wheel.component'
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -71,14 +70,13 @@ export class ApiService implements OnDestroy {
observe: 'response' observe: 'response'
} }
if (needAuthentication) { if (needAuthentication) {
if (this.checkAuthenticated()) { if (this.appState.hasCredentials) {
if (httpOptions) { if (httpOptions) {
httpOptions.withCredentials = true httpOptions.withCredentials = true
} else { } else {
console.error('httpOptions need to be specified to use credentials in HTTP methods.') console.error('httpOptions need to be specified to use credentials in HTTP methods.')
} }
} else { } else {
this.appState.resetAuthenticatedUser()
this.navigateToLogin() this.navigateToLogin()
} }
} }
@ -90,11 +88,6 @@ export class ApiService implements OnDestroy {
.pipe(takeUntil(this._destroy$), map(r => r.body), share()) .pipe(takeUntil(this._destroy$), map(r => r.body), share())
} }
private checkAuthenticated(): boolean {
return (this.appState.isAuthenticated && Date.now() <= this.appState.authenticationExpiration) ||
(this.appState.authenticatedUser && this.appState.authenticatedUser.group != null)
}
private navigateToLogin() { private navigateToLogin() {
this.router.navigate(['/account/login']) this.router.navigate(['/account/login'])
} }

View File

@ -1,7 +1,7 @@
<cre-action-bar> <cre-action-bar>
<cre-action-group></cre-action-group> <cre-action-group></cre-action-group>
<cre-action-group> <cre-action-group>
<cre-accent-button routerLink="/misc/touch-up-kit/add">Ajouter</cre-accent-button> <cre-accent-button *ngIf="canEditTouchUpKits" routerLink="/misc/touch-up-kit/add">Ajouter</cre-accent-button>
</cre-action-group> </cre-action-group>
</cre-action-bar> </cre-action-bar>