flex_roles_access_inventory.md 13 KB

RBAC inventory from current code and DB

Source date: 2026-05-14.

This document is the baseline for RBAC migration. The first migration must preserve the current effective access of the five existing roles.

DB role snapshot

Collected from users.role with soft-deleted users included.

Role Total users Active Deleted
admin 4 3 1
assistant_head 2 2 0
brigadier 9 7 2
manager 5 4 1
warehouse_head 2 2 0

Role deletion rule for new RBAC: a role cannot be deleted while any users.role_id points to it, including soft-deleted users.

Current role semantics

Current role checks are not a simple equality check.

Role::effectiveRoles() currently expands:

assistant_head -> assistant_head, admin, manager
all other roles -> themselves

Consequences:

  • assistant_head passes hasRole('admin').
  • assistant_head passes route middleware role:admin.
  • assistant_head passes hasRole('admin,manager').
  • direct DB queries like where('role', Role::ADMIN) do not include assistant_head.

During RBAC migration assistant_head must receive its own explicit permissions equivalent to current route/helper access. Do not implement role inheritance.

User-facing modules found in controllers/routes

Module key Current table id / controller source Main controller(s)
orders orders OrderController
reclamations reclamations ReclamationController
schedule schedule ScheduleController
catalog products ProductController
maf product_sku ProductSKUController
maf_orders maf_order MafOrderController
contracts contracts ContractController
contractors controller-level $id ContractorController
responsibles responsibles ResponsibleController
reports reports ReportController
users users UserController
profile no table id UserController
import import ImportController
admin_settings no table id AdminSettingsController
districts controller-level $id AdminDistrictController
areas controller-level $id, ajax areas route AdminAreaController, AreaController
clear_data no table id ClearDataController
year_data no table id YearDataController
notifications notifications UserNotificationController
notification_logs notification_logs AdminNotificationLogController
spare_parts spare_parts SparePartController
spare_part_orders spare_part_orders SparePartOrderController
spare_part_reservations ajax/list endpoints SparePartReservationController
spare_part_inventory spare_part_inventory SparePartInventoryController
pricing_codes pricing_codes PricingCodeController
chat_messages no table id ChatMessageController
filters no table id FilterController

Current route/module access baseline

Legend:

  • auth means any authenticated user.
  • admin includes assistant_head through effectiveRoles() when checked through hasRole() or role:*.
  • object-level means additional record-level filtering/checking exists and must remain in Policies/scopes.
Module Current access from routes/code RBAC module permissions to seed
orders index, show, photo upload, photo pack, tech docs, chat create: auth. Edit/store/update/document upload/statement upload/photo/document/statement delete/document generation/reports group: admin,manager. Delete, create, export-one, MAF attach/revert/move, TTN, bulk photo/document/statement delete: admin. Contractor specification: admin,assistant_head. Brigadier and warehouse have object-level filtering in controller. orders.view, orders.create, orders.update, orders.delete, orders.export, orders.photos.upload, orders.photos.delete, orders.documents.upload, orders.documents.delete, orders.documents.generate, orders.maf.manage, orders.ttn.create, orders.contractor_specification.create, orders.chat.create, orders.chat.delete
reclamations index, show, photo before/after upload, chat create: auth. Create/update/status/document upload/details/spare parts/delete photos/generate packs: admin,manager. Upload act: admin,manager,brigadier,warehouse_head. Delete act/document/export: admin,manager. Delete reclamation/chat message: admin. Brigadier and warehouse have object-level filtering/checking in controller. reclamations.view, reclamations.create, reclamations.update, reclamations.delete, reclamations.export, reclamations.status.update, reclamations.documents.upload, reclamations.documents.delete, reclamations.documents.generate, reclamations.photos.upload, reclamations.photos.delete, reclamations.act.upload, reclamations.act.delete, reclamations.spare_parts.manage, reclamations.chat.create, reclamations.chat.delete
schedule Index: auth. Create from order/reclamation, update, export, delete: admin. Brigadier sees own records through object-level filtering and has controller checks for some actions. schedule.view, schedule.create, schedule.update, schedule.delete, schedule.export
catalog Index/show: admin,manager. Create/store/update/delete/export/delete certificate: admin. Upload certificate/thumbnail currently in admin,manager route group, but edit UI shows upload only for admin; seed as admin to match visible UI or audit before migration. Product search endpoint: auth. catalog.view, catalog.create, catalog.update, catalog.delete, catalog.export, catalog.certificates.upload, catalog.certificates.delete, catalog.thumbnail.upload, catalog.search
maf Product SKU index/show/update/upload passport: admin,manager. Import/export/delete passport: admin. Blade edit fields mostly admin-only. maf.view, maf.update, maf.import, maf.export, maf.passports.upload, maf.passports.delete
maf_orders All routes are inside role:admin, therefore admin and assistant_head currently pass middleware. Blade also uses hasRole('admin,assistant_head'). maf_orders.view, maf_orders.create, maf_orders.update, maf_orders.delete, maf_orders.stock.manage
contracts Routes are in role:admin,manager group, but StoreContractRequest currently authorizes only admin. Effective write behavior is stricter than routes. contracts.view, contracts.create, contracts.update, contracts.delete
contractors Routes are inside parent admin,manager group and child role:admin,assistant_head. Effective access: admin and assistant_head; manager does not pass child middleware. contractors.view, contractors.create, contractors.update, contractors.delete, contractors.prices.update, contractors.prices.import, contractors.prices.export
responsibles Routes and FormRequest: admin,manager. responsibles.view, responsibles.create, responsibles.update, responsibles.delete
reports Route: admin,manager. reports.view
users Admin route group. Create/update/delete/restore/impersonate: admin through middleware/FormRequest. users.view, users.create, users.update, users.delete, users.restore, users.impersonate, users.permissions.manage
profile View/update/delete own profile: auth; object-level own-user rules. profile.view, profile.update, profile.delete
import Admin route group. import.view, import.create
admin_settings Admin route group. admin.settings.view, admin.settings.update
districts Admin route group with CRUD/import/export/restore. districts.view, districts.create, districts.update, districts.delete, districts.restore, districts.import, districts.export
areas Admin dictionary route group with CRUD/import/export/restore. Public ajax areas/{district_id?} is auth. areas.view, areas.create, areas.update, areas.delete, areas.restore, areas.import, areas.export, areas.ajax.view
clear_data Admin route group. admin.clear_data.view, admin.clear_data.delete
year_data Admin route group with import/export/download/stats. admin.year_data.view, admin.year_data.import, admin.year_data.export, admin.year_data.download
notifications User notifications: auth. notifications.view, notifications.mark_read
notification_logs Admin route group. admin.notification_logs.view
spare_parts Index/help/search/show: admin,manager. Create/store/update/delete/export/import/upload image: admin. Some edit fields readonly for non-admin/non-manager. spare_parts.view, spare_parts.create, spare_parts.update, spare_parts.delete, spare_parts.import, spare_parts.export, spare_parts.image.upload, spare_parts.search
spare_part_orders Index/create/show/store/update/ship/set stock: admin,manager. Delete/correct: admin. spare_part_orders.view, spare_part_orders.create, spare_part_orders.update, spare_part_orders.delete, spare_part_orders.ship, spare_part_orders.stock.manage, spare_part_orders.correct
spare_part_reservations All reservation routes: admin,manager. spare_part_reservations.view, spare_part_reservations.manage, spare_part_reservations.issue, spare_part_reservations.cancel, spare_part_reservations.reassign
spare_part_inventory Route: admin,manager. spare_part_inventory.view
pricing_codes CRUD routes: admin. get-description and search are currently auth without role restriction. pricing_codes.view, pricing_codes.create, pricing_codes.update, pricing_codes.delete, pricing_codes.search
chat_messages Create for orders/reclamations: auth plus object-level checks. Delete: admin. Notifications from chat UI: admin,manager. chat_messages.create, chat_messages.delete, chat_messages.notify
filters get-filters: auth; must be filtered by field visibility. filters.view

