fix(csrf): remove all csrf bypass
This commit is contained in:
committed by
mrugesh
parent
f183df0d88
commit
23b899f50f
@ -37,7 +37,7 @@ export default function bootCertificate(app) {
|
|||||||
api.put('/certificate/verify', ifNoUser401, ifNoSuperBlock404, verifyCert);
|
api.put('/certificate/verify', ifNoUser401, ifNoSuperBlock404, verifyCert);
|
||||||
api.get('/certificate/showCert/:username/:cert', showCert);
|
api.get('/certificate/showCert/:username/:cert', showCert);
|
||||||
|
|
||||||
app.use('/internal', api);
|
app.use(api);
|
||||||
}
|
}
|
||||||
|
|
||||||
const noNameMessage = dedent`
|
const noNameMessage = dedent`
|
||||||
|
@ -61,7 +61,6 @@ export default async function bootChallenge(app, done) {
|
|||||||
router.get('/map', redirectToLearn);
|
router.get('/map', redirectToLearn);
|
||||||
|
|
||||||
app.use(api);
|
app.use(api);
|
||||||
app.use('/internal', api);
|
|
||||||
app.use(router);
|
app.use(router);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
@ -319,7 +319,6 @@ export default function donateBoot(app, done) {
|
|||||||
api.post('/update-paypal', updatePaypal);
|
api.post('/update-paypal', updatePaypal);
|
||||||
donateRouter.use('/donate', api);
|
donateRouter.use('/donate', api);
|
||||||
app.use(donateRouter);
|
app.use(donateRouter);
|
||||||
app.use('/internal', donateRouter);
|
|
||||||
connectToStripe().then(done);
|
connectToStripe().then(done);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ const githubSecret = process.env.GITHUB_SECRET;
|
|||||||
|
|
||||||
module.exports = function(app) {
|
module.exports = function(app) {
|
||||||
const router = app.loopback.Router();
|
const router = app.loopback.Router();
|
||||||
const api = app.loopback.Router();
|
|
||||||
const User = app.models.User;
|
const User = app.models.User;
|
||||||
|
|
||||||
router.get('/api/github', githubCalls);
|
router.get('/api/github', githubCalls);
|
||||||
@ -22,14 +21,12 @@ module.exports = function(app) {
|
|||||||
);
|
);
|
||||||
router.get('/unsubscribed/:unsubscribeId', unsubscribedWithId);
|
router.get('/unsubscribed/:unsubscribeId', unsubscribedWithId);
|
||||||
router.get('/unsubscribed', unsubscribed);
|
router.get('/unsubscribed', unsubscribed);
|
||||||
api.get('/resubscribe/:unsubscribeId', resubscribe);
|
router.get('/resubscribe/:unsubscribeId', resubscribe);
|
||||||
router.get('/nonprofits', nonprofits);
|
router.get('/nonprofits', nonprofits);
|
||||||
router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
|
router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
|
||||||
|
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
||||||
app.use('/internal', api);
|
|
||||||
|
|
||||||
function theFastestWebPageOnTheInternet(req, res) {
|
function theFastestWebPageOnTheInternet(req, res) {
|
||||||
res.render('resources/the-fastest-web-page-on-the-internet', {
|
res.render('resources/the-fastest-web-page-on-the-internet', {
|
||||||
title: 'This is the fastest web page on the internet'
|
title: 'This is the fastest web page on the internet'
|
||||||
|
@ -2,5 +2,4 @@ module.exports = function mountRestApi(app) {
|
|||||||
const restApi = app.loopback.rest();
|
const restApi = app.loopback.rest();
|
||||||
const restApiRoot = app.get('restApiRoot');
|
const restApiRoot = app.get('restApiRoot');
|
||||||
app.use(restApiRoot, restApi);
|
app.use(restApiRoot, restApi);
|
||||||
app.use(`/internal${restApiRoot}`, restApi);
|
|
||||||
};
|
};
|
||||||
|
@ -48,7 +48,6 @@ export default function settingsController(app) {
|
|||||||
api.put('/update-my-username', ifNoUser401, updateMyUsername);
|
api.put('/update-my-username', ifNoUser401, updateMyUsername);
|
||||||
api.put('/update-user-flag', ifNoUser401, updateUserFlag);
|
api.put('/update-user-flag', ifNoUser401, updateUserFlag);
|
||||||
|
|
||||||
app.use('/internal', api);
|
|
||||||
app.use(api);
|
app.use(api);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ function bootUser(app) {
|
|||||||
api.post('/account/reset-progress', ifNoUser401, postResetProgress);
|
api.post('/account/reset-progress', ifNoUser401, postResetProgress);
|
||||||
api.post('/user/report-user/', ifNoUser401, postReportUserProfile);
|
api.post('/user/report-user/', ifNoUser401, postReportUserProfile);
|
||||||
|
|
||||||
app.use('/internal', api);
|
app.use(api);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createReadSessionUser(app) {
|
function createReadSessionUser(app) {
|
||||||
|
@ -6,10 +6,9 @@ export default function() {
|
|||||||
domain: process.env.COOKIE_DOMAIN || 'localhost'
|
domain: process.env.COOKIE_DOMAIN || 'localhost'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Note: paypal webhook goes through /internal
|
|
||||||
return function csrf(req, res, next) {
|
return function csrf(req, res, next) {
|
||||||
const path = req.path.split('/')[1];
|
const path = req.path.split('/')[1];
|
||||||
if (/(^api$|^unauthenticated$|^internal$|^p$)/.test(path)) {
|
if (/^donate\/update-paypal$/.test(path)) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
return protection(req, res, next);
|
return protection(req, res, next);
|
||||||
|
@ -11,19 +11,23 @@ import { jwtSecret as _jwtSecret } from '../../../config/secrets';
|
|||||||
|
|
||||||
import { wrapHandledError } from '../utils/create-handled-error';
|
import { wrapHandledError } from '../utils/create-handled-error';
|
||||||
|
|
||||||
// We need to tunnel through a proxy path set up within
|
const newsShortLinksRE = /^\/n\/|^\/p\//;
|
||||||
// the gatsby app, at this time, that path is /internal
|
const showCertRE = /^\/certificate\/showCert\//;
|
||||||
const apiProxyRE = /^\/internal\/|^\/external\//;
|
const updatePaypalRE = /^\/donate\/update-paypal/;
|
||||||
const newsShortLinksRE = /^\/internal\/n\/|^\/internal\/p\?/;
|
// signin may not have a trailing slash
|
||||||
const loopbackAPIPathRE = /^\/internal\/api\//;
|
const signinRE = /^\/signin/;
|
||||||
const showCertRe = /^\/internal\/certificate\/showCert\//;
|
const unsubscribeRE = /^\/u\/|^\/unsubscribe\/|^\/ue\//;
|
||||||
const updatePaypalRe = /^\/internal\/donate\/update-paypal/;
|
const unsubscribedRE = /^\/unsubscribed\//;
|
||||||
|
const resubscribeRE = /^\/resubscribe\//;
|
||||||
|
|
||||||
const _whiteListREs = [
|
const _whiteListREs = [
|
||||||
newsShortLinksRE,
|
newsShortLinksRE,
|
||||||
loopbackAPIPathRE,
|
showCertRE,
|
||||||
showCertRe,
|
updatePaypalRE,
|
||||||
updatePaypalRe
|
signinRE,
|
||||||
|
unsubscribeRE,
|
||||||
|
unsubscribedRE,
|
||||||
|
resubscribeRE
|
||||||
];
|
];
|
||||||
|
|
||||||
export function isWhiteListedPath(path, whiteListREs = _whiteListREs) {
|
export function isWhiteListedPath(path, whiteListREs = _whiteListREs) {
|
||||||
@ -33,7 +37,7 @@ export function isWhiteListedPath(path, whiteListREs = _whiteListREs) {
|
|||||||
export default ({ jwtSecret = _jwtSecret, getUserById = _getUserById } = {}) =>
|
export default ({ jwtSecret = _jwtSecret, getUserById = _getUserById } = {}) =>
|
||||||
function requestAuthorisation(req, res, next) {
|
function requestAuthorisation(req, res, next) {
|
||||||
const { path } = req;
|
const { path } = req;
|
||||||
if (apiProxyRE.test(path) && !isWhiteListedPath(path)) {
|
if (!isWhiteListedPath(path)) {
|
||||||
const { accessToken, error, jwt } = getAccessTokenFromRequest(
|
const { accessToken, error, jwt } = getAccessTokenFromRequest(
|
||||||
req,
|
req,
|
||||||
jwtSecret
|
jwtSecret
|
||||||
|
@ -64,7 +64,7 @@ describe('request-authorization', () => {
|
|||||||
describe('cookies', () => {
|
describe('cookies', () => {
|
||||||
it('throws when no access token is present', () => {
|
it('throws when no access token is present', () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const req = mockReq({ path: '/internal/some-path/that-needs/auth' });
|
const req = mockReq({ path: '/some-path/that-needs/auth' });
|
||||||
const res = mockRes();
|
const res = mockRes();
|
||||||
const next = sinon.spy();
|
const next = sinon.spy();
|
||||||
expect(() => requestAuthorization(req, res, next)).toThrowError(
|
expect(() => requestAuthorization(req, res, next)).toThrowError(
|
||||||
@ -77,7 +77,7 @@ describe('request-authorization', () => {
|
|||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const invalidJWT = jwt.sign({ accessToken }, invalidJWTSecret);
|
const invalidJWT = jwt.sign({ accessToken }, invalidJWTSecret);
|
||||||
const req = mockReq({
|
const req = mockReq({
|
||||||
path: '/internal/some-path/that-needs/auth',
|
path: '/some-path/that-needs/auth',
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
cookie: { jwt_access_token: invalidJWT }
|
cookie: { jwt_access_token: invalidJWT }
|
||||||
});
|
});
|
||||||
@ -97,7 +97,7 @@ describe('request-authorization', () => {
|
|||||||
validJWTSecret
|
validJWTSecret
|
||||||
);
|
);
|
||||||
const req = mockReq({
|
const req = mockReq({
|
||||||
path: '/internal/some-path/that-needs/auth',
|
path: '/some-path/that-needs/auth',
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
cookie: { jwt_access_token: invalidJWT }
|
cookie: { jwt_access_token: invalidJWT }
|
||||||
});
|
});
|
||||||
@ -114,7 +114,7 @@ describe('request-authorization', () => {
|
|||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
||||||
const req = mockReq({
|
const req = mockReq({
|
||||||
path: '/internal/some-path/that-needs/auth',
|
path: '/some-path/that-needs/auth',
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
cookie: { jwt_access_token: validJWT }
|
cookie: { jwt_access_token: validJWT }
|
||||||
});
|
});
|
||||||
@ -130,7 +130,7 @@ describe('request-authorization', () => {
|
|||||||
it('adds the jwt to the headers', async done => {
|
it('adds the jwt to the headers', async done => {
|
||||||
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
||||||
const req = mockReq({
|
const req = mockReq({
|
||||||
path: '/internal/some-path/that-needs/auth',
|
path: '/some-path/that-needs/auth',
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
cookie: { jwt_access_token: validJWT }
|
cookie: { jwt_access_token: validJWT }
|
||||||
});
|
});
|
||||||
@ -142,7 +142,8 @@ describe('request-authorization', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('calls next if request does not require authorization', async () => {
|
it('calls next if request does not require authorization', async () => {
|
||||||
const req = mockReq({ path: '/unauthenticated/another/route' });
|
// currently /unsubscribe does not require authorization
|
||||||
|
const req = mockReq({ path: '/unsubscribe/another/route' });
|
||||||
const res = mockRes();
|
const res = mockRes();
|
||||||
const next = sinon.spy();
|
const next = sinon.spy();
|
||||||
await requestAuthorization(req, res, next);
|
await requestAuthorization(req, res, next);
|
||||||
@ -153,7 +154,7 @@ describe('request-authorization', () => {
|
|||||||
describe('Auth header', () => {
|
describe('Auth header', () => {
|
||||||
it('throws when no access token is present', () => {
|
it('throws when no access token is present', () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const req = mockReq({ path: '/internal/some-path/that-needs/auth' });
|
const req = mockReq({ path: '/some-path/that-needs/auth' });
|
||||||
const res = mockRes();
|
const res = mockRes();
|
||||||
const next = sinon.spy();
|
const next = sinon.spy();
|
||||||
expect(() => requestAuthorization(req, res, next)).toThrowError(
|
expect(() => requestAuthorization(req, res, next)).toThrowError(
|
||||||
@ -166,7 +167,7 @@ describe('request-authorization', () => {
|
|||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const invalidJWT = jwt.sign({ accessToken }, invalidJWTSecret);
|
const invalidJWT = jwt.sign({ accessToken }, invalidJWTSecret);
|
||||||
const req = mockReq({
|
const req = mockReq({
|
||||||
path: '/internal/some-path/that-needs/auth',
|
path: '/some-path/that-needs/auth',
|
||||||
headers: { 'X-fcc-access-token': invalidJWT }
|
headers: { 'X-fcc-access-token': invalidJWT }
|
||||||
});
|
});
|
||||||
const res = mockRes();
|
const res = mockRes();
|
||||||
@ -185,7 +186,7 @@ describe('request-authorization', () => {
|
|||||||
validJWTSecret
|
validJWTSecret
|
||||||
);
|
);
|
||||||
const req = mockReq({
|
const req = mockReq({
|
||||||
path: '/internal/some-path/that-needs/auth',
|
path: '/some-path/that-needs/auth',
|
||||||
headers: { 'X-fcc-access-token': invalidJWT }
|
headers: { 'X-fcc-access-token': invalidJWT }
|
||||||
});
|
});
|
||||||
const res = mockRes();
|
const res = mockRes();
|
||||||
@ -201,7 +202,7 @@ describe('request-authorization', () => {
|
|||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
||||||
const req = mockReq({
|
const req = mockReq({
|
||||||
path: '/internal/some-path/that-needs/auth',
|
path: '/some-path/that-needs/auth',
|
||||||
headers: { 'X-fcc-access-token': validJWT }
|
headers: { 'X-fcc-access-token': validJWT }
|
||||||
});
|
});
|
||||||
const res = mockRes();
|
const res = mockRes();
|
||||||
@ -216,7 +217,7 @@ describe('request-authorization', () => {
|
|||||||
it('adds the jwt to the headers', async done => {
|
it('adds the jwt to the headers', async done => {
|
||||||
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
const validJWT = jwt.sign({ accessToken }, validJWTSecret);
|
||||||
const req = mockReq({
|
const req = mockReq({
|
||||||
path: '/internal/some-path/that-needs/auth',
|
path: '/some-path/that-needs/auth',
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
cookie: { jwt_access_token: validJWT }
|
cookie: { jwt_access_token: validJWT }
|
||||||
});
|
});
|
||||||
@ -228,7 +229,8 @@ describe('request-authorization', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('calls next if request does not require authorization', async () => {
|
it('calls next if request does not require authorization', async () => {
|
||||||
const req = mockReq({ path: '/unauthenticated/another/route' });
|
// currently /unsubscribe does not require authorization
|
||||||
|
const req = mockReq({ path: '/unsubscribe/another/route' });
|
||||||
const res = mockRes();
|
const res = mockRes();
|
||||||
const next = sinon.spy();
|
const next = sinon.spy();
|
||||||
await requestAuthorization(req, res, next);
|
await requestAuthorization(req, res, next);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
Here's your sign in link. It will instantly sign you into freeCodeCamp.org - no password necessary:
|
Here's your sign in link. It will instantly sign you into freeCodeCamp.org - no password necessary:
|
||||||
|
|
||||||
<%= host %>/internal/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
<%= host %>/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
||||||
|
|
||||||
Note: this sign in link will expire after 15 minutes. If you need a new sign in link, go to https://www.freecodecamp.org/signin
|
Note: this sign in link will expire after 15 minutes. If you need a new sign in link, go to https://www.freecodecamp.org/signin
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ We have created a new account for you.
|
|||||||
|
|
||||||
Here's your sign in link. It will instantly sign you into freeCodeCamp.org - no password necessary:
|
Here's your sign in link. It will instantly sign you into freeCodeCamp.org - no password necessary:
|
||||||
|
|
||||||
<%= host %>/internal/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
<%= host %>/passwordless-auth/?email=<%= loginEmail %>&token=<%= loginToken %>
|
||||||
|
|
||||||
Note: this sign in link will expire after 15 minutes. If you need a new sign in link, go to https://www.freecodecamp.org/signin
|
Note: this sign in link will expire after 15 minutes. If you need a new sign in link, go to https://www.freecodecamp.org/signin
|
||||||
|
|
||||||
|
33
client/package-lock.json
generated
33
client/package-lock.json
generated
@ -8141,6 +8141,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
|
||||||
"integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
|
"integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
|
||||||
},
|
},
|
||||||
|
"csrf": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/csrf/-/csrf-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==",
|
||||||
|
"requires": {
|
||||||
|
"rndm": "1.2.0",
|
||||||
|
"tsscmp": "1.0.6",
|
||||||
|
"uid-safe": "2.1.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"css": {
|
"css": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
|
||||||
@ -22220,6 +22230,11 @@
|
|||||||
"ret": "~0.1.10"
|
"ret": "~0.1.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"random-bytes": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
|
||||||
|
},
|
||||||
"randombytes": {
|
"randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
@ -23989,6 +24004,11 @@
|
|||||||
"inherits": "^2.0.1"
|
"inherits": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rndm": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w="
|
||||||
|
},
|
||||||
"rst-selector-parser": {
|
"rst-selector-parser": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
|
||||||
@ -26173,6 +26193,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
||||||
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
||||||
},
|
},
|
||||||
|
"tsscmp": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="
|
||||||
|
},
|
||||||
"tsutils": {
|
"tsutils": {
|
||||||
"version": "3.17.1",
|
"version": "3.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
|
||||||
@ -26286,6 +26311,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz",
|
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz",
|
||||||
"integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g=="
|
"integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g=="
|
||||||
},
|
},
|
||||||
|
"uid-safe": {
|
||||||
|
"version": "2.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||||
|
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
|
||||||
|
"requires": {
|
||||||
|
"random-bytes": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"unc-path-regex": {
|
"unc-path-regex": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
"bezier-easing": "^2.1.0",
|
"bezier-easing": "^2.1.0",
|
||||||
"browser-cookies": "^1.2.0",
|
"browser-cookies": "^1.2.0",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
|
"csrf": "^3.1.0",
|
||||||
"date-fns": "^1.30.1",
|
"date-fns": "^1.30.1",
|
||||||
"entities": "^1.1.2",
|
"entities": "^1.1.2",
|
||||||
"enzyme": "^3.10.0",
|
"enzyme": "^3.10.0",
|
||||||
|
@ -31,7 +31,7 @@ function ShowUnsubscribed({ unsubscribeId }) {
|
|||||||
block={true}
|
block={true}
|
||||||
bsSize='lg'
|
bsSize='lg'
|
||||||
bsStyle='primary'
|
bsStyle='primary'
|
||||||
href={`${apiLocation}/internal/resubscribe/${unsubscribeId}`}
|
href={`${apiLocation}/resubscribe/${unsubscribeId}`}
|
||||||
>
|
>
|
||||||
You can click here to resubscribe
|
You can click here to resubscribe
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -115,6 +115,7 @@ export const preventProgressDonationRequests = createAction(
|
|||||||
|
|
||||||
export const onlineStatusChange = createAction(types.onlineStatusChange);
|
export const onlineStatusChange = createAction(types.onlineStatusChange);
|
||||||
|
|
||||||
|
// TODO: re-evaluate this since /internal is no longer used.
|
||||||
// `hardGoTo` is used to hit the API server directly
|
// `hardGoTo` is used to hit the API server directly
|
||||||
// without going through /internal
|
// without going through /internal
|
||||||
// used for things like /signin and /signout
|
// used for things like /signin and /signout
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import { apiLocation } from '../../config/env.json';
|
import { apiLocation } from '../../config/env.json';
|
||||||
|
import { _csrf } from '../redux/cookieValues';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import Tokens from 'csrf';
|
||||||
|
|
||||||
const base = apiLocation + '/internal';
|
const base = apiLocation;
|
||||||
const baseUnauthenticated = apiLocation + '/unauthenticated';
|
const tokens = new Tokens();
|
||||||
|
|
||||||
axios.defaults.withCredentials = true;
|
axios.defaults.withCredentials = true;
|
||||||
|
|
||||||
export function postUnauthenticated(path, body) {
|
// _csrf is passed to the client as a cookie. Tokens are sent back to the server
|
||||||
return axios.post(`${baseUnauthenticated}${path}`, body);
|
// via headers:
|
||||||
|
if (_csrf) {
|
||||||
|
axios.defaults.headers.post['CSRF-Token'] = tokens.create(_csrf);
|
||||||
|
axios.defaults.headers.put['CSRF-Token'] = tokens.create(_csrf);
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(path) {
|
function get(path) {
|
||||||
|
@ -10,10 +10,6 @@ exports[`createRedirects matches the snapshot 1`] = `
|
|||||||
https://freecodecamp-dev.netlify.com/* https://www.freecodecamp.dev/:splat 301!
|
https://freecodecamp-dev.netlify.com/* https://www.freecodecamp.dev/:splat 301!
|
||||||
https://freecodecamp-org.netlify.com/* https://www.freecodecamp.org/:splat 301!
|
https://freecodecamp-org.netlify.com/* https://www.freecodecamp.org/:splat 301!
|
||||||
|
|
||||||
|
|
||||||
#api redirect
|
|
||||||
/internal/* https://api.example.com/internal/:splat 200!
|
|
||||||
|
|
||||||
# pages
|
# pages
|
||||||
/about https://news.example.com/about 200
|
/about https://news.example.com/about 200
|
||||||
/academic-honesty https://news.example.com/academic-honesty 200
|
/academic-honesty https://news.example.com/academic-honesty 200
|
||||||
|
@ -31,10 +31,6 @@ const template = `
|
|||||||
https://freecodecamp-dev.netlify.com/* https://www.freecodecamp.dev/:splat 301!
|
https://freecodecamp-dev.netlify.com/* https://www.freecodecamp.dev/:splat 301!
|
||||||
https://freecodecamp-org.netlify.com/* https://www.freecodecamp.org/:splat 301!
|
https://freecodecamp-org.netlify.com/* https://www.freecodecamp.org/:splat 301!
|
||||||
|
|
||||||
|
|
||||||
#api redirect
|
|
||||||
/internal/* #{{API}}/internal/:splat 200!
|
|
||||||
|
|
||||||
# pages
|
# pages
|
||||||
/about #{{NEWS}}/about 200
|
/about #{{NEWS}}/about 200
|
||||||
/academic-honesty #{{NEWS}}/academic-honesty 200
|
/academic-honesty #{{NEWS}}/academic-honesty 200
|
||||||
|
@ -18,7 +18,7 @@ describe('createRedirects', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('replaces instances of `#{{...}}` with the locations provided', () => {
|
it('replaces instances of `#{{...}}` with the locations provided', () => {
|
||||||
expect.assertions(5);
|
expect.assertions(4);
|
||||||
|
|
||||||
const apiPlaceholderRE = /#\{\{API\}\}/;
|
const apiPlaceholderRE = /#\{\{API\}\}/;
|
||||||
const newsPlaceholderRE = /#\{\{NEWS\}\}/;
|
const newsPlaceholderRE = /#\{\{NEWS\}\}/;
|
||||||
@ -33,8 +33,7 @@ describe('createRedirects', () => {
|
|||||||
expect(hasNewsPlaceholder).toBe(false);
|
expect(hasNewsPlaceholder).toBe(false);
|
||||||
expect(hasForumPlaceholder).toBe(false);
|
expect(hasForumPlaceholder).toBe(false);
|
||||||
|
|
||||||
const { api, forumProxy } = testLocations;
|
const { forumProxy } = testLocations;
|
||||||
expect(redirects.includes(`${api}/internal/:splat`)).toBe(true);
|
|
||||||
expect(redirects.includes(`${forumProxy}`)).toBe(true);
|
expect(redirects.includes(`${forumProxy}`)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user