# Blade administration: Users and Organizations

Implemented 17 October 2026 in the existing Laravel backend. These screens use real application data and the existing web authentication guard. They are not a prototype and include no demo controls, role editor, project management, site-settings editor or deletion actions.

## Opening administration

Open `<backend APP_URL>/admin/login`, normally `http://localhost:8000/admin/login` when using `dev.cmd`. Sign in with an existing active `ultimate_admin` account. No administrator was added to the normal database during implementation. If an authorized operator needs to promote an existing account, the existing CLI command is `php artisan user:set-role existing-account@example.com ultimate_admin`; it changes that account's privileges and must be used intentionally.

The Blade console and React application share Laravel authentication. Logging out, deactivating an account or resetting its password affects that shared access. Normal users cannot access these routes even if they know the URL. Login is restricted to active super admins and rate-limited by email/IP plus an IP-level route throttle. Management forms require CSRF tokens.

The existing local route/configuration caches can hide new routes until refreshed. For an authorized development/release update, run `php artisan route:clear`; if deployment uses route caching, rebuild with `php artisan route:cache` after publishing the code. Follow the existing deployment sequence for configuration cache and long-lived processes. No new migration or JavaScript dependency is required for this console. Copy `public/admin-console/` along with the backend and Blade views; assets are served directly without a new Vite entry point.

## Users

- Search name/email, filter Active/Inactive and page through results. Search and status survive pagination and successful status changes.
- Add ordinary users with name, unique email, confirmed password and preferred language. Emails are normalized for new accounts; duplicate checks include existing mixed-case addresses. Role and verification values cannot be supplied through the form. No email is sent or verification status fabricated.
- Reset an ordinary user, another super admin or your own password. Existing sessions, remember tokens, API tokens and password-reset links are revoked. A self-reset signs you out and returns to login. The new password is never redisplayed, flashed into old input, included in audit metadata or emailed.
- Deactivate/reactivate without deleting projects or memberships. Deactivation is disabled for super admins and the final active administrator of an organization; the server independently enforces those rules. Add another active organization administrator using the existing organization People workflow before retrying a blocked ordinary account.

Password rules follow the application's existing `Password::defaults()` policy, with confirmation and a maximum input length. Share a newly assigned password through your approved secure channel. This change does not introduce a forced-first-login-password-change flow.

## Log in as a user or organization administrator

From **Users**, choose **Log in as user** and confirm. From **Organizations**, choose **Log in as administrator**; choose an active administrator when several are eligible. Both actions open the real React application in a **new tab already authenticated as that account**. The original Blade administrator tab remains authenticated as the super admin. Organization launches open the selected organization's overview. This uses the existing user's permissions and does not create an administrator or change anyone's role.

A persistent English/Arabic support banner identifies the account and organization. Actions affect real data. Choose **End session** to revoke this support session and close its tab; if automatic closing is unavailable, an ended-session screen remains. Normal sign-out in this tab uses the same flow. Navigating to the ending screen first allows the editor's existing unsaved-change warning to cancel navigation before revocation. Closing a tab alone does not immediately revoke its token; it expires within one hour. Signing out of the originating administrator account revokes its outstanding support sessions.

Inactive users and platform super admins cannot be impersonated. Organization launches require the organization feature to be enabled, an active organization, and an existing active, ordinary administrator membership. Every support API request rechecks the original super admin, target account, password stamps and selected organization administrator membership. Suspension, loss of eligibility, password changes, expiry and revocation invalidate access. Account-security mutations such as passwords and provider credentials are blocked during support sessions; language changes remain available.

The launch is a CSRF-protected, throttled POST with `target="_blank"` and `rel="noopener"`. It creates a single-use, one-minute handoff in the existing Sanctum token table. The frontend immediately removes its URL fragment and exchanges it for a one-hour token stored only in that tab's `sessionStorage`. Support API requests explicitly omit cookies and use bearer authentication; invalid support tokens never fall back to the original super-admin cookie. Ordinary Blade/React login continues to use the shared web session. Do not copy support links or tokens into logs, screenshots or tickets.

