Implement login mechanism.
This commit is contained in:
parent
16bbc67850
commit
58f1abce21
@ -1,48 +1,91 @@
|
|||||||
// vim: set tw=80 ts=2 sw=2 sts=2 :
|
// vim: set tw=80 ts=2 sw=2 sts=2 :
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable, Injector } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse
|
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse
|
||||||
} from '@angular/common/http';
|
} from '@angular/common/http';
|
||||||
|
|
||||||
import { Observable} from 'rxjs/Rx';
|
import { Observable} from 'rxjs/Rx';
|
||||||
import 'rxjs/add/operator/map';
|
import 'rxjs/add/operator/catch';
|
||||||
|
|
||||||
import { Logger } from '@nsalaun/ng-logger';
|
import { Logger } from '@nsalaun/ng-logger';
|
||||||
|
|
||||||
import { LoginService } from './login.service';
|
import { LoginService } from './login.service';
|
||||||
|
import { Token } from './token';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthInterceptor implements HttpInterceptor {
|
export class AuthInterceptor implements HttpInterceptor {
|
||||||
|
private observable: Observable<Token>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private logger: Logger,
|
private logger: Logger,
|
||||||
private loginService: LoginService,
|
private injector: Injector,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
injectAuthorizationHeader(request: HttpRequest<any>, accessToken: string) {
|
||||||
|
this.logger.log('Injecting Authorization header');
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
intercept(
|
intercept(
|
||||||
req: HttpRequest<any>,
|
request: HttpRequest<any>,
|
||||||
next: HttpHandler
|
next: HttpHandler,
|
||||||
|
pass?: number
|
||||||
): Observable<HttpEvent<any>> {
|
): Observable<HttpEvent<any>> {
|
||||||
this.logger.log('Intercepted request', req, next);
|
if(!pass) {
|
||||||
|
pass = 1;
|
||||||
if(!req.headers.has('Authorization')) {
|
|
||||||
let accessToken: string = this.loginService.accessToken();
|
|
||||||
|
|
||||||
if(accessToken){
|
|
||||||
req = req.clone({
|
|
||||||
headers: req.headers.set('Authorization', `Bearer ${accessToken}`)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.log('Request', req);
|
let loginService = this.injector.get(LoginService);
|
||||||
|
|
||||||
return next.handle(req).map(
|
if(request.url == loginService.url) {
|
||||||
(event: HttpEvent<any>) => event,
|
this.logger.log("Login URL, do not handle.");
|
||||||
(error) => {
|
|
||||||
if(error instanceof HttpErrorResponse && error.status === 401) {
|
return next.handle(request);
|
||||||
this.logger.error('Unauthorized', error);
|
}
|
||||||
|
|
||||||
|
this.logger.log(`Intercepted request, pass #${pass}`, request, next);
|
||||||
|
|
||||||
|
let accessToken = loginService.accessToken;
|
||||||
|
|
||||||
|
if(accessToken){
|
||||||
|
request = request.clone({
|
||||||
|
headers: request.headers.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log('Request', request);
|
||||||
|
|
||||||
|
let observable: Observable<any> = next.handle(request);
|
||||||
|
|
||||||
|
return observable.catch(
|
||||||
|
(error, caught): Observable<any> => {
|
||||||
|
this.logger.error("Error", error, caught);
|
||||||
|
|
||||||
|
if(!(error instanceof HttpErrorResponse) || error.status != 401) {
|
||||||
|
return Observable.throw(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.log('Unauthorized', error);
|
||||||
|
|
||||||
|
if(pass === 3) {
|
||||||
|
return Observable.throw(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.observable) {
|
||||||
|
this.logger.log("No current login observable.")
|
||||||
|
this.observable = loginService.login();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.observable.flatMap((token: Token): Observable<HttpEvent<any>> => {
|
||||||
|
this.logger.log("Logged in, access_token:", token.access_token);
|
||||||
|
this.observable = null;
|
||||||
|
return this.intercept(request, next, ++pass);
|
||||||
|
}).catch((error) => {
|
||||||
|
this.observable = null;
|
||||||
|
return Observable.throw(error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
// vim: set tw=80 ts=2 sw=2 sts=2 :
|
// vim: set tw=80 ts=2 sw=2 sts=2 :
|
||||||
|
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
|
|
||||||
import { NgLoggerModule } from '@nsalaun/ng-logger';
|
import { NgLoggerModule } from '@nsalaun/ng-logger';
|
||||||
|
|
||||||
import { LoginService } from './login.service';
|
|
||||||
import { AuthInterceptor } from './authInterceptor';
|
import { AuthInterceptor } from './authInterceptor';
|
||||||
|
import { LoginService } from './login.service';
|
||||||
|
import { LoginForm } from './loginForm.component';
|
||||||
|
import { LoginModalComponent } from './loginModal.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
HttpClientModule,
|
||||||
|
FormsModule,
|
||||||
NgLoggerModule,
|
NgLoggerModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
@ -19,6 +25,14 @@ import { AuthInterceptor } from './authInterceptor';
|
|||||||
useClass: AuthInterceptor,
|
useClass: AuthInterceptor,
|
||||||
multi: true
|
multi: true
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
LoginModalComponent,
|
||||||
|
LoginForm,
|
||||||
|
],
|
||||||
|
entryComponents: [
|
||||||
|
LoginModalComponent,
|
||||||
|
LoginForm,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class LoginModule {};
|
export class LoginModule {};
|
||||||
|
@ -1,10 +1,67 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
|
|
||||||
|
import { Observable} from 'rxjs/Rx';
|
||||||
|
|
||||||
import * as base64 from 'base64util';
|
import * as base64 from 'base64util';
|
||||||
|
|
||||||
|
import { Logger } from '@nsalaun/ng-logger';
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
|
import { Token } from './token';
|
||||||
|
import { LoginModalComponent } from './loginModal.component';
|
||||||
|
import { Login } from './login';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LoginService {
|
export class LoginService {
|
||||||
accessToken() {
|
|
||||||
|
constructor(
|
||||||
|
private httpClient: HttpClient,
|
||||||
|
private logger: Logger,
|
||||||
|
private ngbModal: NgbModal,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public readonly url: string = '/api/user/login';
|
||||||
|
|
||||||
|
login(): Observable<Token> {
|
||||||
|
let modal = this.ngbModal.open(LoginModalComponent);
|
||||||
|
|
||||||
|
sessionStorage.clear();
|
||||||
|
|
||||||
|
let observable: Observable<any> = Observable.fromPromise(modal.result);
|
||||||
|
|
||||||
|
return observable.flatMap((login: Login) =>
|
||||||
|
this.doLogin(login)
|
||||||
|
).map((token: Token): Token => {
|
||||||
|
this.accessToken = token.access_token;
|
||||||
|
return token;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
sessionStorage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
doLogin(login: Login): Observable<any> {
|
||||||
|
var authdata = base64.encode(
|
||||||
|
`${login.email}:${login.password}`
|
||||||
|
);
|
||||||
|
|
||||||
|
let headers = new HttpHeaders()
|
||||||
|
headers = headers.set('Authorization', `Basic ${authdata}`);
|
||||||
|
|
||||||
|
this.logger.log("Headers", headers);
|
||||||
|
|
||||||
|
return this.httpClient.post(this.url, {}, {
|
||||||
|
headers: headers
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get accessToken(): string {
|
||||||
return sessionStorage.getItem('access_token');
|
return sessionStorage.getItem('access_token');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set accessToken(token: string) {
|
||||||
|
sessionStorage.setItem('access_token', token);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
33
src/login/loginForm.component.ts
Normal file
33
src/login/loginForm.component.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// vim: set tw=80 ts=2 sw=2 sts=2 :
|
||||||
|
|
||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { NgForm } from '@angular/forms';
|
||||||
|
|
||||||
|
import { Login } from './login';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'form[login-form]',
|
||||||
|
template: `
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email" class="col-sm-4 control-label">Adresse email</label>
|
||||||
|
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input type="text" class="form-control" id="email"
|
||||||
|
[(ngModel)]="login.email" placeholder="Nom d'utilisateur">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" class="col-sm-4 control-label">Mot de passe</label>
|
||||||
|
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input type="password" class="form-control" id="password"
|
||||||
|
[(ngModel)]="login.password" placeholder="Mot de passe">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
||||||
|
export class LoginForm {
|
||||||
|
@Input('login-form') private login: Login
|
||||||
|
}
|
47
src/login/loginModal.component.ts
Normal file
47
src/login/loginModal.component.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// vim: set tw=80 ts=2 sw=2 sts=2:
|
||||||
|
|
||||||
|
import { Component } from '@angular/core'
|
||||||
|
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
|
import { Login } from './login';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'login-modal',
|
||||||
|
template: `
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title" id="modal-title">Authentification requise</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body" id="modal-body">
|
||||||
|
<form [(login-form)]="login" class="form-horizontal"
|
||||||
|
(ngSubmit)="submit()" #form="ngForm">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-primary" [disabled]="!form.valid" (click)="submit()">
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn btn-default" (click)="cancel()">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class LoginModalComponent {
|
||||||
|
private login: Login = new Login();
|
||||||
|
|
||||||
|
constructor(private activeModal: NgbActiveModal) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
submit(): void {
|
||||||
|
this.activeModal.close(this.login);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel(): void {
|
||||||
|
this.activeModal.dismiss("closed");
|
||||||
|
}
|
||||||
|
}
|
6
src/login/token.ts
Normal file
6
src/login/token.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// vim: set tw=80 ts=2 sw=2 sts=2 :
|
||||||
|
|
||||||
|
export class Token {
|
||||||
|
access_token: string;
|
||||||
|
refresh_token: string;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user