From 75d641dc8202b61da39c7d6950e547e1d24a7219 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 25 Jun 2021 16:06:07 +0200 Subject: [PATCH 1/2] Create a new log interceptor with the scope to attach new X-CORRELATION-ID and X-REFERRER headers --- src/app/app.module.ts | 27 ++++++++- src/app/core/log/log.interceptor.spec.ts | 76 ++++++++++++++++++++++++ src/app/core/log/log.interceptor.ts | 33 ++++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 src/app/core/log/log.interceptor.spec.ts create mode 100644 src/app/core/log/log.interceptor.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 3d45ffbfc2..b5feffe03a 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,5 +1,5 @@ import { APP_BASE_HREF, CommonModule } from '@angular/common'; -import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; +import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { APP_INITIALIZER, NgModule } from '@angular/core'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; @@ -38,6 +38,7 @@ import { ForbiddenComponent } from './forbidden/forbidden.component'; import { AuthInterceptor } from './core/auth/auth.interceptor'; import { LocaleInterceptor } from './core/locale/locale.interceptor'; import { XsrfInterceptor } from './core/xsrf/xsrf.interceptor'; +import { LogInterceptor } from './core/log/log.interceptor'; import { RootComponent } from './root/root.component'; import { ThemedRootComponent } from './root/themed-root.component'; import { ThemedEntryComponentModule } from '../themes/themed-entry-component.module'; @@ -48,6 +49,9 @@ import { ThemedFooterComponent } from './footer/themed-footer.component'; import { ThemedBreadcrumbsComponent } from './breadcrumbs/themed-breadcrumbs.component'; import { ThemedHeaderNavbarWrapperComponent } from './header-nav-wrapper/themed-header-navbar-wrapper.component'; +import { UUIDService } from './core/shared/uuid.service'; +import { CookieService } from './core/services/cookie.service'; + export function getBase() { return environment.ui.nameSpace; } @@ -120,6 +124,27 @@ const PROVIDERS = [ useClass: XsrfInterceptor, multi: true }, + // register LogInterceptor as HttpInterceptor + { + provide: HTTP_INTERCEPTORS, + useClass: LogInterceptor, + multi: true + }, + // insert the unique id of the user that is using the application utilizing cookies + { + provide: APP_INITIALIZER, + useFactory: (cookieService: CookieService, uuidService: UUIDService) => { + const correlationId = cookieService.get('CORRELATION-ID'); + + // Check if cookie exists, if don't, set it with unique id + if (!correlationId) { + cookieService.set('CORRELATION-ID', uuidService.generate()); + } + return () => true; + }, + multi: true, + deps: [ CookieService, UUIDService ] + }, ...DYNAMIC_MATCHER_PROVIDERS, ]; diff --git a/src/app/core/log/log.interceptor.spec.ts b/src/app/core/log/log.interceptor.spec.ts new file mode 100644 index 0000000000..9bda4b7934 --- /dev/null +++ b/src/app/core/log/log.interceptor.spec.ts @@ -0,0 +1,76 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { Router } from '@angular/router'; + +import { LogInterceptor } from './log.interceptor'; +import { DspaceRestService } from '../dspace-rest/dspace-rest.service'; +import { RestRequestMethod } from '../data/rest-request-method'; +import { CookieService } from '../services/cookie.service'; +import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock'; +import { RouterStub } from '../../shared/testing/router.stub'; + + +describe('LogInterceptor', () => { + let service: DspaceRestService; + let httpMock: HttpTestingController; + let cookieService: CookieService; + const router = Object.assign(new RouterStub(),{url : '/statistics'}); + + // Mock payload/statuses are dummy content as we are not testing the results + // of any below requests. We are only testing for X-XSRF-TOKEN header. + const mockPayload = { + id: 1 + }; + const mockStatusCode = 200; + const mockStatusText = 'SUCCESS'; + + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + DspaceRestService, + // LogInterceptor, + { + provide: HTTP_INTERCEPTORS, + useClass: LogInterceptor, + multi: true, + }, + { provide: CookieService, useValue: new CookieServiceMock() }, + { provide: Router, useValue: router }, + ], + }); + + service = TestBed.get(DspaceRestService); + httpMock = TestBed.get(HttpTestingController); + cookieService = TestBed.get(CookieService); + + cookieService.set('CORRELATION-ID','123455'); + }); + + + it('headers should be set', (done) => { + service.request(RestRequestMethod.POST, 'server/api/core/items', 'test', { withCredentials: false }).subscribe((response) => { + expect(response).toBeTruthy(); + done(); + }); + + const httpRequest = httpMock.expectOne('server/api/core/items'); + httpRequest.flush(mockPayload, { status: mockStatusCode, statusText: mockStatusText }); + expect(httpRequest.request.headers.has('X-CORRELATION-ID')).toBeTrue(); + expect(httpRequest.request.headers.has('X-REFERRER')).toBeTrue(); + }); + + it('headers should have the right values', (done) => { + service.request(RestRequestMethod.POST, 'server/api/core/items', 'test', { withCredentials: false }).subscribe((response) => { + expect(response).toBeTruthy(); + done(); + }); + + const httpRequest = httpMock.expectOne('server/api/core/items'); + httpRequest.flush(mockPayload, { status: mockStatusCode, statusText: mockStatusText }); + expect(httpRequest.request.headers.get('X-CORRELATION-ID')).toEqual('123455'); + expect(httpRequest.request.headers.get('X-REFERRER')).toEqual('/statistics'); + }); +}); diff --git a/src/app/core/log/log.interceptor.ts b/src/app/core/log/log.interceptor.ts new file mode 100644 index 0000000000..0f97ea0b38 --- /dev/null +++ b/src/app/core/log/log.interceptor.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; + +import { Router } from '@angular/router'; + +import { Observable } from 'rxjs'; +import { CookieService } from '../services/cookie.service'; + +/** + * Log Interceptor intercepting Http Requests & Responses to + * exchange add headers of the user using the application utilizing unique id in cookies. + * Add header for users current page path. + */ +@Injectable() +export class LogInterceptor implements HttpInterceptor { + + constructor(private cookieService: CookieService, private router: Router) {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + + // Get Unique id of the user from the cookies + const correlationId = this.cookieService.get('CORRELATION-ID'); + + // Add headers from the intercepted request + let headers = request.headers; + headers = headers.set('X-CORRELATION-ID', correlationId); + headers = headers.set('X-REFERRER', this.router.url); + + // Add new headers to the intercepted request + request = request.clone({ withCredentials: true, headers: headers }); + return next.handle(request); + } +} From 2bd68b0585666d38594108a79ea27c9e3febbfa8 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 8 Jul 2021 17:06:02 +0200 Subject: [PATCH 2/2] fix issue with ssr --- src/app/core/log/log.interceptor.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/core/log/log.interceptor.ts b/src/app/core/log/log.interceptor.ts index 0f97ea0b38..773a60b6ef 100644 --- a/src/app/core/log/log.interceptor.ts +++ b/src/app/core/log/log.interceptor.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; - import { Router } from '@angular/router'; import { Observable } from 'rxjs'; + import { CookieService } from '../services/cookie.service'; /** @@ -16,15 +16,15 @@ export class LogInterceptor implements HttpInterceptor { constructor(private cookieService: CookieService, private router: Router) {} - intercept(request: HttpRequest, next: HttpHandler): Observable> { + intercept(request: HttpRequest, next: HttpHandler): Observable> { // Get Unique id of the user from the cookies const correlationId = this.cookieService.get('CORRELATION-ID'); // Add headers from the intercepted request let headers = request.headers; - headers = headers.set('X-CORRELATION-ID', correlationId); - headers = headers.set('X-REFERRER', this.router.url); + headers = headers.append('X-CORRELATION-ID', correlationId); + headers = headers.append('X-REFERRER', this.router.url); // Add new headers to the intercepted request request = request.clone({ withCredentials: true, headers: headers });