Skip to content
Snippets Groups Projects
Commit 9d5fa6a3 authored by Tifenn Guillas's avatar Tifenn Guillas
Browse files

Add comments and fix tests

parent 0fd4ad19
No related branches found
No related tags found
2 merge requests!169Develop,!164Resolve "Fix SQ issues"
...@@ -8,7 +8,7 @@ module.exports = function (config) { ...@@ -8,7 +8,7 @@ module.exports = function (config) {
plugins: [ plugins: [
require('karma-jasmine'), require('karma-jasmine'),
require('karma-chrome-launcher'), require('karma-chrome-launcher'),
require('karma-spec-reporter'), // require('karma-spec-reporter'),
require('karma-jasmine-html-reporter'), require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'), require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma') require('@angular-devkit/build-angular/plugins/karma')
...@@ -21,7 +21,11 @@ module.exports = function (config) { ...@@ -21,7 +21,11 @@ module.exports = function (config) {
reports: ['html', 'lcovonly', 'text-summary'], reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true fixWebpackSourcePaths: true
}, },
reporters: ['progress', 'kjhtml', 'spec'], reporters: [
'progress',
'kjhtml',
// 'spec'
],
port: 9876, port: 9876,
colors: true, colors: true,
logLevel: config.LOG_INFO, logLevel: config.LOG_INFO,
......
import * as authActions from './auth.action'; import * as authActions from './auth.action';
describe('[Auth] Action', () => { describe('[Auth] Action', () => {
it('should create CheckAuthAction', () => {
const action = new authActions.CheckAuthAction();
expect(action.type).toEqual(authActions.CHECK_AUTH);
});
it('should create CheckAuthCompleteAction', () => {
const action = new authActions.CheckAuthCompleteAction(true);
expect(action.type).toEqual(authActions.CHECK_AUTH_COMPLETE);
expect(action.payload).toBeTruthy();
});
it('should create LoginAction', () => { it('should create LoginAction', () => {
const action = new authActions.LoginAction(); const action = new authActions.LoginAction();
expect(action.type).toEqual(authActions.LOGIN); expect(action.type).toEqual(authActions.LOGIN);
}); });
it('should create LoginCompleteAction', () => {
const action = new authActions.LoginCompleteAction('toto');
expect(action.type).toEqual(authActions.LOGIN_COMPLETE);
expect(action.payload).toEqual('toto');
});
it('should create LogoutAction', () => { it('should create LogoutAction', () => {
const action = new authActions.LogoutAction(); const action = new authActions.LogoutAction();
expect(action.type).toEqual(authActions.LOGOUT); expect(action.type).toEqual(authActions.LOGOUT);
}); });
it('should create OpenEditProfileAction', () => {
const action = new authActions.OpenEditProfileAction();
expect(action.type).toEqual(authActions.OPEN_EDIT_PROFILE);
});
it('should create ParseJwtAction', () => {
const action = new authActions.ParseJwtAction('toto');
expect(action.type).toEqual(authActions.PARSE_JWT);
expect(action.payload).toEqual('toto');
});
it('should create LoadUserRolesAction', () => {
const action = new authActions.LoadUserRolesAction(['toto']);
expect(action.type).toEqual(authActions.LOAD_USER_ROLES);
expect(action.payload).toEqual(['toto']);
});
}); });
...@@ -9,22 +9,31 @@ ...@@ -9,22 +9,31 @@
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
export const CHECK_AUTH = '[Auth] check Auth'; export const CHECK_AUTH = '[Auth] Check Auth';
export const CHECK_AUTH_COMPLETE = '[Auth] Check Auth Complete'; export const CHECK_AUTH_COMPLETE = '[Auth] Check Auth Complete';
export const LOGIN = '[Auth] Login'; export const LOGIN = '[Auth] Login';
export const LOGIN_COMPLETE = '[Auth] Login Complete'; export const LOGIN_COMPLETE = '[Auth] Login Complete';
export const LOGOUT = '[Auth] Logout'; export const LOGOUT = '[Auth] Logout';
export const OPEN_EDIT_PROFILE = '[Auth] Edit Profile'; export const OPEN_EDIT_PROFILE = '[Auth] Edit Profile';
export const GET_USER_ROLES = '[Auth] Get User Roles';
export const PARSE_JWT = '[Auth] Parse JWT'; export const PARSE_JWT = '[Auth] Parse JWT';
export const LOAD_USER_ROLES = '[Auth] Load User Roles'; export const LOAD_USER_ROLES = '[Auth] Load User Roles';
/**
* @class
* @classdesc CheckAuthAction action.
* @readonly
*/
export class CheckAuthAction implements Action { export class CheckAuthAction implements Action {
readonly type = CHECK_AUTH; readonly type = CHECK_AUTH;
constructor(public payload: {} = null) { } constructor(public payload: {} = null) { }
} }
/**
* @class
* @classdesc CheckAuthCompleteAction action.
* @readonly
*/
export class CheckAuthCompleteAction implements Action { export class CheckAuthCompleteAction implements Action {
readonly type = CHECK_AUTH_COMPLETE; readonly type = CHECK_AUTH_COMPLETE;
...@@ -42,13 +51,17 @@ export class LoginAction implements Action { ...@@ -42,13 +51,17 @@ export class LoginAction implements Action {
constructor(public payload: string = null) { } constructor(public payload: string = null) { }
} }
/**
* @class
* @classdesc LoginCompleteAction action.
* @readonly
*/
export class LoginCompleteAction implements Action { export class LoginCompleteAction implements Action {
readonly type = LOGIN_COMPLETE; readonly type = LOGIN_COMPLETE;
constructor(public payload: any) { } constructor(public payload: any) { }
} }
/** /**
* @class * @class
* @classdesc LogoutAction action. * @classdesc LogoutAction action.
...@@ -60,18 +73,33 @@ export class LogoutAction implements Action { ...@@ -60,18 +73,33 @@ export class LogoutAction implements Action {
constructor(public payload: {} = null) { } constructor(public payload: {} = null) { }
} }
/**
* @class
* @classdesc OpenEditProfileAction action.
* @readonly
*/
export class OpenEditProfileAction implements Action { export class OpenEditProfileAction implements Action {
readonly type = OPEN_EDIT_PROFILE; readonly type = OPEN_EDIT_PROFILE;
constructor(public payload: {} = null) { } constructor(public payload: {} = null) { }
} }
/**
* @class
* @classdesc ParseJwtAction action.
* @readonly
*/
export class ParseJwtAction implements Action { export class ParseJwtAction implements Action {
readonly type = PARSE_JWT; readonly type = PARSE_JWT;
constructor(public payload: string) { } constructor(public payload: string) { }
} }
/**
* @class
* @classdesc LoadUserRolesAction action.
* @readonly
*/
export class LoadUserRolesAction implements Action { export class LoadUserRolesAction implements Action {
readonly type = LOAD_USER_ROLES; readonly type = LOAD_USER_ROLES;
......
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Effect, Actions, ofType } from '@ngrx/effects';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { switchMap, map, tap } from 'rxjs/operators'; import { switchMap, map, tap } from 'rxjs/operators';
import { Effect, Actions, ofType } from '@ngrx/effects';
import jwt_decode from 'jwt-decode'; import jwt_decode from 'jwt-decode';
import * as authActions from './auth.action'; import * as authActions from './auth.action';
...@@ -32,7 +32,7 @@ export class AuthEffects { ...@@ -32,7 +32,7 @@ export class AuthEffects {
) { } ) { }
/** /**
* Execute Login (OIDC Login) * Executes log in (OIDC Login).
*/ */
@Effect({ dispatch: false }) @Effect({ dispatch: false })
loginAction$ = this.actions$.pipe( loginAction$ = this.actions$.pipe(
...@@ -46,6 +46,9 @@ export class AuthEffects { ...@@ -46,6 +46,9 @@ export class AuthEffects {
}) })
); );
/**
* Checks if user is authenticated.
*/
@Effect() @Effect()
checkAuthAction$ = this.actions$.pipe( checkAuthAction$ = this.actions$.pipe(
ofType(authActions.CHECK_AUTH), ofType(authActions.CHECK_AUTH),
...@@ -58,12 +61,15 @@ export class AuthEffects { ...@@ -58,12 +61,15 @@ export class AuthEffects {
) )
); );
/**
* Completes authentication if not complete.
*/
@Effect() @Effect()
checkAuthCompleteAction$ = this.actions$.pipe( checkAuthCompleteAction$ = this.actions$.pipe(
ofType(authActions.CHECK_AUTH_COMPLETE), ofType(authActions.CHECK_AUTH_COMPLETE),
switchMap((action: authActions.CheckAuthCompleteAction) => { switchMap((action: authActions.CheckAuthCompleteAction) => {
const isAuthenticated = action.payload; const isAuthenticated = action.payload;
if (isAuthenticated) { if (isAuthenticated) {
return this.authService.userData.pipe( return this.authService.userData.pipe(
switchMap((profile) => [ switchMap((profile) => [
...@@ -77,6 +83,9 @@ export class AuthEffects { ...@@ -77,6 +83,9 @@ export class AuthEffects {
}) })
); );
/**
* Redirects user after login completes.
*/
@Effect({ dispatch: false }) @Effect({ dispatch: false })
loginCompleteAction$ = this.actions$.pipe( loginCompleteAction$ = this.actions$.pipe(
ofType(authActions.LOGIN_COMPLETE), ofType(authActions.LOGIN_COMPLETE),
...@@ -89,6 +98,9 @@ export class AuthEffects { ...@@ -89,6 +98,9 @@ export class AuthEffects {
}) })
); );
/**
* Parses token to get user roles.
*/
@Effect() @Effect()
parseJWTAction$ = this.actions$.pipe( parseJWTAction$ = this.actions$.pipe(
ofType(authActions.PARSE_JWT), ofType(authActions.PARSE_JWT),
...@@ -102,6 +114,9 @@ export class AuthEffects { ...@@ -102,6 +114,9 @@ export class AuthEffects {
}) })
); );
/**
* Executes log out.
*/
@Effect({ dispatch: false }) @Effect({ dispatch: false })
logoutAction$ = this.actions$.pipe( logoutAction$ = this.actions$.pipe(
ofType(authActions.LOGOUT), ofType(authActions.LOGOUT),
...@@ -112,7 +127,7 @@ export class AuthEffects { ...@@ -112,7 +127,7 @@ export class AuthEffects {
* Opens edit profile page. * Opens edit profile page.
*/ */
@Effect({ dispatch: false }) @Effect({ dispatch: false })
OpenEditProfileAction$ = this.actions$.pipe( openEditProfileAction$ = this.actions$.pipe(
ofType(authActions.OPEN_EDIT_PROFILE), ofType(authActions.OPEN_EDIT_PROFILE),
tap(_ => window.open(environment.ssoAuthUrl + '/account', '_blank')) tap(_ => window.open(environment.ssoAuthUrl + '/account', '_blank'))
); );
......
...@@ -10,6 +10,40 @@ describe('[Auth] Reducer', () => { ...@@ -10,6 +10,40 @@ describe('[Auth] Reducer', () => {
expect(state).toBe(initialState); expect(state).toBe(initialState);
}); });
it('should set isAuthenticated to true and set userProfile', () => {
const { initialState } = fromAuth;
const action = new authActions.LoginCompleteAction('toto');
const state = fromAuth.reducer(initialState, action);
expect(state.isAuthenticated).toBeTruthy();
expect(state.userProfile).toEqual('toto');
expect(state.userRoles.length).toEqual(0);
expect(state).not.toEqual(initialState);
});
it('should set userRoles', () => {
const { initialState } = fromAuth;
const action = new authActions.LoadUserRolesAction(['toto']);
const state = fromAuth.reducer(initialState, action);
expect(state.isAuthenticated).toBeFalsy();
expect(state.userProfile).toBeNull();
expect(state.userRoles.length).toEqual(1);
expect(state.userRoles[0]).toEqual('toto');
expect(state).not.toEqual(initialState);
});
it('should set isAuthenticated to false and unset userRoles', () => {
const initialState = { ...fromAuth.initialState, isAuthenticated: true, userProfile: 'toto' };
const action = new authActions.LogoutAction();
const state = fromAuth.reducer(initialState, action);
expect(state.isAuthenticated).toBeFalsy();
expect(state.userProfile).toBeNull();
expect(state.userRoles.length).toEqual(0);
expect(state).not.toEqual(initialState);
});
it('should get isAuthenticated', () => { it('should get isAuthenticated', () => {
const action = {} as authActions.Actions; const action = {} as authActions.Actions;
const state = fromAuth.reducer(undefined, action); const state = fromAuth.reducer(undefined, action);
...@@ -23,4 +57,11 @@ describe('[Auth] Reducer', () => { ...@@ -23,4 +57,11 @@ describe('[Auth] Reducer', () => {
expect(fromAuth.getUserProfile(state)).toBeNull(); expect(fromAuth.getUserProfile(state)).toBeNull();
}); });
it('should get userRoles', () => {
const action = {} as authActions.Actions;
const state = fromAuth.reducer(undefined, action);
expect(fromAuth.getUserRoles(state).length).toEqual(0);
});
}); });
...@@ -37,20 +37,16 @@ export const initialState: State = { ...@@ -37,20 +37,16 @@ export const initialState: State = {
export function reducer(state: State = initialState, action: actions.Actions): State { export function reducer(state: State = initialState, action: actions.Actions): State {
switch (action.type) { switch (action.type) {
case actions.LOGIN_COMPLETE: case actions.LOGIN_COMPLETE:
const userProfile = action.payload as any;
return { return {
...state, ...state,
isAuthenticated: true, isAuthenticated: true,
userProfile userProfile: action.payload
}; };
case actions.LOAD_USER_ROLES: case actions.LOAD_USER_ROLES:
const userRoles = action.payload;
return { return {
...state, ...state,
userRoles userRoles: action.payload
}; };
case actions.LOGOUT: case actions.LOGOUT:
......
...@@ -7,8 +7,18 @@ describe('[Auth] Selector', () => { ...@@ -7,8 +7,18 @@ describe('[Auth] Selector', () => {
expect(authSelector.isAuthenticated(state)).toBeFalsy(); expect(authSelector.isAuthenticated(state)).toBeFalsy();
}); });
it('should get isAuthenticated', () => {
const state = { auth: { ...fromAuth.initialState }};
expect(authSelector.isAuthenticated(state)).toBeFalsy();
});
it('should get userProfile', () => { it('should get userProfile', () => {
const state = { auth: { ...fromAuth.initialState }}; const state = { auth: { ...fromAuth.initialState }};
expect(authSelector.getUserProfile(state)).toBeNull(); expect(authSelector.getUserProfile(state)).toBeNull();
}); });
it('should get userRoles', () => {
const state = { auth: { ...fromAuth.initialState }};
expect(authSelector.getUserRoles(state).length).toEqual(0);
});
}); });
\ No newline at end of file
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { OidcSecurityService } from 'angular-auth-oidc-client'; import { OidcSecurityService } from 'angular-auth-oidc-client';
import { environment } from 'src/environments/environment';
import { environment } from '../../environments/environment';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
/**
* @class
* @classdesc Authentication service.
*/
export class AuthService { export class AuthService {
constructor(private oidcSecurityService: OidcSecurityService) { } constructor(private oidcSecurityService: OidcSecurityService) { }
get isLoggedIn() { /**
return this.oidcSecurityService.isAuthenticated$; * Gets user data.
} *
* @return Observable<any>
*/
get token() { get token() {
if (environment.ssoName === 'auth0') { if (environment.ssoName === 'auth0') {
return this.oidcSecurityService.getIdToken(); return this.oidcSecurityService.getIdToken();
...@@ -20,19 +35,37 @@ export class AuthService { ...@@ -20,19 +35,37 @@ export class AuthService {
} }
} }
get userData() { /**
* Gets user data.
*
* @return Observable<any>
*/
get userData(): Observable<any> {
return this.oidcSecurityService.userData$; return this.oidcSecurityService.userData$;
} }
checkAuth() { /**
* Checks authentication.
*
* @return Observable<boolean>
*/
checkAuth(): Observable<boolean> {
return this.oidcSecurityService.checkAuthIncludingServer(); return this.oidcSecurityService.checkAuthIncludingServer();
} }
doLogin() { /**
* Logs in user.
*
* @return Observable<void>
*/
doLogin(): Observable<void> {
return of(this.oidcSecurityService.authorize()); return of(this.oidcSecurityService.authorize());
} }
signOut() { /**
* Logs out user.
*/
signOut(): void {
this.oidcSecurityService.logoffAndRevokeTokens(); this.oidcSecurityService.logoffAndRevokeTokens();
} }
} }
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { OidcConfigService, LogLevel } from 'angular-auth-oidc-client'; import { OidcConfigService, LogLevel } from 'angular-auth-oidc-client';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
export function configureAuth(oidcConfigService: OidcConfigService) { /**
return () => * Adds user token to the request.
oidcConfigService.withConfig({ *
stsServer: environment.ssoAuthUrl, * @param {OidcConfigService} oidcConfigService - The single sign on service.
redirectUrl: window.location.origin, *
postLogoutRedirectUri: window.location.origin, * @return any
clientId: environment.ssoClientId, */
scope: 'openid profile email offline_access', export function configureAuth(oidcConfigService: OidcConfigService): any {
responseType: 'code', return () => oidcConfigService.withConfig({
silentRenew: true, stsServer: environment.ssoAuthUrl,
silentRenewUrl: `${window.location.origin}/silent-renew.html`, redirectUrl: window.location.origin,
renewTimeBeforeTokenExpiresInSeconds: 10, postLogoutRedirectUri: window.location.origin,
logLevel: LogLevel.None, clientId: environment.ssoClientId,
}); scope: 'openid profile email offline_access',
responseType: 'code',
silentRenew: true,
silentRenewUrl: `${window.location.origin}/silent-renew.html`,
renewTimeBeforeTokenExpiresInSeconds: 10,
logLevel: LogLevel.None,
});
} }
/**
* This file is part of Anis Client.
*
* @copyright Laboratoire d'Astrophysique de Marseille / CNRS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
...@@ -16,9 +25,16 @@ import { environment } from '../../environments/environment'; ...@@ -16,9 +25,16 @@ import { environment } from '../../environments/environment';
export class TokenInterceptor implements HttpInterceptor { export class TokenInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) { } constructor(private authService: AuthService) { }
/**
* Adds user token to the request.
*
* @param {HttpRequest<any>} request - The outgoing request.
* @param {HttpHandler} next - The next interceptor.
*
* @return Observable<HttpEvent<any>>
*/
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let requestToForward = request; let requestToForward = request;
const token = this.authService.token; const token = this.authService.token;
const isApiUrl = request.url.startsWith(environment.apiUrl); const isApiUrl = request.url.startsWith(environment.apiUrl);
if (token !== '' && isApiUrl) { if (token !== '' && isApiUrl) {
...@@ -28,7 +44,6 @@ export class TokenInterceptor implements HttpInterceptor { ...@@ -28,7 +44,6 @@ export class TokenInterceptor implements HttpInterceptor {
} }
}); });
} }
return next.handle(requestToForward); return next.handle(requestToForward);
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment