generated from jric11/baseProject
123 lines
6.7 KiB
Markdown
123 lines
6.7 KiB
Markdown
# 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.
|