-
Notifications
You must be signed in to change notification settings - Fork 440
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2886 from tdonohue/csrf_fixes
Ensures CSRF token is initialized prior to first modifying (non-`GET`) request
- Loading branch information
Showing
33 changed files
with
269 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { HttpClient } from '@angular/common/http'; | ||
import { | ||
HttpClientTestingModule, | ||
HttpTestingController, | ||
} from '@angular/common/http/testing'; | ||
import { TestBed } from '@angular/core/testing'; | ||
|
||
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; | ||
import { BrowserXSRFService } from './browser-xsrf.service'; | ||
|
||
describe(`BrowserXSRFService`, () => { | ||
let service: BrowserXSRFService; | ||
let httpClient: HttpClient; | ||
let httpTestingController: HttpTestingController; | ||
|
||
const endpointURL = new RESTURLCombiner('/security/csrf').toString(); | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
imports: [ HttpClientTestingModule ], | ||
providers: [ BrowserXSRFService ], | ||
}); | ||
httpClient = TestBed.inject(HttpClient); | ||
httpTestingController = TestBed.inject(HttpTestingController); | ||
service = TestBed.inject(BrowserXSRFService); | ||
}); | ||
|
||
describe(`initXSRFToken`, () => { | ||
it(`should perform a GET to the csrf endpoint`, (done: DoneFn) => { | ||
service.initXSRFToken(httpClient)(); | ||
|
||
const req = httpTestingController.expectOne({ | ||
url: endpointURL, | ||
method: 'GET', | ||
}); | ||
|
||
req.flush({}); | ||
httpTestingController.verify(); | ||
expect().nothing(); | ||
done(); | ||
}); | ||
|
||
describe(`when the GET succeeds`, () => { | ||
it(`should set tokenInitialized$ to true`, (done: DoneFn) => { | ||
service.initXSRFToken(httpClient)(); | ||
|
||
const req = httpTestingController.expectOne(endpointURL); | ||
|
||
req.flush({}); | ||
httpTestingController.verify(); | ||
|
||
expect(service.tokenInitialized$.getValue()).toBeTrue(); | ||
done(); | ||
}); | ||
}); | ||
|
||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { HttpClient } from '@angular/common/http'; | ||
import { Injectable } from '@angular/core'; | ||
import { take } from 'rxjs/operators'; | ||
|
||
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; | ||
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() | ||
export class BrowserXSRFService extends XSRFService { | ||
initXSRFToken(httpClient: HttpClient): () => Promise<any> { | ||
return () => new Promise<void>((resolve) => { | ||
// Force a new token to be created by calling the CSRF endpoint | ||
httpClient.get(new RESTURLCombiner('/security/csrf').toString(), undefined).pipe( | ||
take(1), | ||
).subscribe(() => { | ||
// Once token is returned, set tokenInitialized to true. | ||
this.tokenInitialized$.next(true); | ||
}); | ||
|
||
// return immediately, the rest of the app doesn't need to wait for this to finish | ||
resolve(); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { HttpClient } from '@angular/common/http'; | ||
|
||
import { ServerXSRFService } from './server-xsrf.service'; | ||
|
||
describe(`ServerXSRFService`, () => { | ||
let service: ServerXSRFService; | ||
let httpClient: HttpClient; | ||
|
||
beforeEach(() => { | ||
httpClient = jasmine.createSpyObj(['post', 'get', 'request']); | ||
service = new ServerXSRFService(); | ||
}); | ||
|
||
describe(`initXSRFToken`, () => { | ||
it(`shouldn't perform any requests`, (done: DoneFn) => { | ||
service.initXSRFToken(httpClient)().then(() => { | ||
for (const prop in httpClient) { | ||
if (httpClient.hasOwnProperty(prop)) { | ||
expect(httpClient[prop]).not.toHaveBeenCalled(); | ||
} | ||
} | ||
done(); | ||
}); | ||
}); | ||
|
||
it(`should leave tokenInitialized$ on false`, (done: DoneFn) => { | ||
service.initXSRFToken(httpClient)().then(() => { | ||
expect(service.tokenInitialized$.getValue()).toBeFalse(); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { HttpClient } from '@angular/common/http'; | ||
import { Injectable } from '@angular/core'; | ||
|
||
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() | ||
export class ServerXSRFService extends XSRFService { | ||
initXSRFToken(httpClient: HttpClient): () => Promise<any> { | ||
return () => new Promise<void>((resolve) => { | ||
// return immediately, and keep tokenInitialized$ false. The server side can make only GET | ||
// requests, since it can never get a valid XSRF cookie | ||
resolve(); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { HttpClient } from '@angular/common/http'; | ||
|
||
import { XSRFService } from './xsrf.service'; | ||
|
||
class XSRFServiceImpl extends XSRFService { | ||
initXSRFToken(httpClient: HttpClient): () => Promise<any> { | ||
return () => null; | ||
} | ||
} | ||
|
||
describe(`XSRFService`, () => { | ||
let service: XSRFService; | ||
|
||
beforeEach(() => { | ||
service = new XSRFServiceImpl(); | ||
}); | ||
|
||
it(`should start with tokenInitialized$.hasValue() === false`, () => { | ||
expect(service.tokenInitialized$.getValue()).toBeFalse(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { HttpClient } from '@angular/common/http'; | ||
import { Injectable } from '@angular/core'; | ||
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() | ||
export abstract class XSRFService { | ||
public tokenInitialized$: BehaviorSubject<boolean> = new BehaviorSubject(false); | ||
|
||
abstract initXSRFToken(httpClient: HttpClient): () => Promise<any>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.