Commit c498fdf5 authored by Ryan Diehl's avatar Ryan Diehl

feat(security): implement logout for auth service

parent 477253eb
Pipeline #109847 passed with stages
in 3 minutes and 43 seconds
......@@ -6,3 +6,4 @@ const PREFIX = '@psu/utils/security';
export const login = createAction(`${PREFIX} Login`, props<{ targetUrl: string }>());
export const loginSuccessful = createAction(`${PREFIX} Login Successful`, props<{ user: User }>());
export const loginFailure = createAction(`${PREFIX} Login Failure`, props<{ message?: string }>());
export const logout = createAction(`${PREFIX} Logout`);
......@@ -4,7 +4,7 @@ import { Action } from '@ngrx/store';
import { AuthService } from '@psu/utils/security';
import { ReplaySubject } from 'rxjs';
import { Mock } from 'ts-mocks';
import { login } from './security.actions';
import { login, logout } from './security.actions';
import { SecurityEffects } from './security.effects';
describe('SecurityEffects', () => {
......@@ -15,7 +15,8 @@ describe('SecurityEffects', () => {
beforeEach(() => {
actions$ = new ReplaySubject(1);
authService = new Mock<AuthService>({
login: () => {}
login: () => {},
logout: () => {}
});
TestBed.configureTestingModule({
providers: [SecurityEffects, provideMockActions(actions$), { provide: AuthService, useValue: authService.Object }]
......@@ -35,4 +36,12 @@ describe('SecurityEffects', () => {
expect(result).toEqual({ type: 'NO_ACTION' });
expect(authService.Object.login).toHaveBeenCalledWith('/authed');
});
it('logout action should call auth service logout method', () => {
let result: Action;
effects.logout$.subscribe(r => (result = r));
actions$.next(logout());
expect(result).toEqual({ type: 'NO_ACTION' });
expect(authService.Object.logout).toHaveBeenCalled();
});
});
......@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { AuthService } from '@psu/utils/security';
import { map, tap } from 'rxjs/operators';
import { login } from './security.actions';
import { login, logout } from './security.actions';
@Injectable({ providedIn: 'root' })
export class SecurityEffects {
......@@ -17,5 +17,13 @@ export class SecurityEffects {
{ dispatch: false }
);
public logout$ = createEffect(() =>
this.actions$.pipe(
ofType(logout),
tap(() => this.authService.logout()),
map(() => ({ type: 'NO_ACTION' }))
)
);
constructor(private actions$: Actions, private authService: AuthService) {}
}
......@@ -5,7 +5,7 @@ import { User } from '@psu/utils/security';
import { Subject } from 'rxjs';
import { Mock } from 'ts-mocks';
import { SecurityEventListener } from './security-event.listener';
import { login, loginFailure, loginSuccessful } from './security.actions';
import { login, loginFailure, loginSuccessful, logout } from './security.actions';
import { SecurityFacade } from './security.facade';
import { initialSecurityState, SecurityPartialState, securitySliceName } from './security.reducer';
......@@ -61,4 +61,9 @@ describe('SecurityFacade', () => {
loginFailed$.next();
expect(store.dispatch).toHaveBeenCalledWith(loginFailure({}));
});
it('logout should dispatch logout action', () => {
facade.logout();
expect(store.dispatch).toHaveBeenCalledWith(logout());
});
});
......@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { User } from '@psu/utils/security';
import { SecurityEventListener } from './security-event.listener';
import { login, loginFailure, loginSuccessful } from './security.actions';
import { login, loginFailure, loginSuccessful, logout } from './security.actions';
import { SecurityPartialState } from './security.reducer';
import {
selectSecurityCurrentUserName,
......@@ -32,4 +32,8 @@ export class SecurityFacade {
public loginFailure(message?: string): void {
this.store.dispatch(loginFailure({ message }));
}
public logout(): void {
this.store.dispatch(logout());
}
}
import { Injectable } from '@angular/core';
import { AuthService } from './auth.service';
@Injectable()
class MockAuthService extends AuthService {
getUserName = () => 'mock';
}
@Injectable()
class MockAnonymousService extends AuthService {
getUserName = () => undefined;
}
describe('AuthService', () => {
let service: MockAuthService;
let anonymousService: MockAnonymousService;
beforeEach(() => {
service = new MockAuthService();
anonymousService = new MockAnonymousService();
});
it('should create', () => {
expect(service).toBeDefined();
expect(anonymousService).toBeDefined();
});
it('should return mock username', () => {
expect(service.getUserName()).toEqual('mock');
expect(anonymousService.getUserName()).toBeUndefined();
});
it('isLoggedIn should be true', () => {
expect(service.isLoggedIn()).toEqual(true);
});
it('anonymous service isLoggedIn should be false', () => {
expect(anonymousService.isLoggedIn()).toEqual(false);
});
});
......@@ -17,10 +17,13 @@ export abstract class AuthService {
*/
login: (targetUrl: string) => any;
/**
* Trigger a logout request to the auth provider.
*/
logout: () => any;
/**
* Determine if there is a logged in user.
*/
public isLoggedIn(): boolean {
return !!this.getUserName();
}
isLoggedIn: () => boolean;
}
  • SonarQube analysis indicates that quality gate is failed.

    • Bugs is failed: Actual value 1 > 0
    • high_severity_vulns is passed: Actual value 0
    • medium_severity_vulns is passed: Actual value 0

    SonarQube analysis reported 17 issues

    • 4 major
    • 🔽 7 minor
    • 6 info

    Top 10 extra issues

    Note: The following issues were found on lines that were not modified in the commit. Because these issues can't be reported as line comments, they are summarized here:

    1. Remove this useless assignment to variable "result". 📘
    2. Remove this useless assignment to variable "complete". 📘
    3. Remove this useless assignment to variable "loadResult". 📘
    4. Remove this useless assignment to variable "loadComplete". 📘
    5. 🔽 Add an "alt" attribute to this image. 📘
    6. 🔽 This assertion is unnecessary since it does not change the type of the expression. 📘
    7. 🔽 This assertion is unnecessary since it does not change the type of the expression. 📘
    8. 🔽 This assertion is unnecessary since it does not change the type of the expression. 📘
    9. 🔽 This assertion is unnecessary since it does not change the type of the expression. 📘
    10. 🔽 This assertion is unnecessary since it does not change the type of the expression. 📘
    • ... 7 more
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment