policy.js revision 21dcdac963f79c098a5ea1a2c5c5e109429c9786
"policyId" :
"valid-email-address-format",
"policyExec" :
"validEmailAddressFormat",
"clientValidation":
true,
"validateOnlyIfPresent":
true,
"policyRequirements": [
"VALID_EMAIL_ADDRESS_FORMAT"]
"policyId" :
"valid-name-format",
"policyExec" :
"validNameFormat",
"clientValidation":
true,
"validateOnlyIfPresent":
true,
"policyRequirements": [
"VALID_NAME_FORMAT"]
"policyId" :
"valid-phone-format",
"policyExec" :
"validPhoneFormat",
"clientValidation":
true,
"validateOnlyIfPresent":
true,
"policyRequirements": [
"VALID_PHONE_FORMAT"]
{
"policyId" :
"at-least-X-capitals",
"policyExec" :
"atLeastXCapitalLetters",
"clientValidation":
true,
"validateOnlyIfPresent":
true,
"policyRequirements" : [
"AT_LEAST_X_CAPITAL_LETTERS"]
{
"policyId" :
"at-least-X-numbers",
"policyExec" :
"atLeastXNumbers",
"clientValidation":
true,
"validateOnlyIfPresent":
true,
"policyRequirements" : [
"AT_LEAST_X_NUMBERS"]
{
"policyId" :
"minimum-length",
"policyExec" :
"minLength",
"clientValidation":
true,
"validateOnlyIfPresent":
true,
"policyRequirements" : [
"MIN_LENGTH"]
{
"policyId" :
"cannot-contain-others",
"policyExec" :
"cannotContainOthers",
"clientValidation":
true,
"validateOnlyIfPresent":
true,
"policyRequirements" : [
"CANNOT_CONTAIN_OTHERS"]
{
"policyId" :
"cannot-contain-characters",
"policyExec" :
"cannotContainCharacters",
"clientValidation":
true,
"validateOnlyIfPresent":
true,
"policyRequirements" : [
"CANNOT_CONTAIN_CHARACTERS"]
{
"policyId" :
"cannot-contain-duplicates",
"policyExec" :
"cannotContainDuplicates",
"clientValidation":
true,
"validateOnlyIfPresent":
true,
"policyRequirements" : [
"CANNOT_CONTAIN_DUPLICATES"]
"policyId" :
"required-if-configured",
"policyExec":
"requiredIfConfigured",
"policyRequirements" : [
"REQUIRED"]
{
"policyId" :
"re-auth-required",
"policyExec" :
"reauthRequired",
"validateOnlyIfPresent":
true,
"policyRequirements" : [
"REAUTH_REQUIRED"]
// internal-use utility function if (
typeof(
value) ===
"number") {
return [ {
"policyRequirement" :
"REQUIRED" } ];
return [ {
"policyRequirement":
"REQUIRED"}];
"_queryId":
"credential-internaluser-query",
return [{
"policyRequirement":
"UNIQUE"}];
return [{
"policyRequirement":
"UNIQUE"}];
return [ {
"policyRequirement":
"VALID_DATE"}];
join =
function (
arr, d) {
// my own join needed since it appears params.forbiddenChars is not a proper JS array with the normal join method available return [ {
"policyRequirement" :
"CANNOT_CONTAIN_CHARACTERS",
"params" : {
"forbiddenChars" :
join(
params.
forbiddenChars,
", ")} } ];
var pattern = /^\+?([
0-
9\- \(\)])*$/,
return [ {
"policyRequirement":
"VALID_PHONE_FORMAT"}];
var pattern = /^([A-
Za'-\u0105\u0107\u0119\u0142\u00F3\u015B\u017C\u017A\u0104\u0106\u0118\u0141\u00D3\u015A\u017B\u0179\u00C0\u00C8\u00CC\u00D2\u00D9\u00E0\u00E8\u00EC\u00F2\u00F9\u00C1\u00C9\u00CD\u00D3\u00DA\u00DD\u00E1\u00E9\u00ED\u00F3\u00FA\u00FD\u00C2\u00CA\u00CE\u00D4\u00DB\u00E2\u00EA\u00EE\u00F4\u00FB\u00C3\u00D1\u00D5\u00E3\u00F1\u00F5\u00C4\u00CB\u00CF\u00D6\u00DC\u0178\u00E4\u00EB\u00EF\u00F6\u00FC\u0178\u00A1\u00BF\u00E7\u00C7\u0152\u0153\u00DF\u00D8\u00F8\u00C5\u00E5\u00C6\u00E6\u00DE\u00FE\u00D0\u00F0\-\s])+$/, isRequired = _.find(this.failedPolicyRequirements, function (fpr) { return fpr.policyRequirement === "REQUIRED"; isNonEmptyString = (typeof(value) === "string" && value.length), valuePassesRegexp = (function (v) { var testResult = isNonEmptyString ? pattern.test(v) : false; if ((isRequired || isNonEmptyString) && !valuePassesRegexp) { return [ {"policyRequirement": "VALID_NAME_FORMAT"}]; policyFunctions.minLength = function(fullObject, value, params, property) { var isRequired = _.find(this.failedPolicyRequirements, function (fpr) { return fpr.policyRequirement === "REQUIRED"; isNonEmptyString = (typeof(value) === "string" && value.length), hasMinLength = isNonEmptyString ? (value.length >= params.minLength) : false; if ((isRequired || isNonEmptyString) && !hasMinLength) { return [ { "policyRequirement" : "MIN_LENGTH", "params" : {"minLength":params.minLength} } ]; policyFunctions.atLeastXCapitalLetters = function(fullObject, value, params, property) { var isRequired = _.find(this.failedPolicyRequirements, function (fpr) { return fpr.policyRequirement === "REQUIRED"; isNonEmptyString = (typeof(value) === "string" && value.length), valuePassesRegexp = (function (v) { var test = isNonEmptyString ? v.match(/[(A-Z)]/g) : null; return test !== null && test.length >= params.numCaps; if ((isRequired || isNonEmptyString) && !valuePassesRegexp) { return [ { "policyRequirement" : "AT_LEAST_X_CAPITAL_LETTERS", "params" : {"numCaps": params.numCaps} } ]; policyFunctions.atLeastXNumbers = function(fullObject, value, params, property) { var isRequired = _.find(this.failedPolicyRequirements, function (fpr) { return fpr.policyRequirement === "REQUIRED"; isNonEmptyString = (typeof(value) === "string" && value.length), valuePassesRegexp = (function (v) { var test = isNonEmptyString ? v.match(/\d/g) : null; return test !== null && test.length >= params.numNums; if ((isRequired || isNonEmptyString) && !valuePassesRegexp) { return [ { "policyRequirement" : "AT_LEAST_X_NUMBERS", "params" : {"numNums": params.numNums} } ]; policyFunctions.validEmailAddressFormat = function(fullObject, value, params, property) { var pattern = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/, isRequired = _.find(this.failedPolicyRequirements, function (fpr) { return fpr.policyRequirement === "REQUIRED"; isNonEmptyString = (typeof(value) === "string" && value.length), valuePassesRegexp = (function (v) { var testResult = isNonEmptyString ? pattern.test(v) : false; if ((isRequired || isNonEmptyString) && !valuePassesRegexp) { return [ {"policyRequirement": "VALID_EMAIL_ADDRESS_FORMAT"}]; policyFunctions.cannotContainOthers = function(fullObject, value, params, property) { var fieldArray = params.disallowedFields.split(","), // since this function runs on both the client and the server, we need to // check for the presence of our server-side functions before using them. if (typeof(openidm) !== "undefined" && typeof(request) !== "undefined" && request.resourcePath && !request.resourcePath.match('/*$')) { fullObject_server = openidm.read(request.resourcePath); if (fullObject_server === null) { if (value && typeof(value) === "string" && value.length) { for (i = 0; i < fieldArray.length; i++) { if (typeof(fullObject[fieldArray[i]]) === "undefined" && typeof(fullObject_server[fieldArray[i]]) !== "undefined") { fullObject[fieldArray[i]] = fullObject_server[fieldArray[i]]; if (typeof(fullObject[fieldArray[i]]) === "string" && value.match(fullObject[fieldArray[i]])) { return [{"policyRequirement": "CANNOT_CONTAIN_OTHERS", params: {"disallowedFields": fieldArray[i]}}]; policyFunctions.cannotContainDuplicates = function(fullObject, value, params, property) { if (value && value.length) { for (i = 0; i < value.length; i++) { if (Object.prototype.hasOwnProperty.call(checkedValues, value[i])) { return [{"policyRequirement": "CANNOT_CONTAIN_DUPLICATES", params: {"duplicateValue": value[i]}}]; checkedValues[value[i]] = true; policyFunctions.requiredIfConfigured = function(fullObject, value, params, property) { var currentValue = openidm.read("config/" + params.configBase), baseKeyArray = params.baseKey.split("."), if (checkExceptRoles(params.exceptRoles)) { for (i in baseKeyArray) { currentValue = currentValue[baseKeyArray[i]]; if (currentValue && (!value || !value.length)) { return [ {"policyRequirement": "REQUIRED"}]; policyFunctions.reauthRequired = function(fullObject, value, params, propName) { if (checkExceptRoles(params.exceptRoles)) { var actionParams,response,currentObject; // Perform reauth if the context indicates that the caller is external // or if we have set a parameter to force reauth when handing a patch operation. // Important: Interpret any value of additionalParameters.external as `true` // so that an external caller cannot abuse this facility by passing in 'false'. if (context.caller.external || (request.additionalParameters !== null && typeof request.additionalParameters.external !== "undefined")) { // don't do a read if the resource ends with "/*", which indicates that this is a new record if (typeof request.resourcePath === "string" && !request.resourcePath.match('/\\*$')) { currentObject = openidm.read(request.resourcePath); // if the given resource doesn't exist, this also indicates that // this is a new record (likely a client-assigned ID) if (currentObject === null) { if (openidm.isEncrypted(currentObject[propName])) { currentObject[propName] = openidm.decrypt(currentObject[propName]); if (currentObject[propName] === fullObject[propName]) { // this means the value hasn't changed, so don't complain about reauth response = openidm.action("authentication", "reauthenticate", {}, {}); return [ { "policyRequirement" : "REAUTH_REQUIRED" } ]; policyProcessor = (function (policyConfig,policyImpl){ //Internal policy code below - do not modify this module var getPolicy = function(policyId) { for (i = 0; i < policyConfig.policies.length; i++) { if (policyConfig.policies[i].policyId === policyId) { return policyConfig.policies[i]; getPropertyValue = function(requestObject, propName) { var propAddress = propName.split("/"), tmpObject = requestObject, for (i = 0; i < propAddress.length; i++) { propAddress[i] = propAddress[i].replace(/\[\*\]$/, ''); // replace a trailing array indicator, if found tmpObject = tmpObject[propAddress[i]]; if (tmpObject === undefined || tmpObject === null) { getPropertyConfig = function(resource, propName) { for (i = 0; i < props.length; i++) { if (prop.name === propName) { resourceMatches = function(resource1, resource2) { var rsrc1 = resource1.split("/"), rsrc2 = resource2.split("/"), if (rsrc1.length === rsrc2.length) { for (i = 0; i < rsrc1.length; i++) { if (rsrc1[i] !== rsrc2[i] && getResource = function(resources, resourceName) { if (resources !== null) { for (i = 0; i < resources.length; i++) { if (resourceMatches(resource.resource, resourceName)) { getResourceWithPolicyRequirements = function(resource) { // Loop through the properties for this resource for (i = 0; i < compProps.length; i++) { // loop through the policies of each property for (j = 0; j < prop.policies.length; j++) { policy = getPolicy(prop.policies[j].policyId); // Check if client validation is enabled, if so add source if ((policy.clientValidation !== undefined) && policy.clientValidation) { prop.policies[j].policyFunction = policyImpl[policy.policyExec].toString(); prop.policies[j].policyRequirements = policy.policyRequirements; reqs = policy.policyRequirements; // loop through the requirements for each policy for (x = 0; x < reqs.length; x++) { // Add the requirement if it hasen't been added yet if (propPolicyReqs.indexOf(reqs[x]) === -1) { propPolicyReqs.push(reqs[x]); // Add the requirements array to the property object prop.policyRequirements = propPolicyReqs; // Return all property configs for this resource getAllPolicyRequirements = function (policies) { reqs = reqs.concat(getPolicy(policies[i].policyId).policyRequirements); validate = function(policies, fullObject, propName, propValue, retArray) { allPolicyRequirements = getAllPolicyRequirements(policies), i,j,params,policy,validationFunc,failed,y; for (i = 0; i < policies.length; i++) { params = policies[i].params; policy = getPolicy(policies[i].policyId); throw "Unknown policy " + policies[i].policyId; // validate this property every time unless the property has been marked as "validateOnlyIfPresent" and it isn't present if (!(typeof(policy.validateOnlyIfPresent) !== 'undefined' && policy.validateOnlyIfPresent && typeof(propValue) === 'undefined')) { validationFunc = policyImpl[policy.policyExec]; if (propName.match(/\[\*\]$/)) { // if we are dealing with a property that is an array element propValueContainer = propValue; // then use the propValue provided for the array } else { // if we are dealing with a regular property propValueContainer = [propValue]; // then it's a single value array if (propValueContainer !== undefined && propValueContainer !== null) { for (j=0;j<propValueContainer.length;j++) { retObj.policyRequirements = []; if (openidm.isEncrypted(propValueContainer[j])) { propValueContainer[j] = openidm.decrypt(propValueContainer[j]); failed = validationFunc.call({ "failedPolicyRequirements": policyRequirements, "allPolicyRequirements": allPolicyRequirements }, fullObject, propValueContainer[j], params, propName); retObj.property = propName.replace(/\[\*\]$/, "["+j+"]"); for ( y = 0; y < failed.length; y++) { policyRequirements.push(failed[y]); retObj.policyRequirements.push(failed[y]); mergePolicies = function(oldPolicies, newPolicies) { for (i = 0; i < oldPolicies.length; i++) { returnPolicies.push(oldPolicies[i]); for (i = 0; i < newPolicies.length; i++) { newPolicy = newPolicies[i]; for (j = 0; j < returnPolicies.length; j++) { policy = returnPolicies[j]; if (newPolicy.policyId === policy.policyId) { // update old policy with new config returnPolicies[j] = newPolicy; p.policyId = newPolicy.policyId; for (key in newPolicy.params) { p.params[key] = newPolicy.params[key]; getAdditionalPolicies = function(id) { resource = id.substring(0, index); object = id.substring(index + 1, id.length); configId = "config/" + resource; resourceConfig = openidm.read(configId); if (resourceConfig !== null && resourceConfig.error === undefined) { objects = resourceConfig.objects; if (objects !== undefined && objects !== null) { for (i = 0; i < objects.length; i++) { if ((obj.name === object) || resourceMatches(object, obj.name + "/*")) { if (props !== undefined && props !== null) { for (j = 0; j < props.length; j++) { policies = prop.policies; if (policies !== null && policies !== undefined) { property.name = prop.name; for (k = 0; k < policies.length; k++) { property.policies.push(policies[k]); returnArray.push(property); updateResourceConfig = function(resource, id) { var newProps = getAdditionalPolicies(id), for (i = 0; i < newProps.length; i++) { for (j = 0; j < props.length; j++) { if (newProp.name === prop.name) { if (prop.policies.length > 0) { prop.policies = mergePolicies(prop.policies, newProp.policies); prop.policies = newProp.policies; processRequest = function() { failedPolicyRequirements, if (request.resourcePath !== null && request.resourcePath !== undefined) { // Get the policy configuration for the specified resource resource = getResource(resources, request.resourcePath); if (resource === null ) { resource.resource = request.resourcePath; // Update the policy configuration with any resource specific updateResourceConfig(resource, request.resourcePath); if (request.resourcePath === null || request.resourcePath === "") { for (i = 0; i < resources.length; i++) { compArray.push(getResourceWithPolicyRequirements(rsrc)); returnObject.resources = compArray; returnObject = getResourceWithPolicyRequirements(resource); } else if (method === "action") { failedPolicyRequirements = []; if (request.resourcePath === null) { throw "No resource specified"; // There are no configured policies for this resource (nothing to verify) returnObject.result = true; returnObject.failedPolicyRequirements = failedPolicyRequirements; fullObject = request.content; // Perform the validation if (action === "validateObject") { policyRequirements = validate(policies, fullObject, propName, getPropertyValue(fullObject, propName), failedPolicyRequirements); } else if (action === "validateProperty") { for (propName in props) { prop = getPropertyConfig(resource, propName); policies = prop.policies; policyRequirements = validate(policies, fullObject, propName, props[propName], failedPolicyRequirements); throw "Unsupported action: " + action; // Set the result to true if no failedPolicyRequirements (failures), false otherwise returnObject.result = (failedPolicyRequirements.length === 0); // Set the return failedPolicyRequirements returnObject.failedPolicyRequirements = failedPolicyRequirements; throw "Unsupported method: " + method; return {processRequest:processRequest}; }(policyConfig,policyImpl)), additionalPolicyLoader = (function (config,impl) { addPolicy = function(policy) { config.policies.push(policy); obj.load = function (additionalPolicies) { //Load additional policy scripts if configured for (i = 0; i < additionalPolicies.length; i++) { eval(additionalPolicies[i]); for (j=0;j<config.policies.length;j++) { if (!policyImpl.hasOwnProperty(config.policies[j].policyExec) && typeof(eval(config.policies[j].policyExec)) === "function") { impl[config.policies[j].policyExec] = eval(config.policies[j].policyExec); }(policyConfig,policyImpl)); if (typeof additionalPolicies !== 'undefined') { additionalPolicyLoader.load(additionalPolicies); policyProcessor.processRequest();