generated from jric11/baseProject
ChatGPT's update to harden software and run db updates.
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
# Tenant invitation and ticket repairs — October 20, 2026
|
||||
|
||||
Addresses the four confirmed defects in
|
||||
[the multi-tenant production review](MULTI-TENANT-REVIEW-2026-10-20.md), in its
|
||||
recommended order. No schema migration or development database change is needed.
|
||||
All verification uses generated disposable local databases; no real email is sent.
|
||||
|
||||
## 1. Administrator invitation bypass
|
||||
|
||||
`ClubInvitations::accept()` now distinguishes existing **active** administrator
|
||||
memberships from inactive historical roles. A member invitation can reactivate an
|
||||
inactive membership only as a member; it cannot restore the historical
|
||||
administrator role. A fresh administrator invitation still requires a current
|
||||
Super System Administrator grant. Already active administrators keep their role
|
||||
when accepting a member invitation, preventing accidental loss of the last club
|
||||
administrator.
|
||||
|
||||
`RosterService::saveMember()` revokes outstanding invitations when the membership
|
||||
role changes as well as when membership is made inactive. An earlier administrator
|
||||
invitation therefore cannot undo a later explicit demotion. These changes are
|
||||
scoped to the selected membership and do not change a shared account's other clubs.
|
||||
|
||||
Regression cases cover rejoining as a member, Super-approved restoration,
|
||||
preserving active administrators, invitation invalidation on promotion/demotion
|
||||
or deactivation, and unaffected access to another club.
|
||||
|
||||
## 2. Concurrent transactions
|
||||
|
||||
Invitation acceptance now locks the recipient user before the club, matching
|
||||
membership updates and account operations that lock user records first.
|
||||
|
||||
New `classes/DatabaseTransaction.php` provides up to **three complete transaction
|
||||
attempts** for database deadlocks, serialization failures and lock-wait timeouts.
|
||||
Every failed attempt rolls back before a short randomized backoff and retry.
|
||||
Other database errors and business/permission errors are propagated immediately.
|
||||
Exhausted retries propagate the failure after rollback; retries are bounded.
|
||||
|
||||
Applied the helper to:
|
||||
|
||||
- Invitation creation, acceptance and revocation.
|
||||
- Club creation/updates, membership updates and season creation.
|
||||
- Ledger charges, payments, corrections and reversals.
|
||||
- Global account edits, activation/deactivation and deletion.
|
||||
- Ticket creation and replies.
|
||||
|
||||
Permission checks inside the transaction rerun on retry. Administrator invitation
|
||||
issuance also rechecks Super privilege inside each attempt. Email delivery and
|
||||
rate-limit accounting remain outside the retry closure, so retrying database work
|
||||
does not repeat those external effects. Mandatory audits commit with their writes.
|
||||
|
||||
Tests inject retryable failures after database writes, verify exactly one
|
||||
committed write, cover exhausted attempts and nonretryable permission failures,
|
||||
and revoke authority between attempts to confirm the retry cannot commit.
|
||||
Controlled concurrent real service calls cover acceptance versus activation and
|
||||
acceptance versus deactivation. Existing financial and final-administrator races
|
||||
remain part of the full suite.
|
||||
|
||||
## 3. Unicode ticket validation
|
||||
|
||||
- Ticket subjects and bodies use server-owned limits of **200** and **8,000**
|
||||
Unicode characters, with specific required/invalid/over-limit errors.
|
||||
- The dispatcher permits larger message transport only for POST ticket creation
|
||||
and reply actions. That field retains a hard **65,536-byte** ceiling; all other
|
||||
fields retain the existing 8,192-byte ceiling. The field validator then enforces
|
||||
the actual 8,000-character limit before storage.
|
||||
- Query and form values are checked separately. A valid POST value cannot hide an
|
||||
array-shaped query value with the same name.
|
||||
- Added `js/tickets.js`, which validates Unicode code-point length rather than
|
||||
JavaScript UTF-16 units. HTML maxlength permits the required two UTF-16 units
|
||||
per character; client validation and server validation enforce the character
|
||||
limit. The same handling applies to subjects, replies and ticket search.
|
||||
- HTTP tests preserve 3,000 CJK characters and 8,000 four-byte emoji without
|
||||
truncation, reject 8,001-character messages/replies without partial writes,
|
||||
and verify malformed/extreme requests still fail. Browser tests cover native
|
||||
controls, meaningful validation messages and full Unicode submission.
|
||||
|
||||
## 4. Ticket pages and search
|
||||
|
||||
- Replaced the newest-100-only list with **25-ticket pages**, newest serial first,
|
||||
and first/previous/next/last navigation.
|
||||
- Added subject search with a 100-character limit. Search treats SQL wildcard
|
||||
characters `%`, `_` and `!` literally and uses bound parameters.
|
||||
- Both the count and row query apply the club and author/administrator scope.
|
||||
Members see only their personal results/counts; club administrators see only
|
||||
their managed club's tickets. Direct ticket access retains its own permission
|
||||
check.
|
||||
- Validates page numbers, clamps requests beyond the last page, escapes subjects
|
||||
and search text, and provides clear no-match and page-count displays.
|
||||
- Opening a ticket preserves list context. Reply redirects retain page/search
|
||||
state; a newly created ticket returns to an unfiltered first page. Successful
|
||||
submissions still redirect to GET, avoiding resubmission on refresh.
|
||||
- Tests use 102 personal tickets, a private ticket from another member and a ticket
|
||||
in another club. They verify complete nonduplicated traversal, old-ticket search,
|
||||
literal wildcard handling, escaped HTML, private counts, malformed/tampered
|
||||
requests, old-ticket replies and the browser navigation controls.
|
||||
|
||||
## Other changed files
|
||||
|
||||
`Base::VERSION` is now `1.03` for browser cache refresh. Updated `tests/run.py`
|
||||
to include all new repair suites, test fixtures/workers to create owned data and
|
||||
control concurrency, and release-package tests to require the new transaction
|
||||
helper and browser script. Updated the tenancy documentation and linked the
|
||||
review to this follow-up. New classes/scripts are automatically included by the
|
||||
existing release builder.
|
||||
|
||||
## Verification results
|
||||
|
||||
The complete regression suite passed: **584 checks and nine concurrency
|
||||
scenarios**, including the existing authentication, tenant isolation, financial,
|
||||
backup and deployment checks. New coverage includes nine invitation checks,
|
||||
eight transaction retry checks, 18 Unicode HTTP checks, 44 ticket paging HTTP
|
||||
checks and eight ticket browser checks. The nine concurrency scenarios include
|
||||
two new invitation/membership races.
|
||||
|
||||
Source validation passed for 53 PHP files, 57 application JavaScript files and
|
||||
39 XSL files, along with bundled editor and jQuery integrity checks. Tests used
|
||||
disposable databases on the isolated local server; no development database
|
||||
migration or development data changes were needed.
|
||||
|
||||
The remaining MFA,
|
||||
capacity and production-host acceptance work in the review is outside these four
|
||||
repairs; these changes do not assert that production hosting has been validated.
|
||||
Reference in New Issue
Block a user