-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3cfb003
commit 394aa3e
Showing
4 changed files
with
212 additions
and
148 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import { Injectable } from '@angular/core'; | ||
import { HttpClient, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http'; | ||
import { BehaviorSubject, Observable, throwError } from 'rxjs'; | ||
import { catchError, map } from 'rxjs/operators'; | ||
import { Router } from '@angular/router'; | ||
|
||
export const AUTH_EVENTS = { | ||
loginSuccess: 'auth-login-success', | ||
loginFailed: 'auth-login-failed', | ||
logoutSuccess: 'auth-logout-success', | ||
sessionTimeout: 'auth-session-timeout', | ||
notAuthenticated: 'auth-not-authenticated', | ||
notAuthorized: 'auth-not-authorized' | ||
}; | ||
|
||
@Injectable({ | ||
providedIn: 'root' | ||
}) | ||
export class AuthService { | ||
private currentUserSubject: BehaviorSubject<any>; | ||
public currentUser: Observable<any>; | ||
|
||
constructor(private http: HttpClient, private router: Router) { | ||
this.currentUserSubject = new BehaviorSubject<any>(JSON.parse(localStorage.getItem('currentUser'))); | ||
this.currentUser = this.currentUserSubject.asObservable(); | ||
} | ||
|
||
public get currentUserValue(): any { | ||
return this.currentUserSubject.value; | ||
} | ||
|
||
login(credentials: any): Observable<any> { | ||
return this.http.post<any>('/login', credentials) | ||
.pipe(map(user => { | ||
if (user && user.token) { | ||
localStorage.setItem('currentUser', JSON.stringify(user)); | ||
this.currentUserSubject.next(user); | ||
} | ||
return user; | ||
}), catchError(this.handleError)); | ||
} | ||
|
||
logout() { | ||
localStorage.removeItem('currentUser'); | ||
this.currentUserSubject.next(null); | ||
this.router.navigate(['/login']); | ||
} | ||
|
||
private handleError(error: HttpErrorResponse) { | ||
if (error.error instanceof ErrorEvent) { | ||
console.error('An error occurred:', error.error.message); | ||
} else { | ||
console.error( | ||
`Backend returned code ${error.status}, ` + | ||
`body was: ${error.error}`); | ||
} | ||
return throwError('Something bad happened; please try again later.'); | ||
} | ||
} | ||
|
||
@Injectable() | ||
export class AuthInterceptor { | ||
constructor(private router: Router) {} | ||
|
||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | ||
const currentUser = JSON.parse(localStorage.getItem('currentUser')); | ||
if (currentUser && currentUser.token) { | ||
req = req.clone({ | ||
setHeaders: { | ||
Authorization: `Bearer ${currentUser.token}` | ||
} | ||
}); | ||
} | ||
|
||
return next.handle(req).pipe(catchError(err => { | ||
if (err.status === 401) { | ||
this.router.navigate(['/login']); | ||
} | ||
const error = err.error.message || err.statusText; | ||
return throwError(error); | ||
})); | ||
} | ||
} | ||
|
||
export const authInterceptorProviders = [ | ||
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } | ||
]; | ||
|
||
@Injectable({ | ||
providedIn: 'root' | ||
}) | ||
export class SessionService { | ||
private sessionId: string | null = null; | ||
private user: any = null; | ||
|
||
constructor() {} | ||
|
||
create(sessionId: string, user: any) { | ||
this.sessionId = sessionId; | ||
this.user = user; | ||
} | ||
|
||
destroy() { | ||
this.sessionId = null; | ||
this.user = null; | ||
} | ||
|
||
get isAuthenticated(): boolean { | ||
return !!this.user; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
describe("fsaPreBuilt", function () { | ||
beforeEach(module("fsaPreBuilt")); | ||
|
||
var $rootScope, $httpBackend, AUTH_EVENTS, AuthService, Session; | ||
|
||
beforeEach(inject(function (_$rootScope_, _$httpBackend_, _AUTH_EVENTS_, _AuthService_, _Session_) { | ||
$rootScope = _$rootScope_; | ||
$httpBackend = _$httpBackend_; | ||
AUTH_EVENTS = _AUTH_EVENTS_; | ||
AuthService = _AuthService_; | ||
Session = _Session_; | ||
})); | ||
|
||
describe("AuthService", function () { | ||
it("should broadcast loginSuccess on successful login", function () { | ||
var response = { data: { id: 1, user: { name: 'TestUser' } } }; | ||
$httpBackend.whenPOST('/login').respond(200, response); | ||
var loginSuccessSpy = sinon.spy($rootScope, '$broadcast'); | ||
|
||
AuthService.login({ username: 'test', password: 'pass' }); | ||
$httpBackend.flush(); | ||
|
||
expect(loginSuccessSpy).to.have.been.calledWith(AUTH_EVENTS.loginSuccess); | ||
expect(Session.user).to.deep.equal(response.data.user); | ||
|
||
loginSuccessSpy.restore(); | ||
}); | ||
|
||
it("should reject with a message on failed login", function (done) { | ||
$httpBackend.whenPOST('/login').respond(401, { message: 'Invalid login credentials.' }); | ||
|
||
AuthService.login({ username: 'test', password: 'wrong' }) | ||
.catch(function (error) { | ||
expect(error.message).to.equal('Invalid login credentials.'); | ||
done(); | ||
}); | ||
$httpBackend.flush(); | ||
}); | ||
|
||
it("should broadcast logoutSuccess on successful logout", function () { | ||
$httpBackend.whenGET('/logout').respond(200); | ||
var logoutSuccessSpy = sinon.spy($rootScope, '$broadcast'); | ||
|
||
AuthService.logout(); | ||
$httpBackend.flush(); | ||
|
||
expect(logoutSuccessSpy).to.have.been.calledWith(AUTH_EVENTS.logoutSuccess); | ||
expect(Session.id).to.be.null; | ||
expect(Session.user).to.be.null; | ||
|
||
logoutSuccessSpy.restore(); | ||
}); | ||
|
||
it("should destroy session on notAuthenticated event", function () { | ||
Session.create('sessionId', { name: 'TestUser' }); | ||
$rootScope.$broadcast(AUTH_EVENTS.notAuthenticated); | ||
|
||
expect(Session.id).to.be.null; | ||
expect(Session.user).to.be.null; | ||
}); | ||
|
||
it("should destroy session on sessionTimeout event", function () { | ||
Session.create('sessionId', { name: 'TestUser' }); | ||
$rootScope.$broadcast(AUTH_EVENTS.sessionTimeout); | ||
|
||
expect(Session.id).to.be.null; | ||
expect(Session.user).to.be.null; | ||
}); | ||
}); | ||
|
||
describe("AuthInterceptor", function () { | ||
var AuthInterceptor, responseErrorSpy; | ||
|
||
beforeEach(inject(function (_AuthInterceptor_) { | ||
AuthInterceptor = _AuthInterceptor_; | ||
responseErrorSpy = sinon.spy($rootScope, '$broadcast'); | ||
})); | ||
|
||
it("should broadcast corresponding event on response error with status 401", function () { | ||
var rejection = { status: 401 }; | ||
AuthInterceptor.responseError(rejection); | ||
|
||
expect(responseErrorSpy).to.have.been.calledWith(AUTH_EVENTS.notAuthenticated, rejection); | ||
responseErrorSpy.restore(); | ||
}); | ||
|
||
it("should broadcast corresponding event on response error with status 403", function () { | ||
var rejection = { status: 403 }; | ||
AuthInterceptor.responseError(rejection); | ||
|
||
expect(responseErrorSpy).to.have.been.calledWith(AUTH_EVENTS.notAuthorized, rejection); | ||
responseErrorSpy.restore(); | ||
}); | ||
|
||
afterEach(function () { | ||
responseErrorSpy.restore(); | ||
}); | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.