Skip to content

Commit ba3c43a

Browse files
authored
Merge pull request #3889 from 4Science/task/main/DURACOM-309_from-community-main
Add new orejime cookie for correlation-id header
2 parents 98da220 + 520831b commit ba3c43a

21 files changed

+268
-64
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ jobs:
2929
DSPACE_CACHE_SERVERSIDE_ANONYMOUSCACHE_MAX: 0
3030
# Tell Cypress to run e2e tests using the same UI URL
3131
CYPRESS_BASE_URL: http://127.0.0.1:4000
32+
# Disable the cookie consent banner in e2e tests to avoid errors because of elements hidden by it
33+
DSPACE_INFO_ENABLECOOKIECONSENTPOPUP: false
3234
# When Chrome version is specified, we pin to a specific version of Chrome
3335
# Comment this out to use the latest release
3436
#CHROME_VERSION: "90.0.4430.212-1"

config/config.example.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,8 @@ info:
467467
enableEndUserAgreement: true
468468
enablePrivacyStatement: true
469469
enableCOARNotifySupport: true
470+
# Whether to show the cookie consent popup and the cookie settings footer link or not.
471+
enableCookieConsentPopup: true
470472

471473
# Whether to enable Markdown (https://commonmark.org/) and MathJax (https://www.mathjax.org/)
472474
# display in supported metadata fields. By default, only dc.description.abstract is supported.

