pixelplanet/src/core/isAllowed.js

204 lines
4.6 KiB
JavaScript

/*
* decide if IP is allowed
* does proxycheck and check bans and whitelists
*/
import { getIPv6Subnet } from '../utils/ip';
import whois from '../utils/whois';
import ProxyCheck from '../utils/ProxyCheck';
import { IPInfo } from '../data/sql';
import { isIPBanned } from '../data/sql/Ban';
import { isWhitelisted } from '../data/sql/Whitelist';
import {
cacheAllowed,
getCacheAllowed,
} from '../data/redis/isAllowedCache';
import { proxyLogger as logger } from './logger';
import { USE_PROXYCHECK, PROXYCHECK_KEY } from './config';
// checker for IP address validity (proxy or vpn or not)
let checker = () => ({ allowed: true, status: 0, pcheck: 'dummy' });
// checker for mail address (disposable or not)
let mailChecker = () => false;
if (USE_PROXYCHECK && PROXYCHECK_KEY) {
const pc = new ProxyCheck(PROXYCHECK_KEY, logger);
checker = pc.checkIp;
mailChecker = pc.checkEmail;
}
/*
* save information of ip into database
*/
async function saveIPInfo(ip, whoisRet, allowed, info) {
try {
await IPInfo.upsert({
...whoisRet,
ip,
proxy: allowed,
pcheck: info,
});
} catch (error) {
logger.error(`Error whois for ${ip}: ${error.message}`);
}
}
/*
* execute proxycheck and blacklist whitelist check
* @param f proxycheck function
* @param ip full ip
* @param ipKey
* @return [ allowed, status, pcheck capromise]
*/
async function checkPCAndLists(f, ip, ipKey) {
let allowed = true;
let status = -2;
let pcheck = null;
try {
if (await isWhitelisted(ipKey)) {
allowed = true;
pcheck = 'wl';
status = -1;
} else if (await isIPBanned(ipKey)) {
allowed = false;
pcheck = 'bl';
status = 2;
} else {
const res = await f(ip);
status = res.status;
allowed = res.allowed;
pcheck = res.pcheck;
}
} catch (err) {
logger.error(`Error checkAllowed for ${ip}: ${err.message}`);
}
const caPromise = cacheAllowed(ipKey, status);
return [allowed, status, pcheck, caPromise];
}
/*
* execute proxycheck and whois and save result into cache
* @param f function for checking if proxy
* @param ip IP to check
* @return checkifAllowed return
*/
async function withoutCache(f, ip, ipKey) {
const [
[allowed, status, pcheck, caPromise],
whoisRet,
] = await Promise.all([
checkPCAndLists(f, ip, ipKey),
whois(ip),
]);
await Promise.all([
caPromise,
saveIPInfo(ipKey, whoisRet, status, pcheck),
]);
return {
allowed,
status,
};
}
/*
* Array of running ip checks
* [
* [ipKey, promise],
* [ipKey2, promise2],
* ...
* ]
*/
const checking = [];
/*
* Execute proxycheck and whois and save result into cache
* If IP is already getting checked, reuse its request
* @param ip ip to check
* @return checkIfAllowed return
*/
async function withoutCacheButReUse(f, ip, ipKey) {
const runReq = checking.find((q) => q[0] === ipKey);
if (runReq) {
return runReq[1];
}
const promise = withoutCache(f, ip, ipKey);
checking.push([ipKey, promise]);
const result = await promise;
checking.splice(
checking.findIndex((q) => q[0] === ipKey),
1,
);
return result;
}
/*
* execute proxycheck, don't wait, return cache if exists or
* status -2 if currently checking
* @param f function for checking if proxy
* @param ip IP to check
* @return Object as in checkIfAllowed
* @return true if proxy or blacklisted, false if not or whitelisted
*/
async function withCache(f, ip, ipKey) {
const runReq = checking.find((q) => q[0] === ipKey);
if (!runReq) {
const cache = await getCacheAllowed(ipKey);
if (cache) {
return cache;
}
withoutCacheButReUse(f, ip, ipKey);
}
return {
allowed: true,
status: -2,
};
}
/*
* check if ip is allowed
* @param ip IP
* @param disableCache if we fetch result from cache
* @return Promise {
* allowed: boolean if allowed to use site
* , status: -2: not yet checked
* -1: whitelisted
* 0: allowed, no proxy
* 1 is proxy
* 2: is banned
* 3: is rangebanned
* 4: invalid ip
* }
*/
export default function checkIfAllowed(ip, disableCache = false) {
if (!ip || ip === '0.0.0.1') {
return {
allowed: false,
status: 4,
};
}
const ipKey = getIPv6Subnet(ip);
if (disableCache) {
return withoutCacheButReUse(checker, ip, ipKey);
}
return withCache(checker, ip, ipKey);
}
/*
* check if email is disposable
* @param email
* @return Promise
* null: some error occurred
* false: legit provider
* true: disposable
*/
export function checkIfMailDisposable(email) {
return mailChecker(email);
}