Admin API

Authentication model, current capabilities, and roadmap for admin-scoped REST endpoints

Executive Summary

Admin API v5.9.3.3: Authentication plus admin-protected read, write, and DELETE endpoints for properties, invoices, tenants, reservations, expenses, housekeeping, and read-only reports. Login via /API/auth/V1/login.json, then call admin* controllers with the session cookie. Writes and DELETE require super_admin; svc_rep is staff read-only; landlord (adminRole=1, non-superuser) has read-only access scoped to owned properties.

The WiseLoop framework uses a two-tier model — public controllers on an allowlist, and admin* controllers requiring an authenticated admin session via wlRestTriggerAuth. Role checks (super_admin, svc_rep) are enforced per action via RPM\Api\AdminAuthGuard.

Current State vs. Intended Design

ComponentStatusLocation
Admin auth controller Shipped wlRestControllerAuth — login, logout, user
Admin credential storage Shipped admins.adminAPIKey, admins.adminAPISecret
Auth trigger on requests Shipped wlRestTriggerAuth in API/index.html
Public controller allowlist Shipped API/MySqlAuthHandler.html — marketing + guest flows only
Admin read endpoints (properties, invoices, tenants, reservations, expenses) Shipped adminProperties, adminInvoices, adminTenants, adminReservations, adminExpenses
Admin write endpoints (POST/PUT) Shipped Properties, invoices, tenants, reservations, expenses, housekeeping
Admin DELETE endpoints (soft-delete / cancel) Shipped propertyDelete, invoiceDelete, tenantDelete, reservationDelete, expenseDelete, requestDelete
Admin reports (read-only) Shipped adminReportsincomeGet, occupancyGet
Role-scoped API access Shipped AdminAuthGuard + LandlordScope — staff reads: super_admin + svc_rep; landlord read-only on owned data; writes/DELETE: super_admin only

What this means in practice: Website integrations continue using public endpoints (e.g. /API/vrProperties/V1/vrProperties.json). Server-to-server admin integrations call /API/auth/V1/login.json first, then access admin* endpoints with the session cookie. Landlords (adminRole=1, not superuser) may read their own properties, invoices, reservations, expenses, and tenants via API but cannot POST, PUT, or DELETE. Housekeepers have no admin API data access.

Landlord read scope (adminRole=1)

ControllerLandlord accessScope rule
adminPropertiesGET onlyLTR: assignedproperties.adminId; VR: Vrproperties.landlord
adminInvoicesGET onlyInvoice propertyId owned by landlord
adminReservationsGET onlyVR booking on landlord-owned property
adminExpensesGET onlyExpense propertyId owned by landlord
adminTenantsGET onlyTenant propertyId on landlord LTR assignment
adminHousekeeping, adminReportsDeniedStaff roles only (super_admin, svc_rep)

DELETE semantics (super_admin only)

ActionHTTPBehavior409 when
propertyDeleteDELETE …/properties/{id}.jsonSets isArchived=1Active future VR reservations
invoiceDeleteDELETE …/invoices/{id}.jsonCancel invoice (status=5), reverse journalSettled payments exist
tenantDeleteDELETE …/tenants/{id}.jsonDeactivate (isActive=0)
reservationDeleteDELETE …/reservations/{id}.jsonCancel booking (status=3); cancel unpaid invoicesAlready cancelled
expenseDeleteDELETE …/expenses/{id}.jsonCancel expense (status=3)Settled payments exist
requestDeleteDELETE …/requests/{id}.jsonArchive housekeeping request (status=6)Already archived

Authentication Flow

When admin-protected endpoints are added, this is how authentication works:

// 1. Login — establishes wlSession with admin data
POST /API/auth/V1/login.json
Content-Type: application/json

{
  "adminAPIKey": "your-api-key",
  "adminAPISecret": "your-api-secret"
}

// Response (session cookie set):
{
  "adminId": 1,
  "adminFirstName": "Jane",
  "adminLastName": "Admin",
  "adminRole": "0",
  "superuser": "1"
}

// 2. Verify session
GET /API/auth/V1/user.json
// or single field:
GET /API/auth/V1/user/adminEmail.json

// 3. Logout
POST /API/auth/V1/logout.json

The session is managed by WiseLoop wlSession (key: wlAuthData). Requests to non-public admin* controllers require this session cookie. Per-action role checks return 403 Forbidden when the admin role is not allowed.

Admin Credentials

FieldTableNotes
adminAPIKey admins API username. Fresh installs default to 0 until configured via myAdmin or network API.
adminAPISecret admins API password. Stored as plain text (not MD5 — isPasswordMd5 = false in API/index.html).
adminRole admins 0 = super_admin, 1 = landlord, 2 = svc_rep, 3 = housekeeper
superuser admins Additional superuser flag returned in auth response
Admin API credentials are separate from myAdmin portal login (adminEmail / password). Portal sessions use PHP $_SESSION['adminId']; REST admin auth uses wlSession.

Auth Endpoints (Shipped)

POST /API/auth/V1/login.json

Authenticates adminAPIKey + adminAPISecret against the admins table via PDO prepared statement.

Errors: 401 Invalid credentials

POST /API/auth/V1/logout.json

Clears wlAuthData from session.

GET /API/auth/V1/user.json

Returns full admin session object. Optional path segment returns a single field.

Errors: 401 Unauthorized if not logged in

Admin Data Endpoints (Shipped — Phase 3 + 4 writes)

Registered in API/index.html, not on the public allowlist. Requires login + role.

ControllerActionsRolesService
adminProperties propertiesGet, propertyGet, propertyPost, propertyPut read: super_admin, svc_rep · write: super_admin rpm_property_catalog_service()
adminInvoices invoicesGet, invoiceGet, invoicePost, invoicePut read: super_admin, svc_rep · write: super_admin rpm_finance_invoice_service()
adminTenants tenantsGet, tenantGet, tenantPost, tenantPut read: super_admin, svc_rep · write: super_admin rpm_admin_tenant_service()
adminReservations reservationsGet, reservationGet, reservationPost, reservationPut read: super_admin, svc_rep · write: super_admin rpm_booking_reservation_service()

Write actions log to activityLog via rpm_admin_activity_log_service() on every successful POST/PUT.

Full request/response field reference: API Reference — Admin sections.

Public Allowlist

Controllers in MySqlAuthHandler::isAuthorized() bypass admin auth entirely.

ControllerPurpose
siteSettingsPublic site configuration
siteContentCMS content blocks
sitePagesStatic pages
siteReviewsProperty reviews
siteSlidersHomepage sliders
siteNavigationNav menus
siteFaqFAQs
siteEventsEvents
siteAttractionsAttractions + categories
siteContactContact form, newsletter
vrPropertiesVR property catalog
ltrPropertiesLTR property catalog
networkBlogBlog posts and categories
publicAuthTenant login/register/activate
publicBookingAvailability, quotes, booking
leaseApplicationsLease applications (session required in action)
authAdmin login/logout/user (always public per WiseLoop)

Future Work

Phase 4 shipped write endpoints. Phase 5 (v5.9.3.3) completed optional admin surface:

ControllerStatusService
adminExpensesShippedrpm_finance_expense_service()
adminHousekeepingShippedrpm_housekeeping_service()
adminReportsShipped (read-only)rpm_finance_report_service()
Landlord-scoped readsShippedRPM\Api\LandlordScope
DELETE endpointsShippedSoft-delete / cancel per finance invariants

Each new controller should:

  1. Be registered in API/index.html
  2. Not appear in MySqlAuthHandler public allowlist
  3. Delegate to existing PSR-4 services (thin controller pattern)
  4. Check adminRole / superuser for action-level authorization
  5. Use prepared statements and match systemDocuments/myAdmin/coding-standards.md

Recommended Implementation Roadmap

PhaseWorkOutcome
Phase 1 ApiSecurityPolicy, site settings panel, auth off public allowlist, session bootstrap Complete
Phase 2 CSRF on financial pages, bcrypt, CMS sanitization, login lockout Complete
Phase 3 admin* read endpoints with role checks; APIDocs + OpenAPI + Postman Complete
Phase 4 Write endpoints (POST/PUT) with audit logging, per-key rate limits, landlord read scoping Planned
Phase 5 OAuth2/JWT alternative to session cookies for mobile clients Planned

Legacy Unregistered Controllers

These files exist in API/ but are not registered in API/index.html. They contain SQL injection patterns and should not be enabled without a full security rewrite:

FileIssueAction
networkBlogRestControlerV1.html (typo)SQL concatDelete — superseded by networkBlogRestControllerV1.html
networkVideoRestControlerV1.htmlSQLi, unregisteredRemove or rebuild with services
networkTestimonialRestControlerV1.htmlSQLiRemove or rebuild with services

Security Notes

  • API-001 (fixed): Public allowlist default-denies unknown controllers; admin* controllers require session.
  • Role enforcement: AdminAuthGuard::requireRole() maps adminRole/superuser to super_admin, svc_rep, etc.
  • Credential storage: adminAPISecret is plain text in DB — consider hashing before Phase 3 write endpoints.
  • Separate auth domains: Tenant session (publicAuth), admin portal session (myAdmin), and REST admin session (wlSession) are three independent mechanisms.
  • Integration scripts (Stripe, QuickBooks, BookingPal) use their own auth — not the wlRest admin auth. See Integration Scripts.
Mobile / cookieless clients: The WiseLoop session model works for server-to-server integrations. Mobile clients without cookie support may need Phase 5 token auth — use server-side proxy until then.