Routes added: `POST /admin/users/{user}/impersonate`, `POST /admin/organizations/{organization}/impersonate`, `POST /api/v1/auth/impersonation/exchange`, and `POST /api/v1/auth/impersonation/end`. The frontend handles `/admin/impersonate` and `/admin/impersonation-end`. Audit events `admin.impersonation.started`, `.action`, `.ended` and `.revoked` identify the actual super admin and target without recording credentials. Workspace audit events also retain the originating administrator ID during support requests.

Publish backend and frontend together. `FRONTEND_URL` must identify the frontend origin and CORS must permit that origin, `Authorization`, and `X-DocuDeck-Impersonation`. Refresh configuration and route caches using the existing deployment sequence. No new dependency or migration is required. Local caches were refreshed for this implementation; `.env` and the application database were not modified by this addition.

### Support-session validation, 17 October 2026

- Guarded in-memory SQLite backend suite: **168 tests, 2,478 assertions, no failures, one renderer-dependent skip**. The first invocation lacked the SQLite PHP extensions and failed without touching MySQL; rerunning with `-d extension=pdo_sqlite -d extension=sqlite3` passed.
- Focused Blade/support coverage: **21 tests, 265 assertions**. Covers one-use handoffs, CSRF and authorization, expiry, no cookie fallback, target project isolation, organization eligibility, password/suspension invalidation, token revocation, security mutation restrictions, audit credential exclusion and CORS headers.
- Frontend regression: **173 tests passed**; TypeScript/Vite production build and lint passed. API client checks cover tab bearer headers, cookie omission, ended sessions, external request isolation and bilingual banners.
- Browser review on disposable SQLite and reserved `.example` identities verified both new-tab launches, the selected user and organization context, original-admin retention after reload, session ending, and Arabic support-banner reflow at an actual 390 CSS-pixel viewport with no horizontal page overflow. The review used separate ports 8015/5187 and a separate cookie. No normal-account impersonation, real email or provider request was performed.
- Outstanding acceptance: deployed-host CORS/proxy behavior, MySQL concurrency, and an edited-project save/discard browser journey under a support session. Automated project authorization checks are not a substitute for those checks.

Screenshots: [User session](blade-admin-evidence/impersonation-user.jpg), [organization administrator session](blade-admin-evidence/impersonation-organization.jpg), [Arabic mobile session](blade-admin-evidence/impersonation-arabic-mobile.jpg).

## Organizations

- Search/filter/page existing organizations, including when organization creation is disabled. Rows show active administrators, retained active/suspended membership totals, project totals and current access status.
- Create an organization with its existing required fields and an active existing user as initial administrator. The email input suggests up to 50 accounts but accepts any existing active user's email. Create the user first if needed.
- Creation is atomic and idempotent for a retried form submission: organization, initial membership, free entitlements and audit event commit together. External sharing starts disabled. It neither sends an invitation nor marks the administrator's email verified.
- Creation requires `ORGANIZATIONS_ENABLED=true`. Existing inspection and status management remain available to support admins while the flag is off.
- Deactivation blocks member access and is honored by queued-work revalidation/publication checks. It retains memberships, assignments, projects and files. It cannot undo work already submitted to a provider or promise that no provider charges occur. Reactivation does not automatically requeue cancelled work.

## Integration

The new `/admin/*` routes are session-based Blade web routes; no new public JSON contract was introduced. Existing `/api/v1/admin/users/{user}` and `/api/v1/admin/organizations/{organization}` updates now use the same `AdminManagementService` as Blade. Existing API authorization and response contracts are retained.

Protected pages use `AdminConsoleAccess` before session password checks, explicitly selecting the web guard even after an API request. The login redirect applies to web requests; API requests retain their JSON unauthenticated response. English/Arabic selection is scoped to the admin session and does not overwrite the account's preference in React.

Mutation audit events use existing workspace audit storage: `admin.user.created`, `admin.user.updated`, `admin.user.password_reset`, `admin.organization.created`, and `admin.organization.updated`. There is no new audit navigation section. Passwords and tokens are excluded from event metadata. Existing personal and organization project permissions continue to apply.

## Validation and screenshots

