/** * Grant utility functions for SP-SM enforcement. * Include this file where grant time/eligibility checks are needed. */ /** * Check if a grant's time policy is currently active. * @timePolicyType always | schedule | date_range | event * @timePolicyData JSON string or struct with policy parameters * @return boolean */ function isGrantTimeActive(required string timePolicyType, any timePolicyData = "") { if (arguments.timePolicyType == "always") return true; var policy = {}; if (isSimpleValue(arguments.timePolicyData) && len(trim(arguments.timePolicyData))) { try { policy = deserializeJSON(arguments.timePolicyData); } catch (any e) { return false; } } else if (isStruct(arguments.timePolicyData)) { policy = arguments.timePolicyData; } else { return false; } var nowDt = now(); switch (arguments.timePolicyType) { case "schedule": // policy: { days: [1,2,3,4,5], startTime: "09:00", endTime: "17:00" } // days: 1=Sunday, 2=Monday, ... 7=Saturday (ColdFusion dayOfWeek) if (!structKeyExists(policy, "days") || !isArray(policy.days)) return false; var todayDow = dayOfWeek(nowDt); if (!arrayFind(policy.days, todayDow)) return false; if (structKeyExists(policy, "startTime") && structKeyExists(policy, "endTime")) { var currentTime = timeFormat(nowDt, "HH:mm"); if (currentTime < policy.startTime || currentTime > policy.endTime) return false; } return true; case "date_range": // policy: { start: "2026-03-01", end: "2026-06-30" } if (!structKeyExists(policy, "start") || !structKeyExists(policy, "end")) return false; var today = dateFormat(nowDt, "yyyy-mm-dd"); return (today >= policy.start && today <= policy.end); case "event": // policy: { name: "Summer Festival", start: "2026-07-04 10:00", end: "2026-07-04 22:00" } if (!structKeyExists(policy, "start") || !structKeyExists(policy, "end")) return false; var nowStr = dateTimeFormat(nowDt, "yyyy-mm-dd HH:nn"); return (nowStr >= policy.start && nowStr <= policy.end); default: return false; } } /** * Check if a user meets the eligibility scope for a grant. * @eligibilityScope public | employees | guests | internal * @userID The ordering user's ID * @ownerBusinessID The SP-owning business * @guestBusinessID The granted business * @return boolean */ function checkGrantEligibility( required string eligibilityScope, required numeric userID, required numeric ownerBusinessID, required numeric guestBusinessID ) { if (arguments.eligibilityScope == "public") return true; // Check if user is employee of a given business var isGuestEmployee = queryExecute( "SELECT 1 FROM Employees WHERE BusinessID = ? AND UserID = ? AND IsActive = 1 LIMIT 1", [ { value = arguments.guestBusinessID, cfsqltype = "cf_sql_integer" }, { value = arguments.userID, cfsqltype = "cf_sql_integer" } ], { datasource = "payfrit" } ).recordCount > 0; var isOwnerEmployee = queryExecute( "SELECT 1 FROM Employees WHERE BusinessID = ? AND UserID = ? AND IsActive = 1 LIMIT 1", [ { value = arguments.ownerBusinessID, cfsqltype = "cf_sql_integer" }, { value = arguments.userID, cfsqltype = "cf_sql_integer" } ], { datasource = "payfrit" } ).recordCount > 0; switch (arguments.eligibilityScope) { case "employees": return isGuestEmployee; case "guests": return (!isGuestEmployee && !isOwnerEmployee); case "internal": return isOwnerEmployee; default: return false; } } /** * Record an entry in the immutable grant history table. */ function recordGrantHistory( required numeric grantID, required string action, required numeric actorUserID, required numeric actorBusinessID, any previousData = "", any newData = "" ) { var prevJson = isStruct(arguments.previousData) ? serializeJSON(arguments.previousData) : (len(trim(arguments.previousData)) ? arguments.previousData : javaCast("null", "")); var newJson = isStruct(arguments.newData) ? serializeJSON(arguments.newData) : (len(trim(arguments.newData)) ? arguments.newData : javaCast("null", "")); var ip = ""; try { ip = cgi.REMOTE_ADDR; } catch (any e) {} queryExecute( "INSERT INTO ServicePointGrantHistory (GrantID, Action, ActorUserID, ActorBusinessID, PreviousData, NewData, IPAddress) VALUES (?, ?, ?, ?, ?, ?, ?)", [ { value = arguments.grantID, cfsqltype = "cf_sql_integer" }, { value = arguments.action, cfsqltype = "cf_sql_varchar" }, { value = arguments.actorUserID, cfsqltype = "cf_sql_integer" }, { value = arguments.actorBusinessID, cfsqltype = "cf_sql_integer" }, { value = prevJson, cfsqltype = "cf_sql_varchar", null = isNull(prevJson) }, { value = newJson, cfsqltype = "cf_sql_varchar", null = isNull(newJson) }, { value = ip, cfsqltype = "cf_sql_varchar" } ], { datasource = "payfrit" } ); }