Name
sn_access_analyzer.AccessAnalyzerUtil
Description
No description available
Script
var AccessAnalyzerUtil = Class.create();
AccessAnalyzerUtil.prototype = {
sequence: 1,
scriptExecutionListForOperation: [],
TRUE_ACL_API_STATUS: "true",
FALSE_ACL_API_STATUS: "false",
NOT_EVALUATED_ACL_API_STATUS: "not_evaluated",
PASS_UI_STATUS: "passed",
BLOCK_UI_STATUS: "blocked",
SKIP_UI_STATUS: "skipped",
UNDEFINED: "undefined",
initialize: function() {},
saveRequest: function(requestObj) {
var requestGR = new GlideRecord("sn_access_analyzer_request");
requestGR.initialize();
requestGR.setValue("analyze_by", requestObj.analyze_by);
requestGR.setValue("user", requestObj.user);
requestGR.setValue("group", requestObj.group);
requestGR.setValue("role", requestObj.role);
requestGR.setValue("resource_type", requestObj.resource_type);
requestGR.setValue("target_table", requestObj.target_table);
requestGR.setValue("target_record", requestObj.target_record);
requestGR.setValue("target_field", requestObj.target_field);
requestGR.setValue("target_client_callable_script_include", requestObj.target_client_callable_script_include);
requestGR.setValue("target_rest_endpoint", requestObj.target_rest_endpoint);
requestGR.setValue("target_rest_endpoint_method", requestObj.target_rest_endpoint_method);
requestGR.setValue("target_ui_page", requestObj.target_ui_page);
requestGR.setValue("operations", requestObj.operations);
requestGR.setValue("short_description", requestObj.short_description);
requestGR.setValue("last_run", new GlideDateTime());
requestGR.insert();
return requestGR;
},
saveResult: function(resp, accessRequestGR) {
var apiResponse = JSON.parse(resp);
var operations = apiResponse.request.targetOperations;
var executionTime = accessRequestGR.last_run;
var accessRequestId = accessRequestGR.sys_id.toString();
for (var i = 0; i < operations.length; i++) {
this.sequence = 1; //reset for every operation
var operation = operations[i].toLowerCase();
var resultId = this._saveAccessResult(accessRequestGR, apiResponse, operation, executionTime);
this.scriptExecutionListForOperation = [];
this._saveAllAccessHandlerDebugLogs(apiResponse.securityDebug, operation, resultId, accessRequestId);
this._saveAllBRDebugLogs(apiResponse.securityDebug, operation, resultId, accessRequestId);
this._saveAllDFDebugLogs(apiResponse.securityDebug, operation, resultId, accessRequestId);
this._saveAllACLDebugLogs(apiResponse.securityDebug, operation, resultId, accessRequestId);
this._addScriptExecutionListToResult(resultId);
}
},
_getAdminOverrideApplied: function(apiResponse, operationDebug, accessRequestGR, operation) {
var prop = gs.getProperty('glide.security.admin.override.accessterm');
if (prop === 'true' || !operationDebug || !operationDebug.debugLogs)
return "n/a";
if (accessRequestGR.resource_type.toString() !== "record" || !apiResponse.request.targetRecord)
return "n/a";
var table = apiResponse.request.targetName;
var field = apiResponse.request.targetField;
var path = "record/" + table + (field ? "." + field : "") + "/" + operation;
for (var i = 0; i < operationDebug.debugLogs.length; i++) {
var debug = operationDebug.debugLogs[i];
if (debug && debug.path && debug.path === path)
return debug.isCumulativeOverride ? "yes" : "no";
}
return "n/a";
},
_saveAccessResult: function(accessRequestGR, apiResponse, operation, executionTime) {
var accessResultGR = new GlideRecord("sn_access_analyzer_access_result");
accessResultGR.initialize();
accessResultGR.setValue("access_analyzer_request", accessRequestGR.sys_id.toString());
accessResultGR.setValue("execution_time", executionTime);
accessResultGR.setValue("analyzed_by", accessRequestGR.analyzed_by);
accessResultGR.setValue("operation", operation);
accessResultGR.setValue("admin_override_applied", this._getAdminOverrideApplied(apiResponse, apiResponse.securityDebug[operation], accessRequestGR, operation));
this._populateAccessInfo(accessRequestGR, apiResponse, operation, accessResultGR);
return accessResultGR.insert();
},
// populates status for access handler, data filtration and acl
_populateAccessInfo: function(accessRequestGR, apiResponse, operation, accessResultGR) {
var accessInfo = apiResponse.securityDebug[operation];
accessResultGR.setValue("overall_access", accessInfo.status ? this.PASS_UI_STATUS : this.BLOCK_UI_STATUS);
if (accessRequestGR.resource_type.toString() === "record" && !gs.nil(accessRequestGR.target_field.toString())) {
var readOnlyField = apiResponse.detail.readOnlyProperty === "true";
accessResultGR.setValue("read_only_field", readOnlyField ? "yes" : "no");
if (operation === "write" && readOnlyField) {
this._populateResultsForReadOnlyField(accessRequestGR.target_field.toString(),
operation, accessResultGR);
return;
}
if (operation === "create" && readOnlyField) {
accessResultGR.setValue("insights", "The field " + accessRequestGR.target_field.toString() +
" is read only");
}
}
var debugAclsPresent = accessInfo.debugLogs.some(function(debug) {
return debug.acls && debug.acls.length > 0;
});
if (accessInfo.logType === "AccessHandlerInfo") {
if (this._isDFLogPresent(accessInfo)) {
accessResultGR.setValue("accesshandler_result", this.SKIP_UI_STATUS);
accessResultGR.setValue("datafiltration_result", accessInfo.status ? this.PASS_UI_STATUS :
this.BLOCK_UI_STATUS);
} else {
if (accessInfo.status) {
accessResultGR.setValue("accesshandler_result", this.PASS_UI_STATUS);
} else {
//do not populate if insights is already set (for readOnly field)
if (accessResultGR.getValue("insights") === null)
accessResultGR.setValue("insights", this._findFirstBlockingAccessHandler(accessInfo) + " is blocking access");
accessResultGR.setValue("accesshandler_result", this.BLOCK_UI_STATUS);
}
}
} else {
if (!debugAclsPresent)
accessResultGR.setValue("acl_result", this.UNDEFINED);
accessResultGR.setValue("accesshandler_result", this.SKIP_UI_STATUS);
if (this._isDFLogPresent(accessInfo)) {
//if df log is present, the result should be passed in this case
accessResultGR.setValue("datafiltration_result", this.PASS_UI_STATUS);
} else {
// if no df log, then df not available
accessResultGR.setValue("datafiltration_result", this.SKIP_UI_STATUS);
}
}
if (accessInfo.logType === "SecurityRulesInfo" && debugAclsPresent) {
accessResultGR.setValue("acl_result", accessInfo.status ? this.PASS_UI_STATUS : this.BLOCK_UI_STATUS);
}
},
_populateResultsForReadOnlyField: function(targetField, operation, accessResultGR) {
accessResultGR.setValue("insights", "The field " + targetField + " is read only. " +
operation + " operation is not allowed for read only fields");
accessResultGR.setValue("accesshandler_result", this.SKIP_UI_STATUS);
accessResultGR.setValue("datafiltration_result", this.SKIP_UI_STATUS);
accessResultGR.setValue("acl_result", this.SKIP_UI_STATUS);
},
_findFirstBlockingAccessHandler: function(accessInfo) {
var debugLogs = accessInfo.debugLogs;
for (var i = 0; i < debugLogs.length; i++) {
var debugLog = debugLogs[i];
if (debugLog.logType === "AccessHandlerInfo" && !debugLog.result) {
return debugLog.accessHandler;
}
}
},
_isDFLogPresent: function(accessInfo) {
var debugLogs = accessInfo.debugLogs;
for (var i = 0; i < debugLogs.length; i++) {
var debugLog = debugLogs[i];
if (debugLog.logType === "DataFiltrationInfo") {
return true;
}
}
return false;
},
_saveAllBRDebugLogs: function(securityDebugInfo, operation, accessResultRef, accessRequestId) {
var accessInfo = securityDebugInfo[operation];
var debugLogs = accessInfo.debugLogs;
if (!debugLogs) {
gs.info("_saveAllBRDebugLogs: No debug log found for operation " + operation +
" in accessRequestID: " + accessRequestId);
return;
}
for (var i = 0; i < debugLogs.length; i++) {
var debugLog = debugLogs[i];
if (debugLog.logType !== "BusinessRuleInfo") {
continue;
}
this._saveBRDebugLog(debugLog, accessResultRef, accessRequestId);
}
},
_saveBRDebugLog: function(dfLog, accessResultRef, accessRequestId) {
var brDebugLogGR = new GlideRecord("sn_access_analyzer_debug_log");
brDebugLogGR.initialize();
brDebugLogGR.setValue("access_analyzer_request", accessRequestId);
brDebugLogGR.setValue("access_result", accessResultRef);
brDebugLogGR.setValue("log_table_name", "sys_script");
brDebugLogGR.setValue("record_ref", dfLog.sysId);
brDebugLogGR.setValue("type", "Business Rule");
brDebugLogGR.log_time.setDisplayValue(dfLog.time);
brDebugLogGR.setValue("log_sequence", this.sequence++);
brDebugLogGR.insert();
},
_saveAllDFDebugLogs: function(securityDebugInfo, operation, accessResultRef, accessRequestId) {
var accessInfo = securityDebugInfo[operation];
var debugLogs = accessInfo.debugLogs;
if (!accessInfo.debugLogs) {
return;
}
for (var i = 0; i < debugLogs.length; i++) {
var debugLog = debugLogs[i];
if (debugLog.logType !== "DataFiltrationInfo") {
continue;
}
this._saveDFDebugLog(debugLog, accessResultRef, accessRequestId);
}
},
_saveDFDebugLog: function(dfLog, accessResultRef, accessRequestId) {
var dfDebugLogGR = new GlideRecord("sn_access_analyzer_debug_log");
dfDebugLogGR.initialize();
dfDebugLogGR.setValue("access_analyzer_request", accessRequestId);
dfDebugLogGR.setValue("access_result", accessResultRef);
dfDebugLogGR.setValue("log_table_name", "sys_df_data_filtration");
dfDebugLogGR.setValue("record_ref", dfLog.ref.split("=")[1]);
dfDebugLogGR.setValue("path", dfLog.path);
dfDebugLogGR.setValue("type", "Data Filtration");
dfDebugLogGR.log_time.setDisplayValue(dfLog.time);
dfDebugLogGR.setValue("log_sequence", this.sequence++);
if (dfLog.status === true) {
dfDebugLogGR.setValue("status", this.PASS_UI_STATUS);
} else {
dfDebugLogGR.setValue("status", this.BLOCK_UI_STATUS);
}
dfDebugLogGR.insert();
},
_saveAllACLDebugLogs: function(securityDebugInfo, operation, accessResultRef, accessRequestId) {
var accessInfo = securityDebugInfo[operation];
if (!accessInfo.debugLogs) {
return;
}
var debugLogs = accessInfo.debugLogs;
var scriptExecutedInACLs = false;
for (var i = 0; i < debugLogs.length; i++) {
var debugLog = debugLogs[i];
var acls = debugLog.acls;
if (debugLog.logType !== "SecurityRulesInfo" || !acls) {
continue;
}
for (var j = 0; j < acls.length; j++) {
var debugLogId = this._saveACLDebugLog(acls[j], debugLog.time, accessResultRef, accessRequestId);
if (!scriptExecutedInACLs && this._debugLogHasScriptExecution(debugLogId))
scriptExecutedInACLs = true;
}
}
if (scriptExecutedInACLs)
this.scriptExecutionListForOperation.push("ACL");
},
_saveACLDebugLog: function(acl, logTime, accessResultRef, accessRequestId) {
var aclDebugLogGR = new GlideRecord("sn_access_analyzer_debug_log");
var scriptExecutionListForACL = [];
aclDebugLogGR.initialize();
aclDebugLogGR.setValue("access_analyzer_request", accessRequestId);
aclDebugLogGR.setValue("log_table_name", "sys_security_acl");
aclDebugLogGR.setValue("record_ref", acl.sysId);
aclDebugLogGR.setValue("access_result", accessResultRef);
aclDebugLogGR.setValue("roles_result", this._getUIResultString(acl.rolesResult));
aclDebugLogGR.setValue("roles", acl.roles.join(", "));
aclDebugLogGR.setValue("condition_result", this._getUIResultString(acl.conditionResult));
aclDebugLogGR.setValue("is_acl_customized", this._isAclCustomized(acl.sysId));
aclDebugLogGR.setValue("script_result", this._getUIResultString(acl.scriptResult));
if (this._isScriptedACL(acl.sysId) && (acl.scriptResult.toLowerCase() === this.TRUE_ACL_API_STATUS ||
acl.scriptResult.toLowerCase() === this.FALSE_ACL_API_STATUS)) {
scriptExecutionListForACL.push("Script");
}
aclDebugLogGR.setValue("path", acl.path);
aclDebugLogGR.setValue("applies_to", this._getEvaluationLevel(acl.path));
aclDebugLogGR.setValue("type", "ACL");
aclDebugLogGR.setValue("app", acl.app);
aclDebugLogGR.log_time.setDisplayValue(logTime);
aclDebugLogGR.setValue("status", this._getCumulativeACLStatus(acl.rolesResult, acl.conditionResult,
acl.scriptResult, acl.sysId, acl.securityAttributeResult));
aclDebugLogGR.setValue("log_sequence", this.sequence++);
aclDebugLogGR.setValue("security_attribute", this._getUIResultString(acl.securityAttributeResult));
if (scriptExecutionListForACL.length > 0)
aclDebugLogGR.setValue("script_execution", scriptExecutionListForACL.join(','));
return aclDebugLogGR.insert();
},
_saveAllAccessHandlerDebugLogs: function(securityDebugInfo, operation, accessResultRef, accessRequestId) {
var accessInfo = securityDebugInfo[operation];
var debugLogs = accessInfo.debugLogs;
if (!debugLogs) {
gs.info("_saveAllAccessHandlerDebugLogs: No debug log found for operation " + operation +
" in accessRequestID: " + accessRequestId);
return;
}
for (var i = 0; i < debugLogs.length; i++) {
var debugLog = debugLogs[i];
if (debugLog.logType !== "AccessHandlerInfo" ||
(debugLog.logType == "AccessHandlerInfo" && debugLog.accessHandler === "DataFiltrationAccessHandler")) {
continue;
}
this._saveAccessHandlerDebugLog(debugLog, accessResultRef, accessRequestId);
}
},
_saveAccessHandlerDebugLog: function(debugLog, accessResultRef, accessRequestId) {
var accessHandlerDebugLogGR = new GlideRecord("sn_access_analyzer_debug_log");
accessHandlerDebugLogGR.initialize();
accessHandlerDebugLogGR.setValue("access_analyzer_request", accessRequestId);
accessHandlerDebugLogGR.setValue("access_result", accessResultRef);
accessHandlerDebugLogGR.setValue("path", debugLog.path);
accessHandlerDebugLogGR.setValue("type", "Access Handler");
accessHandlerDebugLogGR.setValue("applies_to", debugLog.accessHandler);
accessHandlerDebugLogGR.log_time.setDisplayValue(debugLog.time);
if (debugLog.result === true) {
accessHandlerDebugLogGR.setValue("status", this.PASS_UI_STATUS);
} else {
accessHandlerDebugLogGR.setValue("status", this.BLOCK_UI_STATUS);
}
accessHandlerDebugLogGR.setValue("log_sequence", this.sequence++);
accessHandlerDebugLogGR.insert();
},
_getEvaluationLevel: function(path) {
var aclName = path.split("/")[1];
if (aclName.includes('.'))
return "Field";
else
return "Table";
},
_getUIResultString: function(result) {
if (!result)
return "";
var resultLC = result.toLowerCase();
if (resultLC === this.TRUE_ACL_API_STATUS) {
return this.PASS_UI_STATUS;
} else if (resultLC === this.FALSE_ACL_API_STATUS) {
return this.BLOCK_UI_STATUS;
} else if (resultLC === this.NOT_EVALUATED_ACL_API_STATUS) {
return this.SKIP_UI_STATUS;
}
return "";
},
_getCumulativeACLStatus: function(role, condition, script, aclSysId, securityAttr) {
var roleStatus = role.toLowerCase();
var condStatus = condition.toLowerCase();
var scriptStatus = script.toLowerCase();
var securityAttrStatus = securityAttr ? securityAttr.toLowerCase() : "";
if (roleStatus === this.TRUE_ACL_API_STATUS && condStatus === this.TRUE_ACL_API_STATUS &&
scriptStatus === this.TRUE_ACL_API_STATUS && securityAttrStatus === this.TRUE_ACL_API_STATUS) {
return this.PASS_UI_STATUS;
}
// For table level access check, condition and script will always be not evaluated
// cumulative status should be based on role & security attribute status in this case
if (roleStatus === this.TRUE_ACL_API_STATUS && securityAttrStatus !== this.FALSE_ACL_API_STATUS &&
condStatus === this.NOT_EVALUATED_ACL_API_STATUS && scriptStatus === this.NOT_EVALUATED_ACL_API_STATUS) {
return this.PASS_UI_STATUS;
}
if (roleStatus === this.NOT_EVALUATED_ACL_API_STATUS && condStatus === this.NOT_EVALUATED_ACL_API_STATUS &&
scriptStatus === this.NOT_EVALUATED_ACL_API_STATUS && securityAttrStatus === this.NOT_EVALUATED_ACL_API_STATUS) {
return this.SKIP_UI_STATUS;
}
return this.BLOCK_UI_STATUS;
},
// To determine an ACL with aclId has customisations from Customer. Below customisations are identified:
// 1) Check for the changes on ACL fields 2) Addition/Modification of ACL roles 3) Removal of any ACL role
_isAclCustomized: function(aclId) {
if (gs.getProperty("glide.identity.aclanalyzer.retrieve_acl_customization_info", "false").toString() !== "true")
return "";
// Check for the changes on ACL fields
var gr = this._queryUpdateXml("sys_security_acl_" + aclId);
if (gr.hasNext()) {
return true;
}
var secAttrId = this._getAclSecAttribute(aclId);
if (secAttrId) {
gr = this._queryUpdateXml("sys_security_attribute_" + secAttrId);
if (gr.hasNext()) {
return true;
}
}
// Addition/Modification of ACL roles
var rolesIds = this._getAclRolesByAclId(aclId);
gr = this._queryUpdateXml(rolesIds);
if (gr.hasNext()) {
return true;
}
// check for ACL role deletions
gr = new GlideRecord("sys_metadata_delete");
gr.addQuery("sys_parent", aclId);
gr.query();
return gr.hasNext();
},
_queryUpdateXml: function(name) {
var gr = new GlideRecord("sys_update_xml");
gr.addQuery("name", name);
gr.addQuery("category", "customer");
gr.query();
return gr;
},
_getAclSecAttribute: function(aclId) {
var gr = new GlideRecord("sys_security_acl");
gr.addQuery("sys_id", aclId);
gr.query();
if (gr.next()) {
return gr.getValue("security_attribute");
}
return "";
},
// Method to retrieve list of acl role Ids from the Acl Sys_id
_getAclRolesByAclId: function(aclId) {
var aclRole = new GlideRecord("sys_security_acl_role");
aclRole.addQuery("sys_security_acl", aclId);
aclRole.query();
var rolesIds = [];
while (aclRole.next()) {
rolesIds.push("sys_security_acl_role_" + aclRole.sys_id.toString());
}
return rolesIds;
},
//Method to find out whether an ACL actually has advance = true and a non-empty Script
_isScriptedACL: function(aclId) {
var gr = new GlideRecord("sys_security_acl");
gr.addQuery("sys_id", aclId);
gr.query();
if (gr.next()) {
return !gs.nil(gr.getValue("script"));
}
return false;
},
_addScriptExecutionListToResult: function(resultId) {
if (this.scriptExecutionListForOperation.length != 0) {
var resultGr = new GlideRecord("sn_access_analyzer_access_result");
resultGr.addQuery("sys_id", resultId);
resultGr.query();
if (resultGr.next())
resultGr.setValue("script_execution", this.scriptExecutionListForOperation.join(','));
resultGr.update();
}
},
_debugLogHasScriptExecution: function(debugLogId) {
var debugLogGr = new GlideRecord("sn_access_analyzer_debug_log");
debugLogGr.addQuery("sys_id", debugLogId);
debugLogGr.query();
if (debugLogGr.next()) {
var scriptExec = debugLogGr.getValue("script_execution");
return (scriptExec == null || scriptExec == "") ? false : true;
}
return false;
},
type: 'AccessAnalyzerUtil'
};
Sys ID
e171277777971110638cfe21fe5a994d