Linkup footer back to top with authentication

Signed-off-by: Dillon-Brown <dillon.brown@salesagility.com>
This commit is contained in:
Dillon-Brown 2020-03-30 14:14:09 +01:00
parent ca47dded0a
commit 9d484ed4a4
11 changed files with 105 additions and 41 deletions

View file

@ -52,3 +52,4 @@ dunglas_angular_csrf:
- { path: ^/logout$ }
- { path: ^/(_(profiler|wdt))/ }
- { path: ^/api }
- { path: ^/session-status$ }

View file

@ -31,6 +31,8 @@ const routes: Routes = [
{
path: 'Home',
loadChildren: () => import('../components/home/home.module').then(m => m.HomeUiModule),
canActivate: [AuthGuard],
runGuardsAndResolvers: 'always',
resolve: {
metadata: BaseMetadataResolver,
}

View file

@ -1,26 +1,26 @@
<!-- Start of footer section -->
<div class="footer">
<div class="copyright-links">
<a (click)="openSuiteCopyright(suitecopyright)" class="footer-link" data-toggle="modal"
data-target=".copyright-suitecrm">
&copy; Supercharged by SuiteCRM
</a>
<a (click)="openSugarCopyright(sugarcopyright)" class="footer-link" data-toggle="modal"
data-target=".copyright-sugarcrm">
&copy; Powered By SugarCRM
</a>
</div>
<div class="copyright-links">
<a (click)="openSuiteCopyright(suitecopyright)" class="footer-link" data-toggle="modal"
data-target=".copyright-suitecrm">
&copy; Supercharged by SuiteCRM
</a>
<a (click)="openSugarCopyright(sugarcopyright)" class="footer-link" data-toggle="modal"
data-target=".copyright-sugarcrm">
&copy; Powered By SugarCRM
</a>
</div>
<ng-container *ngIf="this.isUserLoggedIn">
<div class="back-to-top">
<a (click)="backToTop()" class="footer-link">
<a (click)="backToTop()" class="footer-link">
<span>
Back To Top
<scrm-image class="sicon back-top-icon" image="arrow_up_filled"></scrm-image>
</span>
</a>
</a>
</div>
</ng-container>
</div>
<!-- End of footer section -->
<!-- Start of copyright modal section -->
@ -98,4 +98,4 @@
<!-- End of SuiteCRM Copyright notice modal -->
</div>
<!-- End of copyright modal section -->
<!-- End of copyright modal section -->

View file

@ -5,6 +5,7 @@ import {RouterTestingModule} from '@angular/router/testing';
import {HttpClientTestingModule} from '@angular/common/http/testing';
import {FooterUiComponent} from './footer.component';
import {ApolloTestingModule} from "apollo-angular/testing";
describe('FooterUiComponent', () => {
let component: FooterUiComponent;
@ -13,7 +14,7 @@ describe('FooterUiComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [RouterTestingModule, HttpClientTestingModule, FormsModule],
imports: [RouterTestingModule, HttpClientTestingModule, FormsModule, ApolloTestingModule],
declarations: [FooterUiComponent]
})
.compileComponents();

View file

@ -1,6 +1,8 @@
import {Component, OnInit} from '@angular/core';
import {ModalDismissReasons, NgbModal} from '@ng-bootstrap/ng-bootstrap';
import {AuthService} from "@services/auth/auth.service";
import {Subscription} from "rxjs";
@Component({
selector: 'scrm-footer-ui',
@ -9,9 +11,15 @@ import {ModalDismissReasons, NgbModal} from '@ng-bootstrap/ng-bootstrap';
})
export class FooterUiComponent implements OnInit {
closeResult: string;
private authSub: Subscription;
constructor(private modalService: NgbModal) {
closeResult: string;
isUserLoggedIn: boolean;
constructor(
private modalService: NgbModal,
private authService: AuthService
) {
}
openSugarCopyright(sugarcopyright) {
@ -54,6 +62,12 @@ export class FooterUiComponent implements OnInit {
}
ngOnInit() {
this.authSub = this.authService.isUserLoggedIn.subscribe(value => {
this.isUserLoggedIn = value;
});
}
ngOnDestroy() {
this.authSub.unsubscribe();
}
}

View file

@ -19,8 +19,8 @@
<!-- Start of empty navbar with logo -->
<ng-template [ngIf]="loaded">
<ng-template [ngIf]="!navbar.authenticated">
<ng-container *ngIf="loaded">
<ng-container *ngIf="!this.isUserLoggedIn">
<nav class="navbar ml-0 pl-0">
<div class="navbar-collapse">
<ul class="navbar-nav">
@ -30,13 +30,13 @@
</ul>
</div>
</nav>
</ng-template>
</ng-container>
<!-- End of empty navbar section with logo -->
<!-- Start of mobile navigation section -->
<ng-template [ngIf]="mobileNavbar">
<ng-container *ngIf="this.isUserLoggedIn && mobileNavbar">
<ul class="navbar mobile-nav-block mobilenavbar">
<div class="position-static" ngbDropdown #myDrop="ngbDropdown" [autoClose]="false">
<button aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"
@ -110,13 +110,13 @@
</ul>
</ng-template>
</ng-container>
<!-- End of mobile navigation section-->
<!-- Start of navbar section with data once authenticated -->
<ng-template [ngIf]="navbar.authenticated && !mobileNavbar">
<ng-container *ngIf="this.isUserLoggedIn && !mobileNavbar">
<nav class="navbar navbar-expand-md navbar-1">
<div class="navbar-collapse collapse collapsenav" [ngbCollapse]="mainNavCollapse">
<ul class="navbar-nav home-nav">
@ -252,8 +252,8 @@
<!-- End of navbar section with data once authenticated -->
<scrm-action-bar-ui></scrm-action-bar-ui>
</ng-template>
</ng-template>
</ng-container>
</ng-container>
</div>
<!-- End of main navbar section -->
<!-- End of main navbar section -->

View file

@ -12,6 +12,7 @@ import {navigationMock} from '@base/facades/navigation/navigation.facade.spec.mo
import {languageFacadeMock} from '@base/facades/language/language.facade.spec.mock';
import {UserPreferenceFacade} from '@base/facades/user-preference/user-preference.facade';
import {userPreferenceFacadeMock} from '@base/facades/user-preference/user-preference.facade.spec.mock';
import {ApolloTestingModule} from "apollo-angular/testing";
describe('NavbarUiComponent', () => {
@ -26,7 +27,8 @@ describe('NavbarUiComponent', () => {
imports: [
RouterTestingModule,
HttpClientTestingModule,
NgbModule
NgbModule,
ApolloTestingModule
],
providers: [
{provide: NavigationFacade, useValue: navigationMock},

View file

@ -10,6 +10,7 @@ import {LanguageFacade, LanguageListStringMap, LanguageStringMap} from '@base/fa
import {UserPreferenceFacade, UserPreferenceMap} from '@base/facades/user-preference/user-preference.facade';
import {map} from 'rxjs/operators';
import {AuthService} from "@services/auth/auth.service";
@Component({
selector: 'scrm-navbar-ui',
@ -21,6 +22,7 @@ export class NavbarUiComponent implements OnInit {
protected static instances: NavbarUiComponent[] = [];
loaded = true;
isUserLoggedIn: boolean;
mainNavCollapse = true;
subItemCollapse = true;
@ -75,7 +77,9 @@ export class NavbarUiComponent implements OnInit {
constructor(protected navigationFacade: NavigationFacade,
protected languageFacade: LanguageFacade,
protected api: ApiService,
protected userPreferenceFacade: UserPreferenceFacade) {
protected userPreferenceFacade: UserPreferenceFacade,
private authService: AuthService
) {
const navbar = new NavbarAbstract();
this.setNavbar(navbar);
@ -115,6 +119,9 @@ export class NavbarUiComponent implements OnInit {
ngOnInit(): void {
const navbar = new NavbarAbstract();
this.setNavbar(navbar);
this.authService.isUserLoggedIn.subscribe(value => {
this.isUserLoggedIn = value;
});
window.dispatchEvent(new Event('resize'));
}
@ -127,4 +134,8 @@ export class NavbarUiComponent implements OnInit {
protected isLoaded() {
return this.loaded;
}
ngOnDestroy() {
this.authService.isUserLoggedIn.unsubscribe();
}
}

View file

@ -1,27 +1,51 @@
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
import {MessageService} from '../message/message.service';
import {AuthService} from '../auth/auth.service';
import {Observable, of} from "rxjs";
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {catchError, map, take, tap} from "rxjs/operators";
import {StateManager} from "@base/facades/state-manager";
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
protected message: MessageService,
protected router: Router,
private authService: AuthService
protected http: HttpClient,
private authService: AuthService,
protected stateManager: StateManager,
) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const currentUser = this.authService.currentUserValue;
if (currentUser) {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
if (this.authService.isUserLoggedIn.value) {
return true;
}
const loginUrl = 'Login';
const tree: UrlTree = this.router.parseUrl(loginUrl);
return true;
const Url = 'session-status';
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
return this.http.get(Url, {headers})
.pipe(
take(1),
map((user: any) => {
if (user && user.active === true) {
return true;
}
// Re-direct to login
return tree;
}),
catchError(() => of(tree)),
tap((result: boolean | UrlTree) => {
if (result === true) {
this.authService.isUserLoggedIn.next(true);
}
})
);
}
}

View file

@ -18,6 +18,7 @@ export class AuthService {
public currentUser$: Observable<User>;
private currentUserSubject: BehaviorSubject<User>;
defaultTimeout: string = '3600';
public isUserLoggedIn: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
constructor(
private http: HttpClient,
@ -32,10 +33,6 @@ export class AuthService {
this.currentUser$ = this.currentUserSubject.asObservable();
}
public get currentUserValue(): User {
return this.currentUserSubject.value;
}
doLogin(
caller: LoginUiComponent,
username: string,
@ -58,6 +55,8 @@ export class AuthService {
{headers}
).subscribe((response: any) => {
onSuccess(caller, response);
this.isUserLoggedIn.next(true);
let duration = response.duration;
if (duration === 0 || duration === '0') {
@ -107,6 +106,7 @@ export class AuthService {
this.message.log('Logout success');
const label = this.languageFacade.getAppString(messageKey);
this.message.addSuccessMessage(label);
this.isUserLoggedIn.next(false);
});
}
}

View file

@ -40,4 +40,13 @@ class SecurityController extends AbstractController
{
throw new RuntimeException('This will be intercepted by the logout key');
}
/**
* @Route("/session-status", name="app_session_status", methods={"GET"})
* @throws Exception
*/
public function sessionStatus(): JsonResponse
{
return new JsonResponse(['active' => true], Response::HTTP_OK);
}
}