cypress/support/e2e.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ before(() => {
5656
beforeEach(() => {
5757
// Pre-agree to all Orejime cookies by setting the orejime-anonymous cookie
5858
// This just ensures it doesn't get in the way of matching other objects in the page.
59-
cy.setCookie('orejime-anonymous', '{"authentication":true,"preferences":true,"acknowledgement":true,"google-analytics":true}');
59+
cy.setCookie('orejime-anonymous', '{"authentication":true,"preferences":true,"acknowledgement":true,"google-analytics":true,"correlation-id":true}');
6060

6161
// Remove any CSRF cookies saved from prior tests
6262
cy.clearCookie(DSPACE_XSRF_COOKIE);

src/app/core/auth/auth.service.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,16 @@ export class AuthService {
256256
);
257257
}
258258

259+
/**
260+
* Returns the authenticated user id from the store
261+
* @returns {User}
262+
*/
263+
public getAuthenticatedUserIdFromStore(): Observable<string> {
264+
return this.store.pipe(
265+
select(getAuthenticatedUserId),
266+
);
267+
}
268+
259269
/**
260270
* Checks if token is present into browser storage and is valid.
261271
*/

src/app/core/log/log.interceptor.spec.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,18 @@ import {
1010
import { TestBed } from '@angular/core/testing';
1111
import { Router } from '@angular/router';
1212
import { StoreModule } from '@ngrx/store';
13+
import { of } from 'rxjs';
1314

1415
import {
1516
appReducers,
1617
storeModuleConfig,
1718
} from '../../app.reducer';
1819
import { CorrelationIdService } from '../../correlation-id/correlation-id.service';
20+
import { OrejimeService } from '../../shared/cookies/orejime.service';
21+
import {
22+
CORRELATION_ID_COOKIE,
23+
CORRELATION_ID_OREJIME_KEY,
24+
} from '../../shared/cookies/orejime-configuration';
1925
import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock';
2026
import { RouterStub } from '../../shared/testing/router.stub';
2127
import { RestRequestMethod } from '../data/rest-request-method';
@@ -40,6 +46,8 @@ describe('LogInterceptor', () => {
4046
const mockStatusCode = 200;
4147
const mockStatusText = 'SUCCESS';
4248

49+
const mockOrejimeService = jasmine.createSpyObj('OrejimeService', ['getSavedPreferences']);
50+
4351

4452
beforeEach(() => {
4553
TestBed.configureTestingModule({
@@ -56,6 +64,7 @@ describe('LogInterceptor', () => {
5664
{ provide: Router, useValue: router },
5765
{ provide: CorrelationIdService, useClass: CorrelationIdService },
5866
{ provide: UUIDService, useClass: UUIDService },
67+
{ provide: OrejimeService, useValue: mockOrejimeService },
5968
provideHttpClient(withInterceptorsFromDi()),
6069
provideHttpClientTesting(),
6170
],
@@ -66,12 +75,14 @@ describe('LogInterceptor', () => {
6675
cookieService = TestBed.inject(CookieService);
6776
correlationIdService = TestBed.inject(CorrelationIdService);
6877

69-
cookieService.set('CORRELATION-ID','123455');
70-
correlationIdService.initCorrelationId();
78+
cookieService.set(CORRELATION_ID_COOKIE,'123455');
79+
correlationIdService.setCorrelationId();
7180
});
7281

7382

74-
it('headers should be set', (done) => {
83+
it('headers should be set when cookie is accepted', (done) => {
84+
mockOrejimeService.getSavedPreferences.and.returnValue(of({ [CORRELATION_ID_OREJIME_KEY]: true }));
85+
7586
service.request(RestRequestMethod.POST, 'server/api/core/items', 'test', { withCredentials: false }).subscribe((response) => {
7687
expect(response).toBeTruthy();
7788
done();
@@ -83,7 +94,23 @@ describe('LogInterceptor', () => {
8394
expect(httpRequest.request.headers.has('X-REFERRER')).toBeTrue();
8495
});
8596

86-
it('headers should have the right values', (done) => {
97+
it('headers should not be set when cookie is declined', (done) => {
98+
mockOrejimeService.getSavedPreferences.and.returnValue(of({ [CORRELATION_ID_OREJIME_KEY]: false }));
99+
100+
service.request(RestRequestMethod.POST, 'server/api/core/items', 'test', { withCredentials: false }).subscribe((response) => {
101+
expect(response).toBeTruthy();
102+
done();
103+
});
104+
105+
const httpRequest = httpMock.expectOne('server/api/core/items');
106+
httpRequest.flush(mockPayload, { status: mockStatusCode, statusText: mockStatusText });
107+
expect(httpRequest.request.headers.has('X-CORRELATION-ID')).toBeFalse();
108+
expect(httpRequest.request.headers.has('X-REFERRER')).toBeTrue();
109+
});
110+
111+
it('headers should have the right values when cookie is accepted', (done) => {
112+
mockOrejimeService.getSavedPreferences.and.returnValue(of({ [CORRELATION_ID_OREJIME_KEY]: true }));
113+
87114
service.request(RestRequestMethod.POST, 'server/api/core/items', 'test', { withCredentials: false }).subscribe((response) => {
88115
expect(response).toBeTruthy();
89116
done();

src/app/core/log/log.interceptor.ts

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ import {
77
import { Injectable } from '@angular/core';
88
import { Router } from '@angular/router';
99
import { Observable } from 'rxjs';
10+
import { switchMap } from 'rxjs/operators';
1011

1112
import { CorrelationIdService } from '../../correlation-id/correlation-id.service';
12-
import { hasValue } from '../../shared/empty.util';
13+
import { OrejimeService } from '../../shared/cookies/orejime.service';
14+
import { CORRELATION_ID_OREJIME_KEY } from '../../shared/cookies/orejime-configuration';
15+
import {
16+
hasValue,
17+
isEmpty,
18+
} from '../../shared/empty.util';
1319

1420
/**
1521
* Log Interceptor intercepting Http Requests & Responses to
@@ -19,22 +25,37 @@ import { hasValue } from '../../shared/empty.util';
1925
@Injectable()
2026
export class LogInterceptor implements HttpInterceptor {
2127

22-
constructor(private cidService: CorrelationIdService, private router: Router) {}
28+
constructor(
29+
private cidService: CorrelationIdService,
30+
private router: Router,
31+
private orejimeService: OrejimeService,
32+
) {
33+
}
2334

2435
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
36+
return this.orejimeService.getSavedPreferences().pipe(
37+
switchMap(preferences => {
38+
// Check if the user has declined correlation id tracking
39+
const correlationDeclined =
40+
isEmpty(preferences) ||
41+
isEmpty(preferences[CORRELATION_ID_OREJIME_KEY]) ||
42+
!preferences[CORRELATION_ID_OREJIME_KEY];
2543

26-
// Get the correlation id for the user from the store
27-
const correlationId = this.cidService.getCorrelationId();
28-
29-
// Add headers from the intercepted request
30-
let headers = request.headers;
31-
if (hasValue(correlationId)) {
32-
headers = headers.append('X-CORRELATION-ID', correlationId);
33-
}
34-
headers = headers.append('X-REFERRER', this.router.url);
44+
// Add headers from the intercepted request
45+
let headers = request.headers;
46+
if (!correlationDeclined) {
47+
// Get the correlation id for the user from the store
48+
const correlationId = this.cidService.getCorrelationId();
49+
if (hasValue(correlationId)) {
50+
headers = headers.append('X-CORRELATION-ID', correlationId);
51+
}
52+
}
53+
headers = headers.append('X-REFERRER', this.router.url);
3554

36-
// Add new headers to the intercepted request
37-
request = request.clone({ withCredentials: true, headers: headers });
38-
return next.handle(request);
55+
// Add new headers to the intercepted request
56+
request = request.clone({ withCredentials: true, headers: headers });
57+
return next.handle(request);
58+
}),
59+
);
3960
}
4061
}

src/app/correlation-id/correlation-id.service.spec.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import {
44
StoreModule,
55
} from '@ngrx/store';
66
import { MockStore } from '@ngrx/store/testing';
7+
import { of } from 'rxjs';
78

89
import {
910
appReducers,
1011
AppState,
1112
storeModuleConfig,
1213
} from '../app.reducer';
1314
import { UUIDService } from '../core/shared/uuid.service';
15+
import { CORRELATION_ID_COOKIE } from '../shared/cookies/orejime-configuration';
1416
import { CookieServiceMock } from '../shared/mocks/cookie.service.mock';
1517
import { SetCorrelationIdAction } from './correlation-id.actions';
1618
import { CorrelationIdService } from './correlation-id.service';
@@ -34,7 +36,13 @@ describe('CorrelationIdService', () => {
3436
cookieService = new CookieServiceMock();
3537
uuidService = new UUIDService();
3638
store = TestBed.inject(Store) as MockStore<AppState>;
37-
service = new CorrelationIdService(cookieService, uuidService, store);
39+
const mockOrejimeService = {
40+
getSavedPreferences: () => of({ CORRELATION_ID_OREJIME_KEY: true }),
41+
initialize: jasmine.createSpy('initialize'),
42+
showSettings: jasmine.createSpy('showSettings'),
43+
};
44+
45+
service = new CorrelationIdService(cookieService, uuidService, store, mockOrejimeService, { nativeWindow: undefined });
3846
});
3947

4048
describe('getCorrelationId', () => {
@@ -46,45 +54,45 @@ describe('CorrelationIdService', () => {
4654
});
4755

4856

49-
describe('initCorrelationId', () => {
57+
describe('setCorrelationId', () => {
5058
const cookieCID = 'cookie CID';
5159
const storeCID = 'store CID';
5260

5361
it('should set cookie and store values to a newly generated value if neither ex', () => {
54-
service.initCorrelationId();
62+
service.setCorrelationId();
5563

56-
expect(cookieService.get('CORRELATION-ID')).toBeTruthy();
64+
expect(cookieService.get(CORRELATION_ID_COOKIE)).toBeTruthy();
5765
expect(service.getCorrelationId()).toBeTruthy();
58-
expect(cookieService.get('CORRELATION-ID')).toEqual(service.getCorrelationId());
66+
expect(cookieService.get(CORRELATION_ID_COOKIE)).toEqual(service.getCorrelationId());
5967
});
6068

6169
it('should set store value to cookie value if present', () => {
6270
expect(service.getCorrelationId()).toBe(null);
6371

64-
cookieService.set('CORRELATION-ID', cookieCID);
72+
cookieService.set(CORRELATION_ID_COOKIE, cookieCID);
6573

66-
service.initCorrelationId();
74+
service.setCorrelationId();
6775

68-
expect(cookieService.get('CORRELATION-ID')).toBe(cookieCID);
76+
expect(cookieService.get(CORRELATION_ID_COOKIE)).toBe(cookieCID);
6977
expect(service.getCorrelationId()).toBe(cookieCID);
7078
});
7179

7280
it('should set cookie value to store value if present', () => {
7381
store.dispatch(new SetCorrelationIdAction(storeCID));
7482

75-
service.initCorrelationId();
83+
service.setCorrelationId();
7684

77-
expect(cookieService.get('CORRELATION-ID')).toBe(storeCID);
85+
expect(cookieService.get(CORRELATION_ID_COOKIE)).toBe(storeCID);
7886
expect(service.getCorrelationId()).toBe(storeCID);
7987
});
8088

8189
it('should set store value to cookie value if both are present', () => {
82-
cookieService.set('CORRELATION-ID', cookieCID);
90+
cookieService.set(CORRELATION_ID_COOKIE, cookieCID);
8391
store.dispatch(new SetCorrelationIdAction(storeCID));
8492

85-
service.initCorrelationId();
93+
service.setCorrelationId();
8694

87-
expect(cookieService.get('CORRELATION-ID')).toBe(cookieCID);
95+
expect(cookieService.get(CORRELATION_ID_COOKIE)).toBe(cookieCID);
8896
expect(service.getCorrelationId()).toBe(cookieCID);
8997
});
9098
});

src/app/correlation-id/correlation-id.service.ts

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { Injectable } from '@angular/core';
1+
import {
2+
Inject,
3+
Injectable,
4+
} from '@angular/core';
25
import {
36
select,
47
Store,
@@ -7,8 +10,20 @@ import { take } from 'rxjs/operators';
710

811
import { AppState } from '../app.reducer';
912
import { CookieService } from '../core/services/cookie.service';
13+
import {
14+
NativeWindowRef,
15+
NativeWindowService,
16+
} from '../core/services/window.service';
1017
import { UUIDService } from '../core/shared/uuid.service';
11-
import { isEmpty } from '../shared/empty.util';
18+
import { OrejimeService } from '../shared/cookies/orejime.service';
19+
import {
20+
CORRELATION_ID_COOKIE,
21+
CORRELATION_ID_OREJIME_KEY,
22+
} from '../shared/cookies/orejime-configuration';
23+
import {
24+
hasValue,
25+
isEmpty,
26+
} from '../shared/empty.util';
1227
import { SetCorrelationIdAction } from './correlation-id.actions';
1328
import { correlationIdSelector } from './correlation-id.selector';
1429

@@ -24,15 +39,32 @@ export class CorrelationIdService {
2439
protected cookieService: CookieService,
2540
protected uuidService: UUIDService,
2641
protected store: Store<AppState>,
42+
protected orejimeService: OrejimeService,
43+
@Inject(NativeWindowService) protected _window: NativeWindowRef,
2744
) {
45+
if (this._window?.nativeWindow) {
46+
this._window.nativeWindow.initCorrelationId = () => this.initCorrelationId();
47+
}
2848
}
2949

3050
/**
31-
* Initialize the correlation id based on the cookie or the ngrx store
51+
* Check if the correlation id is allowed to be set, then set it
3252
*/
3353
initCorrelationId(): void {
54+
this.orejimeService?.getSavedPreferences().subscribe(preferences => {
55+
if (hasValue(preferences) && preferences[CORRELATION_ID_OREJIME_KEY]) {
56+
this.setCorrelationId();
57+
}
58+
},
59+
);
60+
}
61+
62+
/**
63+
* Initialize the correlation id based on the cookie or the ngrx store
64+
*/
65+
setCorrelationId(): void {
3466
// first see of there's a cookie with a correlation-id
35-
let correlationId = this.cookieService.get('CORRELATION-ID');
67+
let correlationId = this.cookieService.get(CORRELATION_ID_COOKIE);
3668

3769
// if there isn't see if there's an ID in the store
3870
if (isEmpty(correlationId)) {
@@ -46,7 +78,7 @@ export class CorrelationIdService {
4678

4779
// Store the correct id both in the store and as a cookie to ensure they're in sync
4880
this.store.dispatch(new SetCorrelationIdAction(correlationId));
49-
this.cookieService.set('CORRELATION-ID', correlationId);
81+
this.cookieService.set(CORRELATION_ID_COOKIE, correlationId);
5082
}
5183

5284
/**

src/app/footer/footer.component.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,13 @@ <h5 class="text-uppercase">Footer Content</h5>
5959
href="https://www.lyrasis.org/" role="link" tabindex="0">{{ 'footer.link.lyrasis' | translate}}</a>
6060
</p>
6161
<ul class="footer-info list-unstyled d-flex justify-content-center mb-0">
62+
@if (showCookieSettings) {
6263
<li>
63-
<button class="btn btn-link text-white" type="button" (click)="showCookieSettings()" role="button" tabindex="0">
64+
<button class="btn btn-link text-white" type="button" (click)="openCookieSettings()" role="button" tabindex="0">
6465
{{ 'footer.link.cookies' | translate}}
6566
</button>
6667
</li>
68+
}
6769
@if (showPrivacyPolicy) {
6870
<li>
6971
<a class="btn text-white"

src/app/footer/footer.component.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,21 @@ describe('Footer component', () => {
6363
expect(comp.showEndUserAgreement).toBe(environment.info.enableEndUserAgreement);
6464
});
6565

66-
describe('showCookieSettings', () => {
66+
describe('openCookieSettings', () => {
6767
it('should call cookies.showSettings() if cookies is defined', () => {
6868
const cookies = jasmine.createSpyObj('cookies', ['showSettings']);
6969
comp.cookies = cookies;
70-
comp.showCookieSettings();
70+
comp.openCookieSettings();
7171
expect(cookies.showSettings).toHaveBeenCalled();
7272
});
7373

7474
it('should not call cookies.showSettings() if cookies is undefined', () => {
7575
comp.cookies = undefined;
76-
expect(() => comp.showCookieSettings()).not.toThrow();
76+
expect(() => comp.openCookieSettings()).not.toThrow();
7777
});
7878

7979
it('should return false', () => {
80-
expect(comp.showCookieSettings()).toBeFalse();
80+
expect(comp.openCookieSettings()).toBeFalse();
8181
});
8282
});
8383

0 commit comments

Comments
 (0)