When configuring scripts it is possible to make REST calls to (external) web services in order to return values (such as IP addresses or true/false).
When this type of call is made within a Condition's access criteria or Policy's assignment expressions it is not intended as a means of setting a claim value within the AppGate ZTNA system, it should just return true/false.
When this type of call is made within a user claim scripts, then the returned claim can subsequently be used in criteria expressions in both Controllers and Gateways.
The JavaScript engine used in AppGate ZTNA is a modified version of Oracle's nashorn:
Functions available
The use of http calls and http redirections are not permitted. Only https can be used by default. If the use of http is required then this can be enabled using a cz-config command. Since this engine is run in a sandbox, it does not provide XMLHttpRequest object. Instead, you can use following REST helpers to make calls:
// conditions and policy assignment can use helper methods to interact with third party REST servers.
// get request functions
httpGet(url)
httpGet(url, headers)
httpGet(url, headers, timeout)
// post request functions
httpPost(url, data)
httpPost(url, data, contentType)
httpPost(url, data, contentType, headers)
httpPost(url, data, contentType, headers, timeout)
// put request functions
httpPut(url, data)sc
httpPut(url, data, contentType)
httpPut(url, data, contentType, headers)
httpPut(url, data, contentType, headers, timeout)
// delete request functions
httpDelete(url)
httpDelete(url, headers)
httpDelete(url, headers, timeout)
// printing output in "Test" mode
console.log(message)
// default values
contentType = application/JSON
timeout = 3000
headers = no value
// creating custom headers
var headers = [
{'key':'header-name1', 'value':'header-value1'},
{'key':'header-name2', 'value':'header-value2'},
];Cache functions
There is a caching feature that allows results to be re-used without making repeat https calls. The caching methods work both in Controller (user and criteria scripts) as well as the Gateway (condition and entitlement scripts). The cache is always local to each appliance and is shared with all users. So if the first user on a Gateway fills the object, subsequent users can use the same object.
You can also use the username/group claim.objectX to cache objects per user or per groups of users for example.
There is a cache.get and a cache.put method. The cache is stored as (key, value, timestamp). If the value is not there or it has expired the function will be used to retrieve the object.
Currently there is no command to clear the cache, but it will be reevaluted when the script is rerun.
// cache functions
cache.get(key, functionToGetIfItsNotInCache) // will pass 1 day validity
cache.get(key, functionToGetIfItsNotInCache, validityDurationSeconds)
cache.getIfPresent(key) // will return the cached value if it hasn't expired. Otherwise null
cache.put(key, value) // will pass 1 day validity
cache.put(key, value, validityDurationSeconds)Code Examples
Check the user is 'test' with a: +44 phone number and an external IP from from GB
var result;
var location = httpGet("https://ipinfo.io/" + claims.system.clientSrcIP + "/country").data;
console.log("resolved location: " + location);
result = claims.user.username === "test";
result = result && claims.user.phone && claims.user.phone.toString().startsWith("+44");
result = result && location == "GB";
return result;Query controller using rest api
// example code: =============
var controllerUrl = 'https://controller.company.com:8443'; // admin interface or the controller(s)
var headers = [
{'key':'Accept', 'value':'application/vnd.appgate.peer-v18+JSON'},
];
// the api user must be extempt from Admin-OTP requirement in Global Settings
var signinData = {
'machineId': 'f0031c00-0522-43b3-a642-ae23cfd1bc22',
'providerName': 'local',
'username': 'api-username',
'password': 'api-user-password'
};
var signinReq = httpPost(controllerUrl + '/admin/login', JSON.stringify(signinData), 'application/JSON', headers);
if(signinReq.statusCode != 200){
console.log('ERROR during sign-in: ' + signinReq.statusCode);
return false;
}
// add authorization token to the headers
headers.push({'key': 'authorization', 'value': 'Bearer ' + JSON.parse(signinReq.data).token});
// now any controller rest interface can be used directly
// for example, fetch tun IPs
var tunIpReq = httpGet(controllerUrl + '/admin/ip-pools/allocated-ips/by-dn/OU=local', headers);
if(tunIpReq.statusCode != 200) {
console.log('ERROR during query: ' + tunIpReq.statusCode);
return false;
}
// set allocations variable to data object in JSON result
var allocations = JSON.parse(tunIpReq.data).data;
// print the ipAddress for each allocation
allocations.forEach((x, i) => console.log(x.ipAddress))
return false;Using the cache to get github IPs once a week
return cache.get('github IPs', // name of the cache entry
() => JSON.parse(httpGet('https://api.github.com/meta').data).api, // if the IPs are not present in the cache or have expired, this is a function to get them and place them to the cache.
60 * 60 * 24 * 7); // how long the entry should be kept in the cache (7 days)Download endpoints from a JSON file for entitlement scripts and cache the results
// This script will download endpoints from a JSON file for entitlement scripts and cache the results in a more advanced way to avoid making excessive HTTP calls but also making sure the script gets up-to-date lists.
// The script downloads the file and puts it to the cache for 24 hours, but it aims to update the information every 15 minutes.
// If the script fails to download the file from the server after 15 minutes, it will use the information from the 24 hours cache.
// If it succeeds, it updates the cache.
const fieldName = "File Server";
const JSONUrl = "https://myserver.com/api/static/scripted_file_server.JSON";
const cutOffMinutes = 15;
const cacheMinutes = 24 * 60;
const lastUpdated = cache.getIfPresent(fieldName + "_lastupdated") || false;
// use the cache if it is newer than cutOffMinutes
if (lastUpdated && (new Date() - lastUpdated) < (cutOffMinutes * 60000)) {
return cache.getIfPresent(fieldName) || [];
}
// if cache is older than cutOffMinutes, fetch again
let response = httpGet(JSONUrl, 1500);
if (response && response.statusCode == 200) {
const endpoints = JSON.parse(response.data);
// store the value in cache for 24 hours
cache.put(fieldName, endpoints[fieldName], 60 * cacheMinutes);
cache.put(fieldName + "_lastupdated", new Date());
}
return cache.getIfPresent(fieldName) || [];Scripts may request information from other systems that require credentials (which will have to be included in the JavaScript). Scripts used in Entitlements and Conditions are therefore passed in the encrypted portion of the Entitlement token.
Any script provided via AppGate Professional Service team or as example scripts from the manual are delivered as is, without warranty of any kind. AppGate disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. Any maintenance needs arising out of the use or performance of these sample scripts (and related documentation) remains the responsibility of the customer.