Hi to all,
Another piece of code for you Knack fellows!
Here's how to lock down your entire App or specific Scene(s) to a list of whitelisted IPs. I know there's the ablity in Knack to restrict ths whole App to some IPs, but this is not flexible enough for me. I need to have complete control over each Scene individually if I want to. This also enables me to decide if I want to apply the IP rule to all Accounts, or exclude some users like myself as a roaming developer who can be anywhere in the world. My IP changes all the time, so this is beneficial.
Here's the code, enjoy!
Norm
//Example showing how to lock an app or scene to a specific set of public IPs.
const MY_SCENE_1 = 'scene_123';
const MY_SCENE_2 = 'scene_456';
var authorizedIPs = ['111.112.113.114', '221.222.223.224']; //For example, these could be the official customer's IP and the developper's IP.
///////////////////////////////////////////////////
$(document).on('knack-scene-render.any', function (event, scene) {
//Here, you can filter for a specific set of scenes.
if (scene.key === MY_SCENE_1 || scene.key === MY_SCENE_2) {
getPublicIP()
.then(function (publicIP) {
console.log('IP has a valid format: ' + publicIP);
//Check if app can run from this machine.
if (authorizedIPs.includes(publicIP) /*|| or logged-in account is a priviledged power-user*/) {
console.log('Ok to run from this public IP.');//$$$
} else {
console.log('You do not have authorization to run from this public IP.');//$$$
/* Normally, you want to redirect to an error page in Knack. Here, the URL is simply: access-denied.
ex: https://mycompany.knack.com/myapp#access-denied/
Use a no-login blank page with Rich Text that states the cause of error. */
var index = window.location.href.indexOf('#');
var newlink = window.location.href.slice(0, index + 1) + 'access-denied/';
window.open(newlink, '_self');
}
}).catch(function (publicIP) {
console.log('IP has an invalid format: ' + publicIP);
});
}
});
//////////////////////////////////////////////////////////////////
function getPublicIP() {
return new Promise(function (resolve, reject) {
$.get('https://www.cloudflare.com/cdn-cgi/trace', function (data) {
var index = data.indexOf('ip=') + 3;
var publicIP = data.substr(index);
index = publicIP.indexOf('\n');
publicIP = publicIP.substr(0, index);
if (ipFormatOk(publicIP))
resolve(publicIP);
else
reject(publicIP);
});
});
}
//////////////////////////////////////////////////////////////////
function ipFormatOk(ipAddress) {
var ipFormat = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
if (ipAddress.match(ipFormat))
return true;
else
return false;
}