src/client/app/core/auth-guard.service.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router';
import { Observable, Observer } from 'rxjs';
import { UserStateService } from '../admin/authentication/user-state.service';
import { AuthenticationService } from '../admin/authentication/authentication.service';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private userStateService: UserStateService,
private authService: AuthenticationService,
private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
// This chain of observables subscribes to the fact that authService may still be initializing when the
// app first loads. We need to wait for an attempted authentication of the user, eua so we subscribe
// to its initializing property. Returning an Observable from this function (canActivate) tells angular to
// wait until it is done before activating the route.
return Observable.create( (observer: Observer<boolean>) => {
this.authService.initializing$
.subscribe( (isInitializing: boolean) => {
if (!isInitializing) {
let url: string = state.url;
observer.next(this.checkAccess(url, route));
observer.complete();
}
});
});
}
/**
*
* @param url - If set, LoginComponent redirects to authRedirectUrl upon successful login
* @param route - activated route
* @returns {boolean} - true if passes checkes; false otherwise
*/
protected checkAccess(url: string, route: ActivatedRouteSnapshot): boolean {
let requiresAuthentication: boolean = true; // default this to true unless told otherwise...
// Only set it to false if it is explicitly set
if (null != route.data && (route.data as any).requiresAuthentication === false) {
requiresAuthentication = false;
}
// -----------------------------------------------------------
// Does the user need to log in?
// -----------------------------------------------------------
// If the route requires authentication and the user is not authenticated, then go to the signin route
if (url !== '/signin' && requiresAuthentication && !this.userStateService.isAuthenticated()) {
// Store the attempted URL so we can redirect after successful login
this.userStateService.authRedirectUrl = url;
this.router.navigate(['/signin']);
return false;
}
// -----------------------------------------------------------
// Does the user need to accept the user agreement??
// -----------------------------------------------------------
// Check to see if the user needs to agree to the end user agreement
if (this.userStateService.isAuthenticated() && !this.userStateService.user.isAdmin() && !this.userStateService.user.isEuaCurrent() ) {
if (url !== '/user-eua') {
this.userStateService.authRedirectUrl = url;
this.router.navigate(['/user-eua']);
return false;
}
}
return this.checkAdminAccess(url, route);
}
protected checkAdminAccess(url: string, route: ActivatedRouteSnapshot): boolean {
if (!this.userStateService.user.isAdmin()) {
// -----------------------------------------------------------
// Check the role requirements for the route
// -----------------------------------------------------------
// compile a list of roles that are missing
let requiredRoles = (null != route.data && null != (route.data as any).roles) ? (route.data as any).roles : ['user'];
let missingRoles: any[] = [];
requiredRoles.forEach( (role: any) => {
if (!this.userStateService.hasRole(role)) {
missingRoles.push(role);
}
});
// If there are roles missing then we need to do something
if (missingRoles.length > 0) {
if (this.userStateService.isAuthenticated() && !this.userStateService.hasRole('user')) {
// If the user is missing the user role, they are pending
if (url !== '/inactive-user') {
this.userStateService.authRedirectUrl = url;
this.router.navigate(['/inactive-user']);
return false;
}
}
else {
// The user doesn't have the needed roles to view the page
if (url !== '/unauthorized') {
this.userStateService.authRedirectUrl = url;
this.router.navigate(['/unauthorized']);
return false;
}
}
}
}
return true;
}
}