Update code to use GET request. Cleanup lint errors & add in basic TypeDocs

This commit is contained in:
Tim Donohue
2024-04-03 10:25:43 -05:00
parent 7d5e499f21
commit 769210bef2
6 changed files with 42 additions and 32 deletions

View File

@@ -1,8 +1,12 @@
import { BrowserXSRFService } from './browser-xsrf.service';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; import {
HttpClientTestingModule,
HttpTestingController,
} from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
import { BrowserXSRFService } from './browser-xsrf.service';
describe(`BrowserXSRFService`, () => { describe(`BrowserXSRFService`, () => {
let service: BrowserXSRFService; let service: BrowserXSRFService;
@@ -14,7 +18,7 @@ describe(`BrowserXSRFService`, () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ HttpClientTestingModule ], imports: [ HttpClientTestingModule ],
providers: [ BrowserXSRFService ] providers: [ BrowserXSRFService ],
}); });
httpClient = TestBed.inject(HttpClient); httpClient = TestBed.inject(HttpClient);
httpTestingController = TestBed.inject(HttpTestingController); httpTestingController = TestBed.inject(HttpTestingController);
@@ -22,20 +26,22 @@ describe(`BrowserXSRFService`, () => {
}); });
describe(`initXSRFToken`, () => { describe(`initXSRFToken`, () => {
it(`should perform a POST to the csrf endpoint`, () => { it(`should perform a GET to the csrf endpoint`, (done: DoneFn) => {
service.initXSRFToken(httpClient)(); service.initXSRFToken(httpClient)();
const req = httpTestingController.expectOne({ const req = httpTestingController.expectOne({
url: endpointURL, url: endpointURL,
method: 'POST' method: 'GET',
}); });
req.flush({}); req.flush({});
httpTestingController.verify(); httpTestingController.verify();
expect().nothing();
done();
}); });
describe(`when the POST succeeds`, () => { describe(`when the GET succeeds`, () => {
it(`should set tokenInitialized$ to true`, () => { it(`should set tokenInitialized$ to true`, (done: DoneFn) => {
service.initXSRFToken(httpClient)(); service.initXSRFToken(httpClient)();
const req = httpTestingController.expectOne(endpointURL); const req = httpTestingController.expectOne(endpointURL);
@@ -44,19 +50,7 @@ describe(`BrowserXSRFService`, () => {
httpTestingController.verify(); httpTestingController.verify();
expect(service.tokenInitialized$.getValue()).toBeTrue(); expect(service.tokenInitialized$.getValue()).toBeTrue();
}); done();
});
describe(`when the POST fails`, () => {
it(`should set tokenInitialized$ to true`, () => {
service.initXSRFToken(httpClient)();
const req = httpTestingController.expectOne(endpointURL);
req.error(new ErrorEvent('415'));
httpTestingController.verify();
expect(service.tokenInitialized$.getValue()).toBeTrue();
}); });
}); });

View File

@@ -1,21 +1,25 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { take } from 'rxjs/operators';
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';
import { take, catchError } from 'rxjs/operators';
import { of as observableOf } from 'rxjs';
import { XSRFService } from './xsrf.service'; import { XSRFService } from './xsrf.service';
/**
* Browser (CSR) Service to obtain a new CSRF/XSRF token when needed by our RequestService
* to perform a modify request (e.g. POST/PUT/DELETE).
* NOTE: This is primarily necessary before the *first* modifying request, as the CSRF
* token may not yet be initialized.
*/
@Injectable() @Injectable()
export class BrowserXSRFService extends XSRFService { export class BrowserXSRFService extends XSRFService {
initXSRFToken(httpClient: HttpClient): () => Promise<any> { initXSRFToken(httpClient: HttpClient): () => Promise<any> {
return () => new Promise((resolve) => { return () => new Promise<void>((resolve) => {
httpClient.post(new RESTURLCombiner('/security/csrf').toString(), undefined).pipe( // Force a new token to be created by calling the CSRF endpoint
// errors are to be expected if the token and the cookie don't match, that's what we're httpClient.get(new RESTURLCombiner('/security/csrf').toString(), undefined).pipe(
// trying to fix for future requests, so just emit any observable to end up in the
// subscribe
catchError(() => observableOf(null)),
take(1), take(1),
).subscribe(() => { ).subscribe(() => {
// Once token is returned, set tokenInitialized to true.
this.tokenInitialized$.next(true); this.tokenInitialized$.next(true);
}); });

View File

@@ -1,6 +1,7 @@
import { ServerXSRFService } from './server-xsrf.service';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { ServerXSRFService } from './server-xsrf.service';
describe(`ServerXSRFService`, () => { describe(`ServerXSRFService`, () => {
let service: ServerXSRFService; let service: ServerXSRFService;
let httpClient: HttpClient; let httpClient: HttpClient;

View File

@@ -1,11 +1,16 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { XSRFService } from './xsrf.service'; import { XSRFService } from './xsrf.service';
/**
* Server (SSR) Service to obtain a new CSRF/XSRF token. Because SSR only triggers GET
* requests a CSRF token is never needed.
*/
@Injectable() @Injectable()
export class ServerXSRFService extends XSRFService { export class ServerXSRFService extends XSRFService {
initXSRFToken(httpClient: HttpClient): () => Promise<any> { initXSRFToken(httpClient: HttpClient): () => Promise<any> {
return () => new Promise((resolve) => { return () => new Promise<void>((resolve) => {
// return immediately, and keep tokenInitialized$ false. The server side can make only GET // return immediately, and keep tokenInitialized$ false. The server side can make only GET
// requests, since it can never get a valid XSRF cookie // requests, since it can never get a valid XSRF cookie
resolve(); resolve();

View File

@@ -1,6 +1,7 @@
import { XSRFService } from './xsrf.service';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { XSRFService } from './xsrf.service';
class XSRFServiceImpl extends XSRFService { class XSRFServiceImpl extends XSRFService {
initXSRFToken(httpClient: HttpClient): () => Promise<any> { initXSRFToken(httpClient: HttpClient): () => Promise<any> {
return () => null; return () => null;

View File

@@ -2,6 +2,11 @@ import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
/**
* Abstract CSRF/XSRF Service used to track whether a CSRF token has been received
* from the DSpace REST API. Once it is received, the "tokenInitialized$" flag will
* be set to "true".
*/
@Injectable() @Injectable()
export abstract class XSRFService { export abstract class XSRFService {
public tokenInitialized$: BehaviorSubject<boolean> = new BehaviorSubject(false); public tokenInitialized$: BehaviorSubject<boolean> = new BehaviorSubject(false);