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 :
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import {
|
||||
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse
|
||||
} from '@angular/common/http';
|
||||
|
||||
import { Observable} from 'rxjs/Rx';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/catch';
|
||||
|
||||
import { Logger } from '@nsalaun/ng-logger';
|
||||
|
||||
import { LoginService } from './login.service';
|
||||
import { Token } from './token';
|
||||
|
||||
@Injectable()
|
||||
export class AuthInterceptor implements HttpInterceptor {
|
||||
private observable: Observable<Token>;
|
||||
|
||||
constructor(
|
||||
private logger: Logger,
|
||||
private loginService: LoginService,
|
||||
private injector: Injector,
|
||||
) {}
|
||||
|
||||
intercept(
|
||||
req: HttpRequest<any>,
|
||||
next: HttpHandler
|
||||
): Observable<HttpEvent<any>> {
|
||||
this.logger.log('Intercepted request', req, next);
|
||||
injectAuthorizationHeader(request: HttpRequest<any>, accessToken: string) {
|
||||
this.logger.log('Injecting Authorization header');
|
||||
|
||||
if(!req.headers.has('Authorization')) {
|
||||
let accessToken: string = this.loginService.accessToken();
|
||||
return request;
|
||||
}
|
||||
|
||||
intercept(
|
||||
request: HttpRequest<any>,
|
||||
next: HttpHandler,
|
||||
pass?: number
|
||||
): Observable<HttpEvent<any>> {
|
||||
if(!pass) {
|
||||
pass = 1;
|
||||
}
|
||||
|
||||
let loginService = this.injector.get(LoginService);
|
||||
|
||||
if(request.url == loginService.url) {
|
||||
this.logger.log("Login URL, do not handle.");
|
||||
|
||||
return next.handle(request);
|
||||
}
|
||||
|
||||
this.logger.log(`Intercepted request, pass #${pass}`, request, next);
|
||||
|
||||
let accessToken = loginService.accessToken;
|
||||
|
||||
if(accessToken){
|
||||
req = req.clone({
|
||||
headers: req.headers.set('Authorization', `Bearer ${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('Request', req);
|
||||
this.logger.log('Unauthorized', error);
|
||||
|
||||
return next.handle(req).map(
|
||||
(event: HttpEvent<any>) => event,
|
||||
(error) => {
|
||||
if(error instanceof HttpErrorResponse && error.status === 401) {
|
||||
this.logger.error('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 :
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
|
||||
import { NgLoggerModule } from '@nsalaun/ng-logger';
|
||||
|
||||
import { LoginService } from './login.service';
|
||||
import { AuthInterceptor } from './authInterceptor';
|
||||
import { LoginService } from './login.service';
|
||||
import { LoginForm } from './loginForm.component';
|
||||
import { LoginModalComponent } from './loginModal.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
NgLoggerModule,
|
||||
],
|
||||
providers: [
|
||||
@ -19,6 +25,14 @@ import { AuthInterceptor } from './authInterceptor';
|
||||
useClass: AuthInterceptor,
|
||||
multi: true
|
||||
}
|
||||
],
|
||||
declarations: [
|
||||
LoginModalComponent,
|
||||
LoginForm,
|
||||
],
|
||||
entryComponents: [
|
||||
LoginModalComponent,
|
||||
LoginForm,
|
||||
]
|
||||
})
|
||||
export class LoginModule {};
|
||||
|
@ -1,10 +1,67 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
|
||||
import { Observable} from 'rxjs/Rx';
|
||||
|
||||
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()
|
||||
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');
|
||||
}
|
||||
|
||||
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