mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-03-27 05:11:42 -04:00
Merge pull request #391 from rschamp/feature/token-from-cookie
Add method to retrieve token from session cookie and use it
This commit is contained in:
commit
29fa914b9a
6 changed files with 133 additions and 34 deletions
|
@ -52,6 +52,7 @@
|
|||
"lodash.range": "3.0.1",
|
||||
"minilog": "2.0.8",
|
||||
"node-sass": "3.3.3",
|
||||
"pako": "0.2.8",
|
||||
"po2icu": "git://github.com/LLK/po2icu.git#develop",
|
||||
"react-addons-test-utils": "0.14.7",
|
||||
"react-modal": "0.6.1",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var cookie = require('cookie');
|
||||
var xhr = require('xhr');
|
||||
var pako = require('pako');
|
||||
|
||||
/**
|
||||
* Module that handles coookie interactions.
|
||||
|
@ -9,41 +10,70 @@ var xhr = require('xhr');
|
|||
* set(name, value) – synchronously sets the cookie
|
||||
* use(name, uri, callback) – can by sync or async, gets cookie from the uri if not there.
|
||||
*/
|
||||
var Jar = {};
|
||||
var Jar = {
|
||||
unsign: function (value, callback) {
|
||||
// Return the usable content portion of a signed, compressed cookie generated by
|
||||
// Django's signing module
|
||||
// https://github.com/django/django/blob/stable/1.8.x/django/core/signing.py
|
||||
if (!value) return callback('No value to unsign');
|
||||
try {
|
||||
var b64Data = value.split(':')[0];
|
||||
var decompress = false;
|
||||
if (b64Data[0] === '.') {
|
||||
decompress = true;
|
||||
b64Data = b64Data.substring(1);
|
||||
}
|
||||
|
||||
Jar.get = function (name, callback) {
|
||||
// Get cookie by name
|
||||
var obj = cookie.parse(document.cookie) || {};
|
||||
// Django makes its base64 strings url safe by replacing + and / with - and _ respectively
|
||||
// using base64.urlsafe_b64encode
|
||||
// https://docs.python.org/2/library/base64.html#base64.b64encode
|
||||
b64Data = b64Data.replace(/[-_]/g, function (c) {return {'-':'+', '_':'/'}[c]; });
|
||||
var strData = atob(b64Data);
|
||||
|
||||
// Handle optional callback
|
||||
if (typeof callback === 'function') {
|
||||
if (typeof obj === 'undefined') return callback('Cookie not found.');
|
||||
return callback(null, obj[name]);
|
||||
}
|
||||
if (decompress) {
|
||||
var charData = strData.split('').map(function (c) { return c.charCodeAt(0); });
|
||||
var binData = new Uint8Array(charData);
|
||||
var data = pako.inflate(binData);
|
||||
strData = String.fromCharCode.apply(null, new Uint16Array(data));
|
||||
}
|
||||
|
||||
return obj[name];
|
||||
};
|
||||
return callback(null, strData);
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
},
|
||||
get: function (name, callback) {
|
||||
// Get cookie by name
|
||||
var obj = cookie.parse(document.cookie) || {};
|
||||
|
||||
Jar.use = function (name, uri, callback) {
|
||||
// Attempt to get cookie
|
||||
Jar.get(name, function (err, obj) {
|
||||
if (typeof obj !== 'undefined') return callback(null, obj);
|
||||
// Handle optional callback
|
||||
if (typeof callback === 'function') {
|
||||
if (typeof obj === 'undefined') return callback('Cookie not found.');
|
||||
return callback(null, obj[name]);
|
||||
}
|
||||
|
||||
// Make XHR request to cookie setter uri
|
||||
xhr({
|
||||
uri: uri
|
||||
}, function (err) {
|
||||
if (err) return callback(err);
|
||||
Jar.get(name, callback);
|
||||
return obj[name];
|
||||
},
|
||||
use: function (name, uri, callback) {
|
||||
// Attempt to get cookie
|
||||
Jar.get(name, function (err, obj) {
|
||||
if (typeof obj !== 'undefined') return callback(null, obj);
|
||||
|
||||
// Make XHR request to cookie setter uri
|
||||
xhr({
|
||||
uri: uri
|
||||
}, function (err) {
|
||||
if (err) return callback(err);
|
||||
Jar.get(name, callback);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Jar.set = function (name, value) {
|
||||
var obj = cookie.serialize(name, value);
|
||||
var expires = '; expires=' + new Date(new Date().setYear(new Date().getFullYear() + 1)).toUTCString();
|
||||
var path = '; path=/';
|
||||
document.cookie = obj + expires + path;
|
||||
},
|
||||
set: function (name, value) {
|
||||
var obj = cookie.serialize(name, value);
|
||||
var expires = '; expires=' + new Date(new Date().setYear(new Date().getFullYear() + 1)).toUTCString();
|
||||
var path = '; path=/';
|
||||
document.cookie = obj + expires + path;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Jar;
|
||||
|
|
|
@ -33,6 +33,13 @@ var Api = {
|
|||
// custom headers.
|
||||
defaults(opts, {useXDR: true});
|
||||
delete opts.headers;
|
||||
if (opts.authentication) {
|
||||
var authenticationParams = ['x-token=' + opts.authentication];
|
||||
var parts = opts.uri.split('?');
|
||||
var qs = (parts[1] || '').split('&').concat(authenticationParams).join('&');
|
||||
opts.uri = parts[0] + '?' + qs;
|
||||
|
||||
}
|
||||
}
|
||||
xhr(opts, function (err, res, body) {
|
||||
if (err) log.error(err);
|
||||
|
@ -50,6 +57,9 @@ var Api = {
|
|||
if (typeof jar.get('scratchlanguage') !== 'undefined') {
|
||||
opts.headers['Accept-Language'] = jar.get('scratchlanguage') + ', en;q=0.8';
|
||||
}
|
||||
if (opts.authentication) {
|
||||
opts.headers['X-Token'] = opts.authentication;
|
||||
}
|
||||
if (opts.useCsrf) {
|
||||
jar.use('scratchcsrftoken', '/csrf_token/', function (err, csrftoken) {
|
||||
if (err) return log.error('Error while retrieving CSRF token', err);
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
var keyMirror = require('keymirror');
|
||||
var api = require('../mixins/api.jsx').api;
|
||||
var jar = require('../lib/jar.js');
|
||||
|
||||
var Types = keyMirror({
|
||||
REFRESH_SESSION: null,
|
||||
SET_SESSION: null,
|
||||
SET_SESSION_ERROR: null
|
||||
SET_SESSION_ERROR: null,
|
||||
SET_TOKEN: null,
|
||||
SET_TOKEN_ERROR: null,
|
||||
USE_TOKEN: null
|
||||
});
|
||||
|
||||
var Actions = {
|
||||
|
@ -36,12 +39,45 @@ var Actions = {
|
|||
if (body.banned) {
|
||||
return window.location = url;
|
||||
} else {
|
||||
return dispatch(Actions.setSession(body));
|
||||
dispatch(Actions.getToken());
|
||||
dispatch(Actions.setSession(body));
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
getToken: function () {
|
||||
return function (dispatch) {
|
||||
jar.get('scratchsessionsid', function (err, value) {
|
||||
if (err) return dispatch(Actions.setTokenError(err));
|
||||
jar.unsign(value, function (err, contents) {
|
||||
if (err) return dispatch(Actions.setTokenError(err));
|
||||
try {
|
||||
var sessionData = JSON.parse(contents);
|
||||
} catch (err) {
|
||||
return dispatch(Actions.setTokenError(err));
|
||||
}
|
||||
return dispatch(Actions.setToken(sessionData.token));
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setToken: function (token) {
|
||||
return {
|
||||
type: Types.SET_TOKEN,
|
||||
token: token
|
||||
};
|
||||
},
|
||||
|
||||
setTokenError: function (error) {
|
||||
return {
|
||||
type: Types.SET_SESSION_ERROR,
|
||||
error: error
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Actions;
|
||||
|
|
|
@ -5,7 +5,6 @@ var actionTypes = require('./actions.js').types;
|
|||
|
||||
var sessionReducer = function (state, action) {
|
||||
// Reducer for handling changes to session state
|
||||
|
||||
if (typeof state === 'undefined') {
|
||||
state = {};
|
||||
}
|
||||
|
@ -20,8 +19,25 @@ var sessionReducer = function (state, action) {
|
|||
}
|
||||
};
|
||||
|
||||
var tokenReducer = function (state, action) {
|
||||
// Reducer for updating the api token
|
||||
if (typeof state === 'undefined') {
|
||||
state = '';
|
||||
}
|
||||
switch (action.type) {
|
||||
case actionTypes.SET_TOKEN:
|
||||
return action.token;
|
||||
case actionTypes.SET_TOKEN_ERROR:
|
||||
// TODO: do something with the error
|
||||
return state;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
var appReducer = combineReducers({
|
||||
session: sessionReducer
|
||||
session: sessionReducer,
|
||||
token: tokenReducer
|
||||
});
|
||||
|
||||
module.exports = appReducer;
|
||||
|
|
6
static/js/lib/polyfill.min.js
vendored
6
static/js/lib/polyfill.min.js
vendored
|
@ -26,6 +26,12 @@
|
|||
*/
|
||||
(function(){try{new e("test")}catch(t){var e=function(t,e){var n;return e=e||{bubbles:!1,cancelable:!1,detail:void 0},n=document.createEvent("CustomEvent"),n.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),n};e.prototype=window.Event.prototype,window.CustomEvent=e}})();
|
||||
|
||||
/*!
|
||||
* https://github.com/davidchambers/Base64.js
|
||||
* see https://github.com/davidchambers/Base64.js/blob/master/LICENSE
|
||||
*/
|
||||
!function(){function t(t){this.message=t}var r="undefined"!=typeof exports?exports:this,e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";t.prototype=new Error,t.prototype.name="InvalidCharacterError",r.btoa||(r.btoa=function(r){for(var o,n,a=String(r),i=0,c=e,d="";a.charAt(0|i)||(c="=",i%1);d+=c.charAt(63&o>>8-i%1*8)){if(n=a.charCodeAt(i+=.75),n>255)throw new t("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");o=o<<8|n}return d}),r.atob||(r.atob=function(r){var o=String(r).replace(/=+$/,"");if(o.length%4==1)throw new t("'atob' failed: The string to be decoded is not correctly encoded.");for(var n,a,i=0,c=0,d="";a=o.charAt(c++);~a&&(n=i%4?64*n+a:a,i++%4)?d+=String.fromCharCode(255&n>>(-2*i&6)):0)a=e.indexOf(a);return d})}();
|
||||
|
||||
/*!
|
||||
* https://github.com/andyearnshaw/Intl.js
|
||||
* @license The MIT License (MIT) Copyright (c) 2013 Andy Earnshaw
|
||||
|
|
Loading…
Add table
Reference in a new issue