fix(permissions): enforce Fleet-Ops permissions across the API and console - #345
Merged
Merged
Conversation
…mapped endpoints Schema - Declare resources the UI already checked but the schema never created, which left their API unguarded and their screens unreachable for non-admins: maintenance-schedule, device, sensor, device-event, telematic, warranty, purchase-rate, fuel-provider-connection/-transaction/-sync-run, analytics, scheduling-settings, tracking-settings. service-rate gains export. - Fix the 'action' => 'actions' key on the settings resources, so `fleet-ops onboard payments` is actually created. - Grant the new resources in the built-in policies (FleetManager, MaintenanceManager, ServiceRateManager, OperationsAdmin, DispatchManager). Custom actions (Support\Authorization + #[SkipAuthorizationCheck]) - AuthorizationGuard maps unmatched method names by HTTP verb, so e.g. bulk-dispatch required `create order`, unassign-vehicle `create driver` and trailer attach `create trailer`. Map them to the schema actions: dispatch, cancel, schedule, import, assign-driver-for, update-route-for order; assign-order-for / assign-vehicle-for / update-user-for driver; attach-/detach-*-for trailer; and update on vehicle, vendor, device, telematic, maintenance-schedule, maintenance, work-order and fuel-provider-* sub-actions. Controllers outside the guard - Analytics and metrics require `view analytics`; live map feeds require list order/driver/vehicle/place; orchestrator, manifests, radar writes, customer portal logins and Stripe payments now require their permissions. - Settings writes require the matching *-settings permission; the admin tracking/map settings require a system admin. - Entity editing settings are one platform-wide map: a save now only writes the session company's order configs and keeps other companies' entries, and reads return only the company's own. Driver onboard settings are pinned to the session company instead of a request-supplied company id. - Navigator link-app returned an API key to unauthenticated callers. The link is now a 30 minute signed URL issued only to system admins, and link-app rejects unsigned requests.
…ermission - List guards on every index route that lacked one (orders, routes, trailers, fuel transactions, integrated vendors, all maintenance and connectivity routes, analytics, settings) and create/view/update guards on new, details and edit routes. Orders is the landing route, so it forwards users without `list order` to the first area they can open. - Index New/Import/Export buttons, bulk actions and row actions pass their permission; the order details menu hides actions the user cannot perform. - Sidebar hub items keep their permissions, branch defaults skip routes the user cannot open, and items use the schema's resource names (analytics, fuel-provider-*, list custom-field/avatar). Reports use the iam report permissions that the core reports API enforces. - Header shortcuts carry their module permission; the virtual route honours menu item permissions. - Live map layers check singular resource names (list vehicle, not vehicles), so non-admin users see their layers. - Maintenance details/edit redirects used route names without the console.fleet-ops prefix and threw; fixed.
… controller middleware - Move the explicit checks out of method bodies into a declarative map: FleetOpsController registers `$methodPermissions` (method => permission) as controller middleware via the AuthorizesMethods trait, and non-resource controllers call authorizeMethods() in their constructor. Behaviour through the router is unchanged; unit tests that call controller methods directly keep exercising the endpoint logic. - Navigator links are signed with an HMAC over the expiry (NavigatorController::linkSignature) instead of Laravel's signed routes, so the link no longer depends on a named route or the URL generator. - Tests: signed/unsigned/tampered/expired Navigator links; entity editing settings keep other companies' entries and ignore keys for foreign order configs; driver onboard settings ignore a request-supplied company id.
This was referenced Sep 27, 2026
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
This PR comes out of an authorization audit of Fleet-Ops. Each finding was verified against a local stack as a non-admin (dispatcher) user.
Security
GET fleet-ops/navigator/link-appwas outside the authenticated group and redirected anonymous callers with an API key for the first system admin's organization.get-link-appis behindfleetbase.protected).link-apprejects unsigned, tampered or expired links with 403.companyId.Schema (
Auth/Schemas/FleetOps.php)maintenance-schedule,device,sensor,device-event,telematic,warranty,purchase-rate,fuel-provider-connection,fuel-provider-transaction,fuel-provider-sync-run,analytics,scheduling-settingsandtracking-settings.service-rategainsexport.'action'→'actions'on the settings resources, sofleet-ops onboard paymentsis actually created.Custom actions and non-resource controllers
AuthorizationGuardmaps an unmatched method by HTTP verb. For example, bulk dispatch requiredcreate order, unassign-vehicle requiredcreate driver, and trailer attach requiredcreate trailer.$methodPermissions(method → schema permission).FleetOpsControllerregisters it as controller middleware through the newAuthorizesMethodstrait, and those methods carry#[SkipAuthorizationCheck].view analytics.reset-credentials-for customer.*-settingspermission. Admin map and tracking settings: system admins.Console
list orderis sent to the first area they can open.iam … reportpermissions the core reports API enforces.list vehicles), so non-admins saw an empty map. They now use singular resource names.console.fleet-ops.prefix, and threw.Behaviour changes to note
dispatch order; it used to needupdate order. The built-in OrderCoordinator policy has no dispatch.view analytics.php artisan fleetbase:create-permissionsafter deploying.Companion PRs
fleetbase/core-api#278 (core authorization gaps, reports on
iam), fleetbase/ember-ui#185 (bulk action and header permissions), fleetbase/ember-core#94 (shortcut permissions).Test plan
server/testsfile run throughscripts/pest-runner.php. Two files fail, and they fail onrelease/v0.6.70too:InternalImportExportControllerContractsTestandSmallControllerContractsTest.unassign-vehicle(assign-vehicle-for driver), trailer attach (attach-vehicle-for trailer), settings saves, orchestrator run andget-link-app.link-app→ 403; signed → 302.