From 0fa1e17078f705ed84418d1e0043bd3474c79605 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 13 Jul 2020 15:47:59 +0200 Subject: [PATCH] 71809: ForwardClientIpInterceptor --- src/app/core/auth/server-auth.service.ts | 3 -- .../forward-client-ip.interceptor.spec.ts | 44 +++++++++++++++++++ .../forward-client-ip.interceptor.ts | 23 ++++++++++ src/modules/app/server-app.module.ts | 10 ++++- 4 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/app/core/forward-client-ip/forward-client-ip.interceptor.spec.ts create mode 100644 src/app/core/forward-client-ip/forward-client-ip.interceptor.ts diff --git a/src/app/core/auth/server-auth.service.ts b/src/app/core/auth/server-auth.service.ts index 30767be85a..7b78255001 100644 --- a/src/app/core/auth/server-auth.service.ts +++ b/src/app/core/auth/server-auth.service.ts @@ -27,9 +27,6 @@ export class ServerAuthService extends AuthService { headers = headers.append('Accept', 'application/json'); headers = headers.append('Authorization', `Bearer ${token.accessToken}`); - // NB this is used to pass server client IP check. - const clientIp = this.req.get('x-forwarded-for') || this.req.connection.remoteAddress; - headers = headers.append('X-Forwarded-For', clientIp); options.headers = headers; return this.authRequestService.getRequest('status', options).pipe( diff --git a/src/app/core/forward-client-ip/forward-client-ip.interceptor.spec.ts b/src/app/core/forward-client-ip/forward-client-ip.interceptor.spec.ts new file mode 100644 index 0000000000..49acd5b46d --- /dev/null +++ b/src/app/core/forward-client-ip/forward-client-ip.interceptor.spec.ts @@ -0,0 +1,44 @@ +import { ForwardClientIpInterceptor } from './forward-client-ip.interceptor'; +import { DSpaceRESTv2Service } from '../dspace-rest-v2/dspace-rest-v2.service'; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { HTTP_INTERCEPTORS, HttpRequest } from '@angular/common/http'; +import { REQUEST } from '@nguniversal/express-engine/tokens'; + +describe('ForwardClientIpInterceptor', () => { + let service: DSpaceRESTv2Service; + let httpMock: HttpTestingController; + + let requestUrl; + let clientIp; + + beforeEach(() => { + requestUrl = 'test-url'; + clientIp = '1.2.3.4'; + + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + DSpaceRESTv2Service, + { + provide: HTTP_INTERCEPTORS, + useClass: ForwardClientIpInterceptor, + multi: true, + }, + { provide: REQUEST, useValue: { get: () => undefined, connection: { remoteAddress: clientIp } }} + ], + }); + + service = TestBed.get(DSpaceRESTv2Service); + httpMock = TestBed.get(HttpTestingController); + }); + + it('should add an X-Forwarded-For header matching the client\'s IP', () => { + service.get(requestUrl).subscribe((response) => { + expect(response).toBeTruthy(); + }); + + const httpRequest = httpMock.expectOne(requestUrl); + expect(httpRequest.request.headers.get('X-Forwarded-For')).toEqual(clientIp); + }); +}); diff --git a/src/app/core/forward-client-ip/forward-client-ip.interceptor.ts b/src/app/core/forward-client-ip/forward-client-ip.interceptor.ts new file mode 100644 index 0000000000..2e1be09c96 --- /dev/null +++ b/src/app/core/forward-client-ip/forward-client-ip.interceptor.ts @@ -0,0 +1,23 @@ +import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { Inject, Injectable } from '@angular/core'; +import { Observable } from 'rxjs/internal/Observable'; +import { REQUEST } from '@nguniversal/express-engine/tokens'; + +@Injectable() +/** + * Http Interceptor intercepting Http Requests, adding the client's IP to their X-Forwarded-For header + */ +export class ForwardClientIpInterceptor implements HttpInterceptor { + constructor(@Inject(REQUEST) protected req: any) { + } + + /** + * Intercept http requests and add the client's IP to the X-Forwarded-For header + * @param httpRequest + * @param next + */ + intercept(httpRequest: HttpRequest, next: HttpHandler): Observable> { + const clientIp = this.req.get('x-forwarded-for') || this.req.connection.remoteAddress; + return next.handle(httpRequest.clone({ setHeaders: { 'X-Forwarded-For': clientIp } })); + } +} diff --git a/src/modules/app/server-app.module.ts b/src/modules/app/server-app.module.ts index 5abd8e3aa1..d46a013b3b 100644 --- a/src/modules/app/server-app.module.ts +++ b/src/modules/app/server-app.module.ts @@ -25,6 +25,8 @@ import { ServerSubmissionService } from '../../app/submission/server-submission. import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider'; import { Angulartics2RouterlessModule } from 'angulartics2/routerlessmodule'; import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { ForwardClientIpInterceptor } from '../../app/core/forward-client-ip/forward-client-ip.interceptor'; export function createTranslateLoader() { return new TranslateJson5UniversalLoader('dist/server/assets/i18n/', '.json5'); @@ -73,7 +75,13 @@ export function createTranslateLoader() { { provide: SubmissionService, useClass: ServerSubmissionService - } + }, + // register ForwardClientIpInterceptor as HttpInterceptor + { + provide: HTTP_INTERCEPTORS, + useClass: ForwardClientIpInterceptor, + multi: true + }, ] }) export class ServerAppModule {