Merge pull request #682 from 4Science/authentication_issue_CSR

Authentication issue csr
This commit is contained in:
Tim Donohue
2020-05-28 10:54:22 -05:00
committed by GitHub
4 changed files with 73 additions and 21 deletions

View File

@@ -1,9 +1,9 @@
import { TestBed } from '@angular/core/testing'; import { fakeAsync, flush, TestBed } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing'; import { provideMockActions } from '@ngrx/effects/testing';
import { Store } from '@ngrx/store'; import { Store, StoreModule } from '@ngrx/store';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { cold, hot } from 'jasmine-marbles'; import { cold, hot } from 'jasmine-marbles';
import { Observable, of as observableOf, throwError as observableThrow } from 'rxjs'; import { Observable, of as observableOf, throwError as observableThrow } from 'rxjs';
import { AuthEffects } from './auth.effects'; import { AuthEffects } from './auth.effects';
@@ -29,41 +29,53 @@ import {
} from './auth.actions'; } from './auth.actions';
import { authMethodsMock, AuthServiceStub } from '../../shared/testing/auth-service.stub'; import { authMethodsMock, AuthServiceStub } from '../../shared/testing/auth-service.stub';
import { AuthService } from './auth.service'; import { AuthService } from './auth.service';
import { AuthState } from './auth.reducer'; import { authReducer } from './auth.reducer';
import { AuthStatus } from './models/auth-status.model'; import { AuthStatus } from './models/auth-status.model';
import { EPersonMock } from '../../shared/testing/eperson.mock'; import { EPersonMock } from '../../shared/testing/eperson.mock';
import { AppState, storeModuleConfig } from '../../app.reducer';
import { StoreActionTypes } from '../../store.actions';
import { isAuthenticated, isAuthenticatedLoaded } from './selectors';
describe('AuthEffects', () => { describe('AuthEffects', () => {
let authEffects: AuthEffects; let authEffects: AuthEffects;
let actions: Observable<any>; let actions: Observable<any>;
let authServiceStub; let authServiceStub;
const store: Store<AuthState> = jasmine.createSpyObj('store', { let initialState;
/* tslint:disable:no-empty */
dispatch: {},
/* tslint:enable:no-empty */
select: observableOf(true)
});
let token; let token;
let store: MockStore<AppState>;
function init() { function init() {
authServiceStub = new AuthServiceStub(); authServiceStub = new AuthServiceStub();
token = authServiceStub.getToken(); token = authServiceStub.getToken();
initialState = {
core: {
auth: {
authenticated: false,
loaded: false,
loading: false,
authMethods: []
}
}
};
} }
beforeEach(() => { beforeEach(() => {
init(); init();
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({ auth: authReducer }, storeModuleConfig)
],
providers: [ providers: [
AuthEffects, AuthEffects,
provideMockStore({ initialState }),
{ provide: AuthService, useValue: authServiceStub }, { provide: AuthService, useValue: authServiceStub },
{ provide: Store, useValue: store },
provideMockActions(() => actions), provideMockActions(() => actions),
// other providers // other providers
], ],
}); });
authEffects = TestBed.get(AuthEffects); authEffects = TestBed.get(AuthEffects);
store = TestBed.get(Store);
}); });
describe('authenticate$', () => { describe('authenticate$', () => {
@@ -362,4 +374,40 @@ describe('AuthEffects', () => {
}); });
}) })
}); });
describe('clearInvalidTokenOnRehydrate$', () => {
beforeEach(() => {
store.overrideSelector(isAuthenticated, false);
});
describe('when auth loaded is false', () => {
it('should not call removeToken method', (done) => {
store.overrideSelector(isAuthenticatedLoaded, false);
actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } });
spyOn(authServiceStub, 'removeToken');
authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => {
expect(authServiceStub.removeToken).not.toHaveBeenCalled();
});
done();
});
});
describe('when auth loaded is true', () => {
it('should call removeToken method', fakeAsync(() => {
store.overrideSelector(isAuthenticatedLoaded, true);
actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } });
spyOn(authServiceStub, 'removeToken');
authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => {
expect(authServiceStub.removeToken).toHaveBeenCalled();
flush();
});
}));
});
});
}); });

View File

@@ -1,20 +1,18 @@
import { Observable, of as observableOf } from 'rxjs';
import { catchError, debounceTime, filter, map, switchMap, take, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
import { catchError, debounceTime, filter, map, switchMap, take, tap } from 'rxjs/operators';
// import @ngrx // import @ngrx
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, Effect, ofType } from '@ngrx/effects';
import { Action, select, Store } from '@ngrx/store'; import { Action, select, Store } from '@ngrx/store';
// import services // import services
import { AuthService } from './auth.service'; import { AuthService } from './auth.service';
import { EPerson } from '../eperson/models/eperson.model'; import { EPerson } from '../eperson/models/eperson.model';
import { AuthStatus } from './models/auth-status.model'; import { AuthStatus } from './models/auth-status.model';
import { AuthTokenInfo } from './models/auth-token-info.model'; import { AuthTokenInfo } from './models/auth-token-info.model';
import { AppState } from '../../app.reducer'; import { AppState } from '../../app.reducer';
import { isAuthenticated } from './selectors'; import { isAuthenticated, isAuthenticatedLoaded } from './selectors';
import { StoreActionTypes } from '../../store.actions'; import { StoreActionTypes } from '../../store.actions';
import { AuthMethod } from './models/auth.method'; import { AuthMethod } from './models/auth.method';
// import actions // import actions
@@ -187,10 +185,11 @@ export class AuthEffects {
public clearInvalidTokenOnRehydrate$: Observable<any> = this.actions$.pipe( public clearInvalidTokenOnRehydrate$: Observable<any> = this.actions$.pipe(
ofType(StoreActionTypes.REHYDRATE), ofType(StoreActionTypes.REHYDRATE),
switchMap(() => { switchMap(() => {
return this.store.pipe( const isLoaded$ = this.store.pipe(select(isAuthenticatedLoaded));
select(isAuthenticated), const authenticated$ = this.store.pipe(select(isAuthenticated));
return observableCombineLatest(isLoaded$, authenticated$).pipe(
take(1), take(1),
filter((authenticated) => !authenticated), filter(([loaded, authenticated]) => loaded && !authenticated),
tap(() => this.authService.removeToken()), tap(() => this.authService.removeToken()),
tap(() => this.authService.resetAuthenticationError()) tap(() => this.authService.resetAuthenticationError())
); );

View File

@@ -1,4 +1,5 @@
<form (ngSubmit)="submit()" <form class="form-login"
(ngSubmit)="submit()"
[formGroup]="form" novalidate> [formGroup]="form" novalidate>
<label for="inputEmail" class="sr-only">{{"login.form.email" | translate}}</label> <label for="inputEmail" class="sr-only">{{"login.form.email" | translate}}</label>
<input id="inputEmail" <input id="inputEmail"

View File

@@ -150,4 +150,8 @@ export class AuthServiceStub {
getImpersonateID() { getImpersonateID() {
return this.impersonating; return this.impersonating;
} }
resetAuthenticationError() {
return;
}
} }