1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
-- NOTE: This audit functionality can be circumvented and is not intended for enforcing security or access control.
local AFTER_UPDATE_CALLBACK_SENTINEL_KEY = { }
local BEFORE_UPDATE_CALLBACK_SENTINEL_KEY = { }
local ORIGINAL_NEWINDEX_SENTINEL_KEY = { }
local PROCESSING_UPDATE_SENTINEL_KEY = { }
-- Forward declaration for self-reference.
-- Check and set the processing flag.
-- Order matters:
if not internalassert ( not mt [ PROCESSING_UPDATE_SENTINEL_KEY ] , "An update callback has caused a recursive update: Use 'rawget' and 'rawset' to access and modify table values in update callbacks." ) then
-- Terminate early to avoid infinite recursion caused by one or more update callbacks.
return
end
mt [ PROCESSING_UPDATE_SENTINEL_KEY ] = true
-- Call each beforeUpdateCallback and, if requested, terminate early to cancel the update operation.
local beforeUpdateCallbackList = mt [ BEFORE_UPDATE_CALLBACK_SENTINEL_KEY ]
if beforeUpdateCallbackList then
if cancelUpdate then
-- Callback requested to cancel the update operation.
-- Clear the processing flag.
mt [ PROCESSING_UPDATE_SENTINEL_KEY ] = nil
return
elseif newValue ~= nil then
-- Callback requested to override the value.
end
end
end
-- Forward the update operation to the metatable's original __newindex handler.
else
-- Manually process the update operation.
end
-- Call each afterUpdateCallback.
local afterUpdateCallbackList = mt [ AFTER_UPDATE_CALLBACK_SENTINEL_KEY ]
if afterUpdateCallbackList then
end
end
-- Clear the processing flag.
mt [ PROCESSING_UPDATE_SENTINEL_KEY ] = nil
end
-- Sample Usage for conditionally redirecting or preventing inserts/updates to a table:
-- local myList = {}
--
-- local function OnBeforeUpdate(object, key, value)
-- if key == "Hello" then
-- local CANCEL_UPDATE = true
-- return CANCEL_UPDATE
-- elseif key == "Fizz" then
-- local CONTINUE_UPDATE = false
-- local NEW_VALUE = "Soda"
-- return CONTINUE_UPDATE, NEW_VALUE
-- end
-- end
--
-- ZO_AuditObject(myList, OnBeforeUpdate)
-- myList["Hello"] = "World"
-- myList["Fizz"] = "Buzz"
-- d(myList)
--
-- Sample Output:
-- .(string): Fizz = Soda
-- Sample Usage for receiving notification of inserts/updates to a table:
-- local myList = {}
--
-- local function OnAfterUpdate(object, key, value)
-- df("myList updated: key=%s value=%s", tostring(key), tostring(value))
-- end
--
-- local NO_BEFORE_UPDATE_CALLBACK = nil
-- ZO_AuditObject(myList, NO_BEFORE_UPDATE_CALLBACK, OnAfterUpdate)
-- myList["Hello"] = "World"
--
-- Sample Output:
-- Update: key=Hello value=World
-- NOTE: This audit functionality can be circumvented and is not intended for enforcing security or access control.
return
end
local registerObjectMetatable = false
if not mt then
-- Create a new metatable for this object.
mt = { }
registerObjectMetatable = true
end
if mt [ ORIGINAL_NEWINDEX_SENTINEL_KEY ] == nil then
-- Initial auditing setup for this object.
-- Order matters:
end
if not internalassert ( type ( beforeUpdateCallback ) == "function" , "beforeUpdateCallback must be a function or nil." ) then
return
end
-- Create or update the beforeUpdateCallbackList for this object.
-- Order matters:
local beforeUpdateCallbackList = mt [ BEFORE_UPDATE_CALLBACK_SENTINEL_KEY ]
if not beforeUpdateCallbackList then
beforeUpdateCallbackList = { }
mt [ BEFORE_UPDATE_CALLBACK_SENTINEL_KEY ] = beforeUpdateCallbackList
end
end
if not internalassert ( type ( afterUpdateCallback ) == "function" , "afterUpdateCallback must be a function or nil." ) then
return
end
-- Create or update the afterUpdateCallbackList for this object.
-- Order matters:
local afterUpdateCallbackList = mt [ AFTER_UPDATE_CALLBACK_SENTINEL_KEY ]
if not afterUpdateCallbackList then
afterUpdateCallbackList = { }
mt [ AFTER_UPDATE_CALLBACK_SENTINEL_KEY ] = afterUpdateCallbackList
end
end
if registerObjectMetatable then
-- Apply the new metatable to this object.
end
end |