From 6c8d4a386bed472b167b628624d6d6d68c87850a Mon Sep 17 00:00:00 2001 From: James <33908344+allen-munsch@users.noreply.github.com> Date: Mon, 23 Sep 2019 20:17:32 -0500 Subject: [PATCH 1/5] The current csrftoken parser breaks on multiple crsftokens For example: `Cookie: csrftoken=asdfasd; sessionid=asdfasdf; csrftoken=qwertyqwerty` This can happen when multiple sessions, or multiple csrftokens for different paths. --- .../static/graphene_django/graphiql.js | 105 +++++++++++------- 1 file changed, 63 insertions(+), 42 deletions(-) diff --git a/graphene_django/static/graphene_django/graphiql.js b/graphene_django/static/graphene_django/graphiql.js index 2be7e3c8f..6f4a95a10 100644 --- a/graphene_django/static/graphene_django/graphiql.js +++ b/graphene_django/static/graphene_django/graphiql.js @@ -1,26 +1,33 @@ (function() { - // Parse the cookie value for a CSRF token var csrftoken; - var cookies = ('; ' + document.cookie).split('; csrftoken='); - if (cookies.length == 2) - csrftoken = cookies.pop().split(';').shift(); - + var cookies = document.cookie.split(';').reduce((p, c) => { + let x = {}; + let key = c.split('=')[0].trim(); + x[key] = p[key] ? p[key].concat([c.split('=')[1]]) : [c.split('=')[1]]; + return Object.assign(p, x); + }, {}); // Collect the URL parameters var parameters = {}; - window.location.hash.substr(1).split('&').forEach(function (entry) { - var eq = entry.indexOf('='); - if (eq >= 0) { - parameters[decodeURIComponent(entry.slice(0, eq))] = - decodeURIComponent(entry.slice(eq + 1)); - } - }); + window.location.hash + .substr(1) + .split('&') + .forEach(function(entry) { + var eq = entry.indexOf('='); + if (eq >= 0) { + parameters[decodeURIComponent(entry.slice(0, eq))] = decodeURIComponent(entry.slice(eq + 1)); + } + }); // Produce a Location fragment string from a parameter object. function locationQuery(params) { - return '#' + Object.keys(params).map(function (key) { - return encodeURIComponent(key) + '=' + - encodeURIComponent(params[key]); - }).join('&'); + return ( + '#' + + Object.keys(params) + .map(function(key) { + return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); + }) + .join('&') + ); } // Derive a fetch URL from the current URL, sans the GraphQL parameters. var graphqlParamNames = { @@ -40,26 +47,43 @@ // Defines a GraphQL fetcher using the fetch API. function graphQLFetcher(graphQLParams) { var headers = { - 'Accept': 'application/json', + Accept: 'application/json', 'Content-Type': 'application/json' }; - if (csrftoken) { - headers['X-CSRFToken'] = csrftoken; + + var cookies = document.cookie.split(';').reduce((p, c) => { + let x = {}; + let key = c.split('=')[0].trim(); + x[key] = p[key] ? p[key].concat([c.split('=')[1]]) : [c.split('=')[1]]; + return Object.assign(p, x); + }, {}); + if (cookies.csrftoken && cookies.csrftoken.length) { + headers['X-CSRFToken'] = cookies.csrftoken.pop(); } - return fetch(fetchURL, { - method: 'post', - headers: headers, - body: JSON.stringify(graphQLParams), - credentials: 'include', - }).then(function (response) { - return response.text(); - }).then(function (responseBody) { - try { - return JSON.parse(responseBody); - } catch (error) { - return responseBody; - } - }); + function getFetch(headers) { + return fetch(fetchURL, { + method: 'post', + headers: headers, + body: JSON.stringify(graphQLParams), + credentials: 'include' + }); + } + return getFetch(headers) + .then(function(response) { + console.log(headers); + return response.text(); + }) + .then(function(responseBody) { + try { + return JSON.parse(responseBody); + } catch (error) { + if (cookies.csrftoken.length) { + headers['X-CSRFToken'] = cookies.csrftoken.pop(); + return getFetch(headers); + } + return responseBody; + } + }); } // When the query and variables string is edited, update the URL bar so // that it can be easily shared. @@ -80,11 +104,11 @@ } var options = { fetcher: graphQLFetcher, - onEditQuery: onEditQuery, - onEditVariables: onEditVariables, - onEditOperationName: onEditOperationName, - query: parameters.query, - } + onEditQuery: onEditQuery, + onEditVariables: onEditVariables, + onEditOperationName: onEditOperationName, + query: parameters.query + }; if (parameters.variables) { options.variables = parameters.variables; } @@ -92,8 +116,5 @@ options.operationName = parameters.operation_name; } // Render into the body. - ReactDOM.render( - React.createElement(GraphiQL, options), - document.body - ); + ReactDOM.render(React.createElement(GraphiQL, options), document.body); })(); From e33523e9921b410963715684798b15ee8298edf9 Mon Sep 17 00:00:00 2001 From: James <33908344+allen-munsch@users.noreply.github.com> Date: Tue, 24 Sep 2019 09:18:35 -0500 Subject: [PATCH 2/5] Use var instead of let, add var for value --- graphene_django/static/graphene_django/graphiql.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/graphene_django/static/graphene_django/graphiql.js b/graphene_django/static/graphene_django/graphiql.js index 6f4a95a10..87775d417 100644 --- a/graphene_django/static/graphene_django/graphiql.js +++ b/graphene_django/static/graphene_django/graphiql.js @@ -2,9 +2,10 @@ // Parse the cookie value for a CSRF token var csrftoken; var cookies = document.cookie.split(';').reduce((p, c) => { - let x = {}; - let key = c.split('=')[0].trim(); - x[key] = p[key] ? p[key].concat([c.split('=')[1]]) : [c.split('=')[1]]; + var x = {}; + var key = c.split('=')[0].trim(); + var value = c.split('=')[1] + x[key] = p[key] ? p[key].concat([value]) : [value]; return Object.assign(p, x); }, {}); // Collect the URL parameters From 9710d008f45aafa1c688ffb5e5331426dc1cdd59 Mon Sep 17 00:00:00 2001 From: James <33908344+allen-munsch@users.noreply.github.com> Date: Tue, 24 Sep 2019 09:20:51 -0500 Subject: [PATCH 3/5] Remove duplicate code --- graphene_django/static/graphene_django/graphiql.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/graphene_django/static/graphene_django/graphiql.js b/graphene_django/static/graphene_django/graphiql.js index 87775d417..a37bb8337 100644 --- a/graphene_django/static/graphene_django/graphiql.js +++ b/graphene_django/static/graphene_django/graphiql.js @@ -51,13 +51,6 @@ Accept: 'application/json', 'Content-Type': 'application/json' }; - - var cookies = document.cookie.split(';').reduce((p, c) => { - let x = {}; - let key = c.split('=')[0].trim(); - x[key] = p[key] ? p[key].concat([c.split('=')[1]]) : [c.split('=')[1]]; - return Object.assign(p, x); - }, {}); if (cookies.csrftoken && cookies.csrftoken.length) { headers['X-CSRFToken'] = cookies.csrftoken.pop(); } From 31391241328ecbab4539fa157c98a6d09304d963 Mon Sep 17 00:00:00 2001 From: James <33908344+allen-munsch@users.noreply.github.com> Date: Tue, 24 Sep 2019 09:22:35 -0500 Subject: [PATCH 4/5] add check for cookie --- graphene_django/static/graphene_django/graphiql.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphene_django/static/graphene_django/graphiql.js b/graphene_django/static/graphene_django/graphiql.js index a37bb8337..d97c79715 100644 --- a/graphene_django/static/graphene_django/graphiql.js +++ b/graphene_django/static/graphene_django/graphiql.js @@ -71,7 +71,7 @@ try { return JSON.parse(responseBody); } catch (error) { - if (cookies.csrftoken.length) { + if (cookies.csrftoken && cookies.csrftoken.length) { headers['X-CSRFToken'] = cookies.csrftoken.pop(); return getFetch(headers); } From f118de7bf9168adce26b016ceb67007e106e61eb Mon Sep 17 00:00:00 2001 From: James <33908344+allen-munsch@users.noreply.github.com> Date: Tue, 24 Sep 2019 09:35:43 -0500 Subject: [PATCH 5/5] update order of cookie and header adding --- .../static/graphene_django/graphiql.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/graphene_django/static/graphene_django/graphiql.js b/graphene_django/static/graphene_django/graphiql.js index d97c79715..693ecf21d 100644 --- a/graphene_django/static/graphene_django/graphiql.js +++ b/graphene_django/static/graphene_django/graphiql.js @@ -8,6 +8,13 @@ x[key] = p[key] ? p[key].concat([value]) : [value]; return Object.assign(p, x); }, {}); + var headers = { + Accept: 'application/json', + 'Content-Type': 'application/json' + }; + if (cookies.csrftoken && cookies.csrftoken.length) { + headers['X-CSRFToken'] = cookies.csrftoken.pop(); + } // Collect the URL parameters var parameters = {}; window.location.hash @@ -47,13 +54,6 @@ // Defines a GraphQL fetcher using the fetch API. function graphQLFetcher(graphQLParams) { - var headers = { - Accept: 'application/json', - 'Content-Type': 'application/json' - }; - if (cookies.csrftoken && cookies.csrftoken.length) { - headers['X-CSRFToken'] = cookies.csrftoken.pop(); - } function getFetch(headers) { return fetch(fetchURL, { method: 'post', @@ -73,6 +73,7 @@ } catch (error) { if (cookies.csrftoken && cookies.csrftoken.length) { headers['X-CSRFToken'] = cookies.csrftoken.pop(); + console.log('retry', headers) return getFetch(headers); } return responseBody;