|
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] = nilend-- 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. endend |