diff --git a/src/app/core/log/log.interceptor.spec.ts b/src/app/core/log/log.interceptor.spec.ts index 021526f2e1..e784dc1f8c 100644 --- a/src/app/core/log/log.interceptor.spec.ts +++ b/src/app/core/log/log.interceptor.spec.ts @@ -10,12 +10,15 @@ import { import { TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; import { StoreModule } from '@ngrx/store'; +import { of } from 'rxjs'; import { appReducers, storeModuleConfig, } from '../../app.reducer'; import { CorrelationIdService } from '../../correlation-id/correlation-id.service'; +import { OrejimeService } from '../../shared/cookies/orejime.service'; +import { CORRELATION_ID_OREJIME_KEY } from '../../shared/cookies/orejime-configuration'; import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock'; import { RouterStub } from '../../shared/testing/router.stub'; import { RestRequestMethod } from '../data/rest-request-method'; @@ -40,6 +43,8 @@ describe('LogInterceptor', () => { const mockStatusCode = 200; const mockStatusText = 'SUCCESS'; + const mockOrejimeService = jasmine.createSpyObj('OrejimeService', ['getSavedPreferences']); + beforeEach(() => { TestBed.configureTestingModule({ @@ -56,6 +61,7 @@ describe('LogInterceptor', () => { { provide: Router, useValue: router }, { provide: CorrelationIdService, useClass: CorrelationIdService }, { provide: UUIDService, useClass: UUIDService }, + { provide: OrejimeService, useValue: mockOrejimeService }, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting(), ], @@ -71,7 +77,9 @@ describe('LogInterceptor', () => { }); - it('headers should be set', (done) => { + it('headers should be set when cookie is accepted', (done) => { + mockOrejimeService.getSavedPreferences.and.returnValue(of({ [CORRELATION_ID_OREJIME_KEY]: true })); + service.request(RestRequestMethod.POST, 'server/api/core/items', 'test', { withCredentials: false }).subscribe((response) => { expect(response).toBeTruthy(); done(); @@ -83,7 +91,23 @@ describe('LogInterceptor', () => { expect(httpRequest.request.headers.has('X-REFERRER')).toBeTrue(); }); - it('headers should have the right values', (done) => { + it('headers should not be set when cookie is declined', (done) => { + mockOrejimeService.getSavedPreferences.and.returnValue(of({ [CORRELATION_ID_OREJIME_KEY]: false })); + + 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')).toBeFalse(); + expect(httpRequest.request.headers.has('X-REFERRER')).toBeTrue(); + }); + + it('headers should have the right values when cookie is accepted', (done) => { + mockOrejimeService.getSavedPreferences.and.returnValue(of({ [CORRELATION_ID_OREJIME_KEY]: true })); + service.request(RestRequestMethod.POST, 'server/api/core/items', 'test', { withCredentials: false }).subscribe((response) => { expect(response).toBeTruthy(); done(); diff --git a/src/app/core/log/log.interceptor.ts b/src/app/core/log/log.interceptor.ts index bda408278f..76ca804cee 100644 --- a/src/app/core/log/log.interceptor.ts +++ b/src/app/core/log/log.interceptor.ts @@ -7,9 +7,15 @@ import { import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { Observable } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; import { CorrelationIdService } from '../../correlation-id/correlation-id.service'; -import { hasValue } from '../../shared/empty.util'; +import { OrejimeService } from '../../shared/cookies/orejime.service'; +import { CORRELATION_ID_OREJIME_KEY } from '../../shared/cookies/orejime-configuration'; +import { + hasValue, + isEmpty, +} from '../../shared/empty.util'; /** * Log Interceptor intercepting Http Requests & Responses to @@ -19,22 +25,37 @@ import { hasValue } from '../../shared/empty.util'; @Injectable() export class LogInterceptor implements HttpInterceptor { - constructor(private cidService: CorrelationIdService, private router: Router) {} + constructor( + private cidService: CorrelationIdService, + private router: Router, + private orejimeService: OrejimeService, + ) { + } intercept(request: HttpRequest, next: HttpHandler): Observable> { + return this.orejimeService.getSavedPreferences().pipe( + switchMap(preferences => { + // Check if the user has declined correlation id tracking + const correlationDeclined = + isEmpty(preferences) || + isEmpty(preferences[CORRELATION_ID_OREJIME_KEY]) || + !preferences[CORRELATION_ID_OREJIME_KEY]; - // Get the correlation id for the user from the store - const correlationId = this.cidService.getCorrelationId(); + // Add headers from the intercepted request + let headers = request.headers; + if (!correlationDeclined) { + // Get the correlation id for the user from the store + const correlationId = this.cidService.getCorrelationId(); + if (hasValue(correlationId)) { + headers = headers.append('X-CORRELATION-ID', correlationId); + } + } + headers = headers.append('X-REFERRER', this.router.url); - // Add headers from the intercepted request - let headers = request.headers; - if (hasValue(correlationId)) { - 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 }); - return next.handle(request); + // Add new headers to the intercepted request + request = request.clone({ withCredentials: true, headers: headers }); + return next.handle(request); + }), + ); } } diff --git a/src/app/correlation-id/correlation-id.service.ts b/src/app/correlation-id/correlation-id.service.ts index 057708b954..d9ea39ec87 100644 --- a/src/app/correlation-id/correlation-id.service.ts +++ b/src/app/correlation-id/correlation-id.service.ts @@ -12,6 +12,8 @@ import { isEmpty } from '../shared/empty.util'; import { SetCorrelationIdAction } from './correlation-id.actions'; import { correlationIdSelector } from './correlation-id.selector'; +export const CORRELATION_ID_COOKIE = 'dsCorrelationId'; + /** * Service to manage the correlation id, an id used to give context to server side logs */ diff --git a/src/app/shared/cookies/orejime-configuration.ts b/src/app/shared/cookies/orejime-configuration.ts index 2bebdd7d83..8ddd22f6c2 100644 --- a/src/app/shared/cookies/orejime-configuration.ts +++ b/src/app/shared/cookies/orejime-configuration.ts @@ -9,6 +9,7 @@ import { } from '../../core/google-recaptcha/google-recaptcha.service'; import { LANG_COOKIE } from '../../core/locale/locale.service'; import { NativeWindowRef } from '../../core/services/window.service'; +import { CORRELATION_ID_COOKIE } from '../../correlation-id/correlation-id.service'; /** * Cookie for has_agreed_end_user @@ -23,6 +24,8 @@ export const MATOMO_OREJIME_KEY = 'matomo'; export const MATOMO_COOKIE = 'dsMatomo'; +export const CORRELATION_ID_OREJIME_KEY = 'correlation-id'; + /** * Orejime configuration * For more information see https://github.com/empreinte-digitale/orejime @@ -141,6 +144,14 @@ export function getOrejimeConfiguration(_window: NativeWindowRef): any { HAS_AGREED_END_USER, ], }, + { + name: CORRELATION_ID_OREJIME_KEY, + purposes: ['statistical'], + required: false, + cookies: [ + CORRELATION_ID_COOKIE, + ], + }, { name: MATOMO_OREJIME_KEY, purposes: ['statistical'], diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 16d56f9708..b8bbc9ee02 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1637,6 +1637,10 @@ "cookies.consent.app.description.authentication": "Required for signing you in", + "cookies.consent.app.title.correlation-id": "Correlation ID", + + "cookies.consent.app.description.correlation-id": "Allow us to track your session for debugging purposes", + "cookies.consent.app.title.preferences": "Preferences", "cookies.consent.app.description.preferences": "Required for saving your preferences", diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 7fd6c74deb..434749a538 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -2666,6 +2666,12 @@ // "cookies.consent.app.description.authentication": "Required for signing you in", "cookies.consent.app.description.authentication": "Necessario per l'accesso", + // "cookies.consent.app.title.correlation-id": "Correlation ID", + "cookies.consent.app.title.correlation-id": "ID di correlazione", + + // "cookies.consent.app.description.correlation-id": "Allow us to track your session for debugging purposes", + "cookies.consent.app.description.correlation-id": "Consentici di tracciare la tua sessione per scopi di debug", + // "cookies.consent.app.title.preferences": "Preferences", "cookies.consent.app.title.preferences": "Preferenze",