- Focused Blade suite: **12 tests, 141 assertions**, passed.
- Full backend suite: **159 tests, 2,354 assertions**, no failures, one renderer-dependent skip.
- Full frontend regression: **168 tests**, TypeScript and Vite build passed. Existing React duplicate-key warnings remain in legacy test output; they were not introduced by the Blade console.
- New vanilla JavaScript syntax check passed. `git diff --check` passed.
- Actual browser review used the real Blade controllers on disposable SQLite, a separate session cookie and reserved `.example` accounts. It exercised login, user creation, password reset, organization creation, organization deactivation/reactivation, confirmation cancellation/Escape focus restoration, no-results search/clear recovery, language switching and mobile navigation. No live invitations or provider requests were made.
- Desktop and actual **390 CSS-pixel** mobile reflow were inspected in English and Arabic. Page width did not exceed viewport width in the inspected layouts. Additional screenshots record the relevant screens below. This is not an exhaustive accessibility audit or MySQL concurrency acceptance.

Screenshots use disposable review data only:

- [Users desktop](blade-admin-evidence/users-desktop.jpg)
- [Final Users screen](blade-admin-evidence/users-final-desktop.jpg)
- [Organizations desktop](blade-admin-evidence/organizations-desktop.jpg)
- [Create organization](blade-admin-evidence/organization-create.jpg)
- [Organization confirmation](blade-admin-evidence/organization-confirmation.jpg)
- [Arabic organizations](blade-admin-evidence/organizations-arabic.jpg)
- [Arabic mobile organizations](blade-admin-evidence/organizations-arabic-mobile.jpg)
- [Arabic mobile users](blade-admin-evidence/users-arabic-mobile.jpg)
- [English mobile users](blade-admin-evidence/users-english-mobile.jpg)
- [Mobile password reset](blade-admin-evidence/password-reset-mobile.jpg)

## Test-isolation incident and recovery status

The first Blade test invocation encountered existing cached local MySQL configuration and cached routes. The cached configuration overrode PHPUnit's SQLite environment variables, and Laravel's `RefreshDatabase` mechanism rebuilt the local `documents_generator_tools` database at approximately **12:29 on 17 October 2026**. This was an implementation error. It is not an authorized deployment or migration.

The normal database was found empty afterward. Recovery was prepared on a separate MySQL instance on port 3317 using a SHA-256-verified October 11 backup and copied binary logs through position **69,756,391 in `binlog.000165`**, immediately before the destructive transaction. Replay completed without errors, producing **5 users, 19 projects and 1 organization**, with zero queued jobs. Main table checks passed and creator/member foreign-reference checks found no orphans.

The recovered SQL is a local private artifact: `out/admin-recovery/recovered-before-reset.sql`, SHA-256 `9164f99aa534f658cffcabaf1de086c82792f9fa895a1540b7d4fe04e405e3e5`. It contains real application data and credentials in stored database form; do not commit, publish or attach it.

With explicit user approval, the normal local database was restored on **17 October 2026 at 12:49 Asia/Amman**. The pre-restore database was preserved in `out/admin-recovery/before-restore-20261017-094907.sql` with its SHA-256 sidecar. During maintenance mode, all **51 restored tables** matched row counts and complete row-data hashes against the validated recovery copy. The restored totals are **5 users, 19 projects and 1 organization**, including one active super-admin account. All **17 referenced source files** and **5 referenced package files** were found, and creator/member reference checks found no orphans. Machine-readable reports are in `out/admin-recovery/restore-verification.json` and `restored-files-verification.json`. The route cache was rebuilt to expose the new Blade routes, then maintenance mode was removed. Normal application requests may naturally change session/cache rows after this verification.

The temporary recovery MySQL instance on port 3317 was shut down after validation; its files and the recovered SQL remain preserved locally. The normal backend development server on port 8000 was not running at the final HTTP check. Start `dev.cmd` before opening the admin login; no successful normal-server HTTP/login check is claimed for this recovery step.

Tests now override local configuration/route cache paths **before application bootstrap**, force in-memory SQLite, array session/cache/mail transports and a null separate session connection, and refuse bootstrap if the isolated connection checks fail. The focused and full passing suites ran with the normal cached files still present; normal table creation timestamps did not change during those guarded runs. Future PHPUnit runs must retain this protection. Do not bypass it to run tests on the application database.

`.env` was not edited for the Blade implementation. No real account was seeded and no paid provider, live email or deployment was invoked. The incidental reset above is explicitly excluded from any claim of application-database isolation.
