mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
72699: Hard redirect after log in - loading fixes
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
<div class="outer-wrapper">
|
||||
<div class="text-center ds-full-screen-loader d-flex align-items-center flex-column justify-content-center" *ngIf="!(hasAuthFinishedLoading$ | async)">
|
||||
<ds-loading [showMessage]="false"></ds-loading>
|
||||
</div>
|
||||
<div class="outer-wrapper" *ngIf="hasAuthFinishedLoading$ | async">
|
||||
<ds-admin-sidebar></ds-admin-sidebar>
|
||||
<div class="inner-wrapper" [@slideSidebarPadding]="{
|
||||
value: (!(sidebarVisible | async) ? 'hidden' : (slideSidebarOver | async) ? 'shown' : 'expanded'),
|
||||
|
@@ -47,3 +47,7 @@ ds-admin-sidebar {
|
||||
position: fixed;
|
||||
z-index: $sidebar-z-index;
|
||||
}
|
||||
|
||||
.ds-full-screen-loader {
|
||||
height: 100vh;
|
||||
}
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import * as ngrx from '@ngrx/store';
|
||||
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { Store, StoreModule } from '@ngrx/store';
|
||||
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
|
||||
@@ -30,13 +29,13 @@ import { RouteService } from './core/services/route.service';
|
||||
import { MockActivatedRoute } from './shared/mocks/active-router.mock';
|
||||
import { RouterMock } from './shared/mocks/router.mock';
|
||||
import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';
|
||||
import { storeModuleConfig } from './app.reducer';
|
||||
import { AppState, storeModuleConfig } from './app.reducer';
|
||||
import { LocaleService } from './core/locale/locale.service';
|
||||
import { authReducer } from './core/auth/auth.reducer';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
|
||||
let comp: AppComponent;
|
||||
let fixture: ComponentFixture<AppComponent>;
|
||||
let de: DebugElement;
|
||||
let el: HTMLElement;
|
||||
const menuService = new MenuServiceStub();
|
||||
|
||||
describe('App component', () => {
|
||||
@@ -52,7 +51,7 @@ describe('App component', () => {
|
||||
return TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
StoreModule.forRoot({}, storeModuleConfig),
|
||||
StoreModule.forRoot(authReducer, storeModuleConfig),
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
@@ -82,12 +81,19 @@ describe('App component', () => {
|
||||
|
||||
// synchronous beforeEach
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
spyOnProperty(ngrx, 'select').and.callFake(() => {
|
||||
return () => {
|
||||
return () => cold('a', {
|
||||
a: {
|
||||
core: { auth: { loading: false } }
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
comp = fixture.componentInstance; // component test instance
|
||||
// query for the <div class='outer-wrapper'> by CSS element selector
|
||||
de = fixture.debugElement.query(By.css('div.outer-wrapper'));
|
||||
el = de.nativeElement;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create component', inject([AppComponent], (app: AppComponent) => {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { delay, filter, map, take } from 'rxjs/operators';
|
||||
import { delay, filter, map, take, distinctUntilChanged } from 'rxjs/operators';
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
@@ -19,7 +19,7 @@ import { MetadataService } from './core/metadata/metadata.service';
|
||||
import { HostWindowResizeAction } from './shared/host-window.actions';
|
||||
import { HostWindowState } from './shared/search/host-window.reducer';
|
||||
import { NativeWindowRef, NativeWindowService } from './core/services/window.service';
|
||||
import { isAuthenticated } from './core/auth/selectors';
|
||||
import { isAuthenticated, isAuthenticationLoading } from './core/auth/selectors';
|
||||
import { AuthService } from './core/auth/auth.service';
|
||||
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
||||
import { MenuService } from './shared/menu/menu.service';
|
||||
@@ -52,6 +52,11 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
notificationOptions = environment.notifications;
|
||||
models;
|
||||
|
||||
/**
|
||||
* Whether or not the authenticated has finished loading
|
||||
*/
|
||||
hasAuthFinishedLoading$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
@Inject(NativeWindowService) private _window: NativeWindowRef,
|
||||
private translate: TranslateService,
|
||||
@@ -89,6 +94,10 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.hasAuthFinishedLoading$ = this.store.pipe(select(isAuthenticationLoading)).pipe(
|
||||
map((isLoading: boolean) => isLoading === false),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
const env: string = environment.production ? 'Production' : 'Development';
|
||||
const color: string = environment.production ? 'red' : 'green';
|
||||
console.info(`Environment: %c${env}`, `color: ${color}; font-weight: bold;`);
|
||||
|
@@ -34,6 +34,7 @@ export const AuthActionTypes = {
|
||||
RETRIEVE_AUTHENTICATED_EPERSON: type('dspace/auth/RETRIEVE_AUTHENTICATED_EPERSON'),
|
||||
RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS: type('dspace/auth/RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS'),
|
||||
RETRIEVE_AUTHENTICATED_EPERSON_ERROR: type('dspace/auth/RETRIEVE_AUTHENTICATED_EPERSON_ERROR'),
|
||||
REDIRECT_AFTER_LOGIN_SUCCESS: type('dspace/auth/REDIRECT_AFTER_LOGIN_SUCCESS')
|
||||
};
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
@@ -335,6 +336,20 @@ export class SetRedirectUrlAction implements Action {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start loading for a hard redirect
|
||||
* @class StartHardRedirectLoadingAction
|
||||
* @implements {Action}
|
||||
*/
|
||||
export class RedirectAfterLoginSuccessAction implements Action {
|
||||
public type: string = AuthActionTypes.REDIRECT_AFTER_LOGIN_SUCCESS;
|
||||
payload: string;
|
||||
|
||||
constructor(url: string) {
|
||||
this.payload = url;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the authenticated eperson.
|
||||
* @class RetrieveAuthenticatedEpersonAction
|
||||
@@ -402,8 +417,8 @@ export type AuthActions
|
||||
| RetrieveAuthMethodsSuccessAction
|
||||
| RetrieveAuthMethodsErrorAction
|
||||
| RetrieveTokenAction
|
||||
| ResetAuthenticationMessagesAction
|
||||
| RetrieveAuthenticatedEpersonAction
|
||||
| RetrieveAuthenticatedEpersonErrorAction
|
||||
| RetrieveAuthenticatedEpersonSuccessAction
|
||||
| SetRedirectUrlAction;
|
||||
| SetRedirectUrlAction
|
||||
| RedirectAfterLoginSuccessAction;
|
||||
|
@@ -27,6 +27,7 @@ import {
|
||||
CheckAuthenticationTokenCookieAction,
|
||||
LogOutErrorAction,
|
||||
LogOutSuccessAction,
|
||||
RedirectAfterLoginSuccessAction,
|
||||
RefreshTokenAction,
|
||||
RefreshTokenErrorAction,
|
||||
RefreshTokenSuccessAction,
|
||||
@@ -79,7 +80,26 @@ export class AuthEffects {
|
||||
public authenticatedSuccess$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(AuthActionTypes.AUTHENTICATED_SUCCESS),
|
||||
tap((action: AuthenticatedSuccessAction) => this.authService.storeToken(action.payload.authToken)),
|
||||
map((action: AuthenticatedSuccessAction) => new RetrieveAuthenticatedEpersonAction(action.payload.userHref))
|
||||
switchMap((action: AuthenticatedSuccessAction) => this.authService.getRedirectUrl().pipe(
|
||||
take(1),
|
||||
map((redirectUrl: string) => [action, redirectUrl])
|
||||
)),
|
||||
map(([action, redirectUrl]: [AuthenticatedSuccessAction, string]) => {
|
||||
if (hasValue(redirectUrl)) {
|
||||
return new RedirectAfterLoginSuccessAction(redirectUrl);
|
||||
} else {
|
||||
return new RetrieveAuthenticatedEpersonAction(action.payload.userHref);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@Effect({ dispatch: false })
|
||||
public redirectAfterLoginSuccess$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(AuthActionTypes.REDIRECT_AFTER_LOGIN_SUCCESS),
|
||||
tap((action: RedirectAfterLoginSuccessAction) => {
|
||||
this.authService.clearRedirectUrl();
|
||||
this.authService.navigateToRedirectUrl(action.payload);
|
||||
})
|
||||
);
|
||||
|
||||
// It means "reacts to this action but don't send another"
|
||||
|
@@ -62,7 +62,7 @@ export interface AuthState {
|
||||
const initialState: AuthState = {
|
||||
authenticated: false,
|
||||
loaded: false,
|
||||
loading: false,
|
||||
loading: undefined,
|
||||
authMethods: []
|
||||
};
|
||||
|
||||
@@ -201,6 +201,11 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
|
||||
redirectUrl: (action as SetRedirectUrlAction).payload,
|
||||
});
|
||||
|
||||
case AuthActionTypes.REDIRECT_AFTER_LOGIN_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
loading: true,
|
||||
});
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@@ -323,35 +323,30 @@ describe('AuthService test', () => {
|
||||
});
|
||||
|
||||
it('should set redirect url to previous page', () => {
|
||||
spyOn(routeServiceMock, 'getHistory').and.callThrough();
|
||||
authService.redirectAfterLoginSuccess(true);
|
||||
expect(routeServiceMock.getHistory).toHaveBeenCalled();
|
||||
(storage.get as jasmine.Spy).and.returnValue('/collection/123');
|
||||
authService.redirectAfterLoginSuccess();
|
||||
// Reload with redirect URL set to /collection/123
|
||||
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*\\?redirect=' + encodeURIComponent('/collection/123'))));
|
||||
});
|
||||
|
||||
it('should set redirect url to current page', () => {
|
||||
spyOn(routeServiceMock, 'getHistory').and.callThrough();
|
||||
authService.redirectAfterLoginSuccess(false);
|
||||
expect(routeServiceMock.getHistory).toHaveBeenCalled();
|
||||
(storage.get as jasmine.Spy).and.returnValue('/home');
|
||||
authService.redirectAfterLoginSuccess();
|
||||
// Reload with redirect URL set to /home
|
||||
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*\\?redirect=' + encodeURIComponent('/home'))));
|
||||
});
|
||||
|
||||
it('should redirect to / and not to /login', () => {
|
||||
spyOn(routeServiceMock, 'getHistory').and.returnValue(observableOf(['/login', '/login']));
|
||||
authService.redirectAfterLoginSuccess(true);
|
||||
expect(routeServiceMock.getHistory).toHaveBeenCalled();
|
||||
it('should redirect to regular reload and not to /login', () => {
|
||||
(storage.get as jasmine.Spy).and.returnValue('/login');
|
||||
authService.redirectAfterLoginSuccess();
|
||||
// Reload without a redirect URL
|
||||
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*(?!\\?)$')));
|
||||
});
|
||||
|
||||
it('should redirect to / when no redirect url is found', () => {
|
||||
spyOn(routeServiceMock, 'getHistory').and.returnValue(observableOf(['']));
|
||||
authService.redirectAfterLoginSuccess(true);
|
||||
expect(routeServiceMock.getHistory).toHaveBeenCalled();
|
||||
it('should not redirect when no redirect url is found', () => {
|
||||
authService.redirectAfterLoginSuccess();
|
||||
// Reload without a redirect URL
|
||||
expect(hardRedirectService.redirect).toHaveBeenCalledWith(jasmine.stringMatching(new RegExp('/reload/[0-9]*(?!\\?)$')));
|
||||
expect(hardRedirectService.redirect).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('impersonate', () => {
|
||||
|
@@ -14,7 +14,15 @@ import { AuthRequestService } from './auth-request.service';
|
||||
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
|
||||
import { AuthStatus } from './models/auth-status.model';
|
||||
import { AuthTokenInfo, TOKENITEM } from './models/auth-token-info.model';
|
||||
import { hasValue, hasValueOperator, isEmpty, isNotEmpty, isNotNull, isNotUndefined } from '../../shared/empty.util';
|
||||
import {
|
||||
hasValue,
|
||||
hasValueOperator,
|
||||
isEmpty,
|
||||
isNotEmpty,
|
||||
isNotNull,
|
||||
isNotUndefined,
|
||||
hasNoValue
|
||||
} from '../../shared/empty.util';
|
||||
import { CookieService } from '../services/cookie.service';
|
||||
import {
|
||||
getAuthenticatedUserId,
|
||||
@@ -413,35 +421,19 @@ export class AuthService {
|
||||
/**
|
||||
* Redirect to the route navigated before the login
|
||||
*/
|
||||
public redirectAfterLoginSuccess(isStandalonePage: boolean) {
|
||||
public redirectAfterLoginSuccess() {
|
||||
this.getRedirectUrl().pipe(
|
||||
take(1))
|
||||
.subscribe((redirectUrl) => {
|
||||
|
||||
if (isNotEmpty(redirectUrl)) {
|
||||
if (hasValue(redirectUrl)) {
|
||||
this.clearRedirectUrl();
|
||||
this.router.onSameUrlNavigation = 'reload';
|
||||
this.navigateToRedirectUrl(redirectUrl);
|
||||
} else {
|
||||
// If redirectUrl is empty use history.
|
||||
this.routeService.getHistory().pipe(
|
||||
take(1)
|
||||
).subscribe((history) => {
|
||||
let redirUrl;
|
||||
if (isStandalonePage) {
|
||||
// For standalone login pages, use the previous route.
|
||||
redirUrl = history[history.length - 2] || '';
|
||||
} else {
|
||||
redirUrl = history[history.length - 1] || '';
|
||||
}
|
||||
this.navigateToRedirectUrl(redirUrl);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
protected navigateToRedirectUrl(redirectUrl: string) {
|
||||
public navigateToRedirectUrl(redirectUrl: string) {
|
||||
let url = `/reload/${new Date().getTime()}`;
|
||||
if (isNotEmpty(redirectUrl) && !redirectUrl.startsWith(LOGIN_ROUTE)) {
|
||||
url += `?redirect=${encodeURIComponent(redirectUrl)}`;
|
||||
@@ -460,12 +452,16 @@ export class AuthService {
|
||||
* Get redirect url
|
||||
*/
|
||||
getRedirectUrl(): Observable<string> {
|
||||
const redirectUrl = this.storage.get(REDIRECT_COOKIE);
|
||||
if (isNotEmpty(redirectUrl)) {
|
||||
return observableOf(redirectUrl);
|
||||
} else {
|
||||
return this.store.pipe(select(getRedirectUrl));
|
||||
}
|
||||
return this.store.pipe(
|
||||
select(getRedirectUrl),
|
||||
map((urlFromStore: string) => {
|
||||
if (hasValue(urlFromStore)) {
|
||||
return urlFromStore;
|
||||
} else {
|
||||
return this.storage.get(REDIRECT_COOKIE);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -482,6 +478,16 @@ export class AuthService {
|
||||
this.store.dispatch(new SetRedirectUrlAction(isNotUndefined(url) ? url : ''));
|
||||
}
|
||||
|
||||
setRedirectUrlIfNotSet(newRedirectUrl: string) {
|
||||
this.getRedirectUrl().pipe(
|
||||
take(1))
|
||||
.subscribe((currentRedirectUrl) => {
|
||||
if (hasNoValue(currentRedirectUrl)) {
|
||||
this.setRedirectUrl(newRedirectUrl);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear redirect url
|
||||
*/
|
||||
|
@@ -9,11 +9,11 @@ import {
|
||||
} from '@angular/router';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { map, find, switchMap } from 'rxjs/operators';
|
||||
import { select, Store } from '@ngrx/store';
|
||||
|
||||
import { CoreState } from '../core.reducers';
|
||||
import { isAuthenticated } from './selectors';
|
||||
import { isAuthenticated, isAuthenticationLoading } from './selectors';
|
||||
import { AuthService, LOGIN_ROUTE } from './auth.service';
|
||||
|
||||
/**
|
||||
@@ -48,11 +48,10 @@ export class AuthenticatedGuard implements CanActivate {
|
||||
}
|
||||
|
||||
private handleAuth(url: string): Observable<boolean | UrlTree> {
|
||||
// get observable
|
||||
const observable = this.store.pipe(select(isAuthenticated));
|
||||
|
||||
// redirect to sign in page if user is not authenticated
|
||||
return observable.pipe(
|
||||
return this.store.pipe(select(isAuthenticationLoading)).pipe(
|
||||
find((isLoading: boolean) => isLoading === false),
|
||||
switchMap(() => this.store.pipe(select(isAuthenticated))),
|
||||
map((authenticated) => {
|
||||
if (authenticated) {
|
||||
return authenticated;
|
||||
|
@@ -58,32 +58,4 @@ export class ServerAuthService extends AuthService {
|
||||
map((status: AuthStatus) => Object.assign(new AuthStatus(), status))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to the route navigated before the login
|
||||
*/
|
||||
public redirectAfterLoginSuccess(isStandalonePage: boolean) {
|
||||
this.getRedirectUrl().pipe(
|
||||
take(1))
|
||||
.subscribe((redirectUrl) => {
|
||||
if (isNotEmpty(redirectUrl)) {
|
||||
// override the route reuse strategy
|
||||
this.router.routeReuseStrategy.shouldReuseRoute = () => {
|
||||
return false;
|
||||
};
|
||||
this.router.navigated = false;
|
||||
const url = decodeURIComponent(redirectUrl);
|
||||
this.router.navigateByUrl(url);
|
||||
} else {
|
||||
// If redirectUrl is empty use history. For ssr the history array should contain the requested url.
|
||||
this.routeService.getHistory().pipe(
|
||||
filter((history) => history.length > 0),
|
||||
take(1)
|
||||
).subscribe((history) => {
|
||||
this.navigateToRedirectUrl(history[history.length - 1] || '');
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -5,7 +5,8 @@ describe('BrowserHardRedirectService', () => {
|
||||
|
||||
const mockLocation = {
|
||||
href: undefined,
|
||||
origin: 'test origin',
|
||||
pathname: '/pathname',
|
||||
search: '/search',
|
||||
} as Location;
|
||||
|
||||
const service: BrowserHardRedirectService = new BrowserHardRedirectService(mockLocation);
|
||||
@@ -31,10 +32,10 @@ describe('BrowserHardRedirectService', () => {
|
||||
})
|
||||
});
|
||||
|
||||
describe('when requesting the origin', () => {
|
||||
describe('when requesting the current route', () => {
|
||||
|
||||
it('should return the location origin', () => {
|
||||
expect(service.getOriginFromUrl()).toEqual('test origin');
|
||||
expect(service.getCurrentRoute()).toEqual(mockLocation.pathname + mockLocation.search);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import {Inject, Injectable} from '@angular/core';
|
||||
import {LocationToken} from '../../../modules/app/browser-app.module';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { LocationToken } from '../../../modules/app/browser-app.module';
|
||||
import { HardRedirectService } from './hard-redirect.service';
|
||||
|
||||
/**
|
||||
* Service for performing hard redirects within the browser app module
|
||||
*/
|
||||
@Injectable()
|
||||
export class BrowserHardRedirectService {
|
||||
export class BrowserHardRedirectService implements HardRedirectService {
|
||||
|
||||
constructor(
|
||||
@Inject(LocationToken) protected location: Location,
|
||||
@@ -23,7 +24,7 @@ export class BrowserHardRedirectService {
|
||||
/**
|
||||
* Get the origin of a request
|
||||
*/
|
||||
getOriginFromUrl() {
|
||||
return this.location.origin;
|
||||
getCurrentRoute() {
|
||||
return this.location.pathname + this.location.search;
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,8 @@ export abstract class HardRedirectService {
|
||||
abstract redirect(url: string);
|
||||
|
||||
/**
|
||||
* Get the origin of a request
|
||||
* Get the current route, with query params included
|
||||
* e.g. /search?page=1&query=open%20access&f.dateIssued.min=1980&f.dateIssued.max=2020
|
||||
*/
|
||||
abstract getOriginFromUrl();
|
||||
abstract getCurrentRoute();
|
||||
}
|
||||
|
@@ -30,19 +30,14 @@ describe('ServerHardRedirectService', () => {
|
||||
})
|
||||
});
|
||||
|
||||
describe('when requesting the origin', () => {
|
||||
describe('when requesting the current route', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
mockRequest.protocol = 'test-protocol';
|
||||
mockRequest.get.and.callFake((name) => {
|
||||
if (name === 'hostname') {
|
||||
return 'test-host';
|
||||
}
|
||||
});
|
||||
mockRequest.originalUrl = 'original/url';
|
||||
});
|
||||
|
||||
it('should return the location origin', () => {
|
||||
expect(service.getOriginFromUrl()).toEqual('test-protocol://test-host');
|
||||
expect(service.getCurrentRoute()).toEqual(mockRequest.originalUrl);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,12 +1,13 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { Request, Response } from 'express';
|
||||
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
|
||||
import { HardRedirectService } from './hard-redirect.service';
|
||||
|
||||
/**
|
||||
* Service for performing hard redirects within the server app module
|
||||
*/
|
||||
@Injectable()
|
||||
export class ServerHardRedirectService {
|
||||
export class ServerHardRedirectService implements HardRedirectService {
|
||||
|
||||
constructor(
|
||||
@Inject(REQUEST) protected req: Request,
|
||||
@@ -55,8 +56,7 @@ export class ServerHardRedirectService {
|
||||
/**
|
||||
* Get the origin of a request
|
||||
*/
|
||||
getOriginFromUrl() {
|
||||
|
||||
return new URL(`${this.req.protocol}://${this.req.get('hostname')}`).toString();
|
||||
getCurrentRoute() {
|
||||
return this.req.originalUrl;
|
||||
}
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ import { AuthService } from '../../../core/auth/auth.service';
|
||||
import { AuthMethod } from '../../../core/auth/models/auth.method';
|
||||
import { AuthServiceStub } from '../../testing/auth-service.stub';
|
||||
import { createTestComponent } from '../../testing/utils.test';
|
||||
import { HardRedirectService } from '../../../core/services/hard-redirect.service';
|
||||
|
||||
describe('LogInContainerComponent', () => {
|
||||
|
||||
@@ -20,7 +21,13 @@ describe('LogInContainerComponent', () => {
|
||||
|
||||
const authMethod = new AuthMethod('password');
|
||||
|
||||
let hardRedirectService: HardRedirectService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
hardRedirectService = jasmine.createSpyObj('hardRedirectService', {
|
||||
redirect: {},
|
||||
getCurrentRoute: {}
|
||||
});
|
||||
// refine the test module by declaring the test component
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
@@ -35,6 +42,7 @@ describe('LogInContainerComponent', () => {
|
||||
],
|
||||
providers: [
|
||||
{provide: AuthService, useClass: AuthServiceStub},
|
||||
{ provide: HardRedirectService, useValue: hardRedirectService },
|
||||
LogInContainerComponent
|
||||
],
|
||||
schemas: [
|
||||
|
@@ -18,6 +18,7 @@ import { NativeWindowService } from '../../core/services/window.service';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { createTestComponent } from '../testing/utils.test';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { HardRedirectService } from '../../core/services/hard-redirect.service';
|
||||
|
||||
describe('LogInComponent', () => {
|
||||
|
||||
@@ -33,8 +34,13 @@ describe('LogInComponent', () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
let hardRedirectService: HardRedirectService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
hardRedirectService = jasmine.createSpyObj('hardRedirectService', {
|
||||
redirect: {},
|
||||
getCurrentRoute: {}
|
||||
});
|
||||
// refine the test module by declaring the test component
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
@@ -58,6 +64,7 @@ describe('LogInComponent', () => {
|
||||
{ provide: NativeWindowService, useFactory: NativeWindowMockFactory },
|
||||
// { provide: Router, useValue: new RouterStub() },
|
||||
{ provide: ActivatedRoute, useValue: new ActivatedRouteStub() },
|
||||
{ provide: HardRedirectService, useValue: hardRedirectService },
|
||||
provideMockStore({ initialState }),
|
||||
LogInComponent
|
||||
],
|
||||
|
@@ -1,13 +1,9 @@
|
||||
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { filter, takeWhile, } from 'rxjs/operators';
|
||||
import { select, Store } from '@ngrx/store';
|
||||
|
||||
import { AuthMethod } from '../../core/auth/models/auth.method';
|
||||
import { getAuthenticationMethods, isAuthenticated, isAuthenticationLoading } from '../../core/auth/selectors';
|
||||
import { CoreState } from '../../core/core.reducers';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { getForgotPasswordPath, getRegisterPath } from '../../app-routing.module';
|
||||
|
||||
/**
|
||||
@@ -19,7 +15,7 @@ import { getForgotPasswordPath, getRegisterPath } from '../../app-routing.module
|
||||
templateUrl: './log-in.component.html',
|
||||
styleUrls: ['./log-in.component.scss']
|
||||
})
|
||||
export class LogInComponent implements OnInit, OnDestroy {
|
||||
export class LogInComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* A boolean representing if LogInComponent is in a standalone page
|
||||
@@ -45,14 +41,7 @@ export class LogInComponent implements OnInit, OnDestroy {
|
||||
*/
|
||||
public loading: Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Component state.
|
||||
* @type {boolean}
|
||||
*/
|
||||
private alive = true;
|
||||
|
||||
constructor(private store: Store<CoreState>,
|
||||
private authService: AuthService,) {
|
||||
constructor(private store: Store<CoreState>) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -66,21 +55,6 @@ export class LogInComponent implements OnInit, OnDestroy {
|
||||
|
||||
// set isAuthenticated
|
||||
this.isAuthenticated = this.store.pipe(select(isAuthenticated));
|
||||
|
||||
// subscribe to success
|
||||
this.store.pipe(
|
||||
select(isAuthenticated),
|
||||
takeWhile(() => this.alive),
|
||||
filter((authenticated) => authenticated))
|
||||
.subscribe(() => {
|
||||
this.authService.redirectAfterLoginSuccess(this.isStandalonePage);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.alive = false;
|
||||
}
|
||||
|
||||
getRegisterPath() {
|
||||
|
@@ -15,6 +15,7 @@ import { AuthServiceStub } from '../../../testing/auth-service.stub';
|
||||
import { AppState } from '../../../../app.reducer';
|
||||
import { AuthMethod } from '../../../../core/auth/models/auth.method';
|
||||
import { AuthMethodType } from '../../../../core/auth/models/auth.method-type';
|
||||
import { HardRedirectService } from '../../../../core/services/hard-redirect.service';
|
||||
|
||||
describe('LogInPasswordComponent', () => {
|
||||
|
||||
@@ -29,8 +30,14 @@ describe('LogInPasswordComponent', () => {
|
||||
loading: false,
|
||||
};
|
||||
|
||||
let hardRedirectService: HardRedirectService;
|
||||
|
||||
beforeEach(() => {
|
||||
user = EPersonMock;
|
||||
|
||||
hardRedirectService = jasmine.createSpyObj('hardRedirectService', {
|
||||
getCurrentRoute: {}
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(async(() => {
|
||||
@@ -47,7 +54,8 @@ describe('LogInPasswordComponent', () => {
|
||||
],
|
||||
providers: [
|
||||
{ provide: AuthService, useClass: AuthServiceStub },
|
||||
{ provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Password) }
|
||||
{ provide: 'authMethodProvider', useValue: new AuthMethod(AuthMethodType.Password) },
|
||||
{ provide: HardRedirectService, useValue: hardRedirectService },
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
|
@@ -13,6 +13,8 @@ import { fadeOut } from '../../../animations/fade';
|
||||
import { AuthMethodType } from '../../../../core/auth/models/auth.method-type';
|
||||
import { renderAuthMethodFor } from '../log-in.methods-decorator';
|
||||
import { AuthMethod } from '../../../../core/auth/models/auth.method';
|
||||
import { AuthService } from '../../../../core/auth/auth.service';
|
||||
import { HardRedirectService } from '../../../../core/services/hard-redirect.service';
|
||||
|
||||
/**
|
||||
* /users/sign-in
|
||||
@@ -66,11 +68,15 @@ export class LogInPasswordComponent implements OnInit {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {AuthMethod} injectedAuthMethodModel
|
||||
* @param {AuthService} authService
|
||||
* @param {HardRedirectService} hardRedirectService
|
||||
* @param {FormBuilder} formBuilder
|
||||
* @param {Store<State>} store
|
||||
*/
|
||||
constructor(
|
||||
@Inject('authMethodProvider') public injectedAuthMethodModel: AuthMethod,
|
||||
private authService: AuthService,
|
||||
private hardRedirectService: HardRedirectService,
|
||||
private formBuilder: FormBuilder,
|
||||
private store: Store<CoreState>
|
||||
) {
|
||||
@@ -134,6 +140,8 @@ export class LogInPasswordComponent implements OnInit {
|
||||
email.trim();
|
||||
password.trim();
|
||||
|
||||
this.authService.setRedirectUrlIfNotSet(this.hardRedirectService.getCurrentRoute());
|
||||
|
||||
// dispatch AuthenticationAction
|
||||
this.store.dispatch(new AuthenticateAction(email, password));
|
||||
|
||||
|
@@ -17,6 +17,7 @@ import { NativeWindowService } from '../../../../core/services/window.service';
|
||||
import { RouterStub } from '../../../testing/router.stub';
|
||||
import { ActivatedRouteStub } from '../../../testing/active-router.stub';
|
||||
import { NativeWindowMockFactory } from '../../../mocks/mock-native-window-ref';
|
||||
import { HardRedirectService } from '../../../../core/services/hard-redirect.service';
|
||||
|
||||
describe('LogInShibbolethComponent', () => {
|
||||
|
||||
@@ -30,6 +31,7 @@ describe('LogInShibbolethComponent', () => {
|
||||
let location;
|
||||
|
||||
let authState;
|
||||
let hardRedirectService: HardRedirectService;
|
||||
|
||||
beforeEach(() => {
|
||||
user = EPersonMock;
|
||||
@@ -41,6 +43,10 @@ describe('LogInShibbolethComponent', () => {
|
||||
loaded: false,
|
||||
loading: false,
|
||||
};
|
||||
|
||||
hardRedirectService = jasmine.createSpyObj('hardRedirectService', {
|
||||
getCurrentRoute: {}
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(async(() => {
|
||||
@@ -59,6 +65,7 @@ describe('LogInShibbolethComponent', () => {
|
||||
{ provide: NativeWindowService, useFactory: NativeWindowMockFactory },
|
||||
{ provide: Router, useValue: new RouterStub() },
|
||||
{ provide: ActivatedRoute, useValue: new ActivatedRouteStub() },
|
||||
{ provide: HardRedirectService, useValue: hardRedirectService },
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
|
@@ -12,6 +12,8 @@ import { isAuthenticated, isAuthenticationLoading } from '../../../../core/auth/
|
||||
import { RouteService } from '../../../../core/services/route.service';
|
||||
import { NativeWindowRef, NativeWindowService } from '../../../../core/services/window.service';
|
||||
import { isNotNull } from '../../../empty.util';
|
||||
import { AuthService } from '../../../../core/auth/auth.service';
|
||||
import { HardRedirectService } from '../../../../core/services/hard-redirect.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-log-in-shibboleth',
|
||||
@@ -51,12 +53,16 @@ export class LogInShibbolethComponent implements OnInit {
|
||||
* @param {AuthMethod} injectedAuthMethodModel
|
||||
* @param {NativeWindowRef} _window
|
||||
* @param {RouteService} route
|
||||
* @param {AuthService} authService
|
||||
* @param {HardRedirectService} hardRedirectService
|
||||
* @param {Store<State>} store
|
||||
*/
|
||||
constructor(
|
||||
@Inject('authMethodProvider') public injectedAuthMethodModel: AuthMethod,
|
||||
@Inject(NativeWindowService) protected _window: NativeWindowRef,
|
||||
private route: RouteService,
|
||||
private authService: AuthService,
|
||||
private hardRedirectService: HardRedirectService,
|
||||
private store: Store<CoreState>
|
||||
) {
|
||||
this.authMethod = injectedAuthMethodModel;
|
||||
@@ -75,6 +81,7 @@ export class LogInShibbolethComponent implements OnInit {
|
||||
}
|
||||
|
||||
redirectToShibboleth() {
|
||||
this.authService.setRedirectUrlIfNotSet(this.hardRedirectService.getCurrentRoute())
|
||||
let newLocationUrl = this.location;
|
||||
const currentUrl = this._window.nativeWindow.location.href;
|
||||
const myRegexp = /\?redirectUrl=(.*)/g;
|
||||
|
@@ -154,4 +154,12 @@ export class AuthServiceStub {
|
||||
resetAuthenticationError() {
|
||||
return;
|
||||
}
|
||||
|
||||
setRedirectUrlIfNotSet(url: string) {
|
||||
return;
|
||||
}
|
||||
|
||||
redirectAfterLoginSuccess() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -40,7 +40,6 @@ export function locationProvider(): Location {
|
||||
return window.location;
|
||||
}
|
||||
|
||||
|
||||
@NgModule({
|
||||
bootstrap: [AppComponent],
|
||||
imports: [
|
||||
|
Reference in New Issue
Block a user