diff --git a/package-lock.json b/package-lock.json index 865cd8b0e..54f7258af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6491,9 +6491,9 @@ } }, "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" }, "is-accessor-descriptor": { "version": "0.1.6", @@ -10459,6 +10459,13 @@ "requires": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + } } }, "pseudomap": { diff --git a/package.json b/package.json index fd113615c..431694700 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "express-openapi-validator": "^4.10.8", "github-download-directory": "^2.0.0", "ioredis": "^4.17.3", + "ipaddr.js": "^2.0.1", "js-yaml": "^3.13.1", "json-merger": "^1.1.7", "jsonwebtoken": "^8.5.1", diff --git a/src/utils/authMiddlewares.js b/src/utils/authMiddlewares.js index f912e7301..b6728aec6 100644 --- a/src/utils/authMiddlewares.js +++ b/src/utils/authMiddlewares.js @@ -9,6 +9,7 @@ const jwtExpress = require('express-jwt'); const jwkToPem = require('jwk-to-pem'); const util = require('util'); const dns = require('dns').promises; +const ipaddr = require('ipaddr.js'); const config = require('../config'); @@ -98,20 +99,36 @@ const checkAuthExpiredMiddleware = (req, res, next) => { return true; } + console.log('isReqFromLocalhost throwing error'); throw new Error('ip address is not localhost'); }; const isReqFromCluster = async () => { - console.log('isReqFromCluster'); - const domains = await dns.reverse(req.ip); + console.log('isReqFromCluster ', req.ip); + + let remoteAddress = req.ip; + const addr = ipaddr.parse(req.ip); + // req.ip returns IPv4 addresses mapped to IPv6, e.g.: + // 127.0.0.1 (IPv4) -> ::ffff:127.0.0.1 (IPv6) + // dns.reverse is not capable of dealing with them, + // it either uses IPv4 or IPv6, so we need to map those + // IPs back to IPv4 before. + if (addr.kind() === 'ipv6' && addr.isIPv4MappedAddress()) { + remoteAddress = addr.toIPv4Address().toString(); + } + + console.log('what now IP ', remoteAddress); + + const domains = await dns.reverse(remoteAddress); console.log('isReqFromCluster domains ', domains); - if (!domains.some((domain) => INTERNAL_DOMAINS_REGEX.test(domain))) { + if (domains.some((domain) => INTERNAL_DOMAINS_REGEX.test(domain))) { console.log('isReqFromCluster throwing error'); - throw new Error('ip address does not come from internal sources'); + return true; } - return true; + console.log('isReqFromCluster throwing error'); + throw new Error('ip address does not come from internal sources'); }; console.log('lcs [URL,METHOD]: ', req.method.toLowerCase(), req.url); @@ -125,16 +142,20 @@ const checkAuthExpiredMiddleware = (req, res, next) => { // JWT `exp` returns seconds since UNIX epoch, conver to milliseconds for this const timeLeft = (req.user.exp * 1000) - Date.now(); - // ignore if JWT is still valid - if (timeLeft > 0) { - return next(); - } + // temporarily ignore valid token to debug patch cellsets + if (!(req.url.includes('cellSets') && req.method.toLowerCase() === 'patch')) { + // ignore if JWT is still valid + if (timeLeft > 0) { + return next(); + } - console.log('lcs time left in token ', timeLeft); - // send error if JWT is older than the limit - if (timeLeft < -(7 * 1000 * 60 * 60)) { - console.log('lcs rejecting very expired token'); - return next(new UnauthenticatedError('token has expired')); + + console.log('lcs time left in token ', timeLeft); + // send error if JWT is older than the limit + if (timeLeft < -(7 * 1000 * 60 * 60)) { + console.log('lcs rejecting very expired token'); + return next(new UnauthenticatedError('token has expired')); + } } // check if we should ignore expired jwt token for this path and request type @@ -156,8 +177,9 @@ const checkAuthExpiredMiddleware = (req, res, next) => { .then(() => { next(); }) - .catch(() => { - next(new UnauthenticatedError('token has expired')); + .catch((e) => { + console.log('lcs error in promise any: ', e); + next(new UnauthenticatedError(`invalid request origin ${e}`)); }); return null; diff --git a/tests/utils/authMiddlewares.test.js b/tests/utils/authMiddlewares.test.js index 0f0889764..ca0a5fd32 100644 --- a/tests/utils/authMiddlewares.test.js +++ b/tests/utils/authMiddlewares.test.js @@ -1,5 +1,6 @@ const AWSMock = require('aws-sdk-mock'); const { + checkAuthExpiredMiddleware, expressAuthorizationMiddleware, authorize, } = require('../../src/utils/authMiddlewares'); @@ -52,6 +53,21 @@ describe('Tests for authorization/authentication middlewares', () => { expect(next).toBeCalledWith(); }); + // it('Express middleware can check expired auth', async () => { + // mockDynamoGetItem(data); + + // const req = { + // params: { experimentId: fake.EXPERIMENT_ID }, + // user: fake.USER, + // url: `/experiments/${fake.EXPERIMENT_ID}/cellSets`, + // method: 'PATCH', + // }; + // const next = jest.fn(); + + // await checkAuthExpiredMiddleware(req, {}, next); + // expect(next).toBeCalledWith(); + // }); + it('Express middleware can reject incorrect users', async () => { mockDynamoGetItem({});