Standardize access to CSRF token in JavaScript
This commit is contained in:
parent
6c0473ef6f
commit
cca06da2e4
5 changed files with 112 additions and 12 deletions
|
@ -3,6 +3,7 @@
|
|||
|
||||
import '../lib/utils/url_utility';
|
||||
import { HIDDEN_CLASS } from '../lib/utils/constants';
|
||||
import csrf from '../lib/utils/csrf';
|
||||
|
||||
function toggleLoading($el, $icon, loading) {
|
||||
if (loading) {
|
||||
|
@ -36,9 +37,7 @@ export default class BlobFileDropzone {
|
|||
maxFiles: 1,
|
||||
addRemoveLinks: true,
|
||||
previewsContainer: '.dropzone-previews',
|
||||
headers: {
|
||||
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
|
||||
},
|
||||
headers: csrf.headers,
|
||||
init: function () {
|
||||
this.on('addedfile', function () {
|
||||
toggleLoading(submitButton, submitButtonLoadingIcon, false);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
/* global Dropzone */
|
||||
import _ from 'underscore';
|
||||
import './preview_markdown';
|
||||
import csrf from './lib/utils/csrf';
|
||||
|
||||
window.DropzoneInput = (function() {
|
||||
function DropzoneInput(form) {
|
||||
|
@ -50,9 +51,7 @@ window.DropzoneInput = (function() {
|
|||
paramName: 'file',
|
||||
maxFilesize: maxFileSize,
|
||||
uploadMultiple: false,
|
||||
headers: {
|
||||
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
headers: csrf.headers,
|
||||
previewContainer: false,
|
||||
processing: function() {
|
||||
return $('.div-dropzone-alert').alert('close');
|
||||
|
@ -260,9 +259,7 @@ window.DropzoneInput = (function() {
|
|||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
headers: {
|
||||
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
headers: csrf.headers,
|
||||
beforeSend: function() {
|
||||
showSpinner();
|
||||
return closeAlertMessage();
|
||||
|
|
56
app/assets/javascripts/lib/utils/csrf.js
Normal file
56
app/assets/javascripts/lib/utils/csrf.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
This module provides easy access to the CSRF token and caches
|
||||
it for re-use. It also exposes some values commonly used in relation
|
||||
to the CSRF token (header key and headers object).
|
||||
|
||||
If you need to refresh the csrfToken for some reason, just call `init` and
|
||||
then use the accessors as you would normally.
|
||||
|
||||
If you need to compose a headers object, use the spread operator:
|
||||
|
||||
```
|
||||
headers: {
|
||||
...csrf.headers,
|
||||
someOtherHeader: '12345',
|
||||
}
|
||||
```
|
||||
*/
|
||||
|
||||
const csrf = {
|
||||
init() {
|
||||
const tokenEl = document.querySelector('meta[name=csrf-token]');
|
||||
|
||||
if (tokenEl !== null) {
|
||||
this.csrfToken = tokenEl.getAttribute('content');
|
||||
} else {
|
||||
this.csrfToken = null;
|
||||
}
|
||||
},
|
||||
|
||||
get token() {
|
||||
return this.csrfToken;
|
||||
},
|
||||
|
||||
get headerKey() {
|
||||
return 'X-CSRF-Token';
|
||||
},
|
||||
|
||||
get headers() {
|
||||
if (this.csrfToken !== null) {
|
||||
return {
|
||||
[this.headerKey]: this.token,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
};
|
||||
|
||||
csrf.init();
|
||||
|
||||
// use our cached token for any $.rails-generated AJAX requests
|
||||
if ($.rails) {
|
||||
$.rails.csrfToken = () => csrf.token;
|
||||
}
|
||||
|
||||
export default csrf;
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import VueResource from 'vue-resource';
|
||||
import csrf from '../lib/utils/csrf';
|
||||
|
||||
Vue.use(VueResource);
|
||||
|
||||
|
@ -18,9 +19,7 @@ Vue.http.interceptors.push((request, next) => {
|
|||
// New Vue Resource version uses Headers, we are expecting a plain object to render pagination
|
||||
// and polling.
|
||||
Vue.http.interceptors.push((request, next) => {
|
||||
if ($.rails) {
|
||||
request.headers.set('X-CSRF-Token', $.rails.csrfToken());
|
||||
}
|
||||
request.headers.set(csrf.headerKey, csrf.token);
|
||||
|
||||
next((response) => {
|
||||
// Headers object has a `forEach` property that iterates through all values.
|
||||
|
|
49
spec/javascripts/lib/utils/csrf_token_spec.js
Normal file
49
spec/javascripts/lib/utils/csrf_token_spec.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
import csrf from '~/lib/utils/csrf';
|
||||
|
||||
describe('csrf', () => {
|
||||
beforeEach(() => {
|
||||
this.tokenKey = 'X-CSRF-Token';
|
||||
this.token = 'pH1cvjnP9grx2oKlhWEDvUZnJ8x2eXsIs1qzyHkF3DugSG5yTxR76CWeEZRhML2D1IeVB7NEW0t5l/axE4iJpQ==';
|
||||
});
|
||||
|
||||
it('returns the correct headerKey', () => {
|
||||
expect(csrf.headerKey).toBe(this.tokenKey);
|
||||
});
|
||||
|
||||
describe('when csrf token is in the DOM', () => {
|
||||
beforeEach(() => {
|
||||
setFixtures(`
|
||||
<meta name="csrf-token" content="${this.token}">
|
||||
`);
|
||||
|
||||
csrf.init();
|
||||
});
|
||||
|
||||
it('returns the csrf token', () => {
|
||||
expect(csrf.token).toBe(this.token);
|
||||
});
|
||||
|
||||
it('returns the csrf headers object', () => {
|
||||
expect(csrf.headers[this.tokenKey]).toBe(this.token);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when csrf token is not in the DOM', () => {
|
||||
beforeEach(() => {
|
||||
setFixtures(`
|
||||
<meta name="some-other-token">
|
||||
`);
|
||||
|
||||
csrf.init();
|
||||
});
|
||||
|
||||
it('returns null for token', () => {
|
||||
expect(csrf.token).toBeNull();
|
||||
});
|
||||
|
||||
it('returns empty object for headers', () => {
|
||||
expect(typeof csrf.headers).toBe('object');
|
||||
expect(Object.keys(csrf.headers).length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue