fix: flush the update cache on every license removal path - #1684
Merged
Conversation
jakejackson1
force-pushed
the
fix/license-flush-update-cache
branch
2 times, most recently
from
July 24, 2026 02:28
9198e17 to
2dd3b55
Compare
Activation and deactivation already flushed the cached update info, but three paths that change license state did not: 1. Access Pass siblings. maybe_auto_activate_license()/maybe_auto_deactivate_license() called update_license_info() alone, so every add-on covered by the pass kept version info fetched under the old key (no package) for up to the 3 hour TTL, plus its stale update_plugins entry. 2. Clearing the key in the settings form. Unlike the Deactivate button, the empty-key branch of maybe_active_licenses() left the cached package in place. 3. The Multisite network cache. delete_version_info_cache() only removes the per-site option, so the gpdf_sl_net_* package a site promoted stayed borrowable through maybe_apply_network_package() for its 3 day TTL — a site that had lost its license could still hand out a download URL to the network. Withdrawing the network package needs a lost license and ownership of the cached copy. Lost means the key was removed or the store rejected it outright: expired, revoked, disabled, missing, invalid, site_inactive, item_name_mismatch, invalid_item_id, no_activations_left — all of which arrive with the key still populated, so the status is the only signal. `error` and `rate_limit` are excluded, because they are written on a WP_Error, timeout or 429, and treating a failed request as a verdict would strip a package every subsite depends on whenever the licensing server is briefly unreachable. Ownership is tracked by recording the promoting blog id alongside the package: a promotion made by another site belongs to a site that is still licensed, and must outlive this one. Two guards keep the new flushes off no-op paths. The sibling flush only fires when the key or status actually moved, since a hardcoded GPDF_LICENSE_KEY re-fires the activation action every few hours and would otherwise force a fresh remote request per add-on each cycle. The settings-form flush only fires when a key was previously stored, since the licensing tab posts an empty field for every registered add-on on an unrelated save. Also corrects Model_Uninstall::remove_plugin_network_options()'s docblock, which described the network cache as a single option. There is one per extension; the wildcard delete is what makes the cleanup complete. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jakejackson1
force-pushed
the
fix/license-flush-update-cache
branch
from
July 24, 2026 02:36
2dd3b55 to
e7dbf0c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
License activation and deactivation already flushed the cached update info, but three paths that change license state did not.
1. Access Pass siblings.
maybe_auto_activate_license()/maybe_auto_deactivate_license()calledupdate_license_info()alone. Every add-on covered by the pass kept version info fetched under the old key (so, no package) for up to the 3-hour TTL, plus its staleupdate_pluginsentry.2. Clearing the key in the settings form. Unlike the Deactivate button, the empty-key branch of
maybe_active_licenses()left the cached package in place.3. The Multisite network cache.
delete_version_info_cache()only removes the per-site option, so thegpdf_sl_net_*package a site promoted stayed borrowable viamaybe_apply_network_package()for its 3-day TTL — a site that had lost its license could still hand a download URL to the network.What ends the network entitlement
Withdrawal needs a lost license and ownership of the shared package.
expired,revoked,disabled,missing,invalid,site_inactive,item_name_mismatch,invalid_item_id,no_activations_left. These all arrive with the key still populated, so the status is the only signal.errorandrate_limitare deliberately excluded. They are written on aWP_Error, timeout or 429, and treating a failed request as a verdict would strip a package every subsite depends on whenever the licensing server is briefly unreachable.flush_update_cache()takes no flag; the withdrawal derives from the updater's own key and status, so no caller has to remember to ask for it.Guards against no-op work
GPDF_LICENSE_KEYre-fires the activation action every few hours and would otherwise force a fresh remote request per add-on each cycle.Testing
16 new tests. Full PHPUnit suite green in both modes: 1149 tests / 3578 assertions single-site, 1149 / 3677 multisite.
phpcsclean on the changed files.The rejection-status tests were verified non-vacuous: with the status gate reverted to the key-only check, all 10 fail.
Follow-up (not in this patch)
All five
update_license_info()call sites insrc/are now immediately followed by a flush — that 100% correlation says the invariant belongs insideupdate_license_info()itself. Deliberately left for 6.17 rather than relocating side effects of a public@since 4.2method in a hot patch. Same for the settings-clear branch being a second, partial definition of "license removed" alongsidedelete_license_info().🤖 Generated with Claude Code