Seed matrix for built-in roles

The seeder must create these role permissions first, then manual RBAC changes can adjust them later.

Role Seed rule
admin All action permissions allow. All field view and update permissions allow. Deny is forbidden.
assistant_head Materialize current effectiveRoles() behavior: all permissions that admin and manager receive through role:*/hasRole(), plus direct assistant_head checks. Do not create runtime inheritance.
manager All permissions currently guarded by admin,manager, plus all auth permissions that are available to every authenticated user. Do not include admin-only permissions.
brigadier All auth permissions available to every authenticated user, plus reclamations.act.upload; preserve object-level restrictions for own orders/reclamations/schedule.
warehouse_head All auth permissions available to every authenticated user, plus reclamations.act.upload; preserve current warehouse object-level filtering in orders/reclamations/chat controllers.

Field ACL policy decisions

User decisions for implementation:

  • Field permissions use only view and update.
  • Export and import are controlled by action permissions. If a user has export/import action access, the export/import may include the whole document/data set for that action.
  • Document access is all-or-nothing. If a user has document permission, return the full document without field masking.
  • Hidden required fields are not validated as required for that user.
  • Readonly fields may be displayed, but backend must remove them from update payload.
  • If create requires a field the user cannot set, deny create unless a safe default exists.
  • Hidden fields must also be removed from filters, sorting, and search server-side.
  • assistant_head keeps current effective access on migration; permissions can be manually reduced later.
  • Roles cannot be deleted while any user, including soft-deleted users, references the role.
  • Permissions are cached; invalidate cache on role, permission, role assignment, and user permission changes.
  • A baseline test must be added before/with migration to prove built-in roles keep current access.

Current DB/user migration notes

  • Existing users.role values are valid and match the five system roles.
  • users.role_id can be backfilled directly by roles.slug = users.role.
  • Soft-deleted users must also receive role_id.
  • Role deletion checks must include soft-deleted users.

Known inconsistencies to preserve initially

These are not blockers, but they must not be "fixed" accidentally during the migration:

  • assistant_head passes role:admin, but direct where('role', Role::ADMIN) queries do not include it.
  • contracts routes allow admin,manager, but StoreContractRequest allows only admin.
  • catalog.upload-certificate and catalog.upload-thumbnail routes are in admin,manager group, while Blade only exposes controls to admin.
  • Some FormRequests use auth()->check() while routes are stricter. Keep route-level effective behavior during migration.
  • pricing-codes/get-description and pricing-codes/search are role-unrestricted inside auth group even though CRUD is admin-only.