Problem Description
The Equipment module currently lacks tenant-level data isolation. Although Equipment is modeled to belong to a specific Hospital, the service layer ignores this association. This leads to two critical vulnerabilities:
- Any authenticated hospital user can view, edit, or delete equipment records belonging to any other hospital.
- Due to the JPA mapping configuration (
insertable = false, updatable = false on the hospital relationship), all newly created equipment via the POST /api/equipment endpoint is saved without a hospital_id, resulting in permanently orphaned records in the database.
Steps to Reproduce
To reproduce unauthorized data modification:
- Authenticate as a user belonging to "Hospital A" and obtain a token.
- Send a
GET /api/equipment request to fetch the global inventory.
- Identify the
{id} of an equipment record that belongs to "Hospital B".
- Send a
DELETE /api/equipment/{id} or PUT /api/equipment/{id} request using Hospital A's token.
- The request succeeds, modifying or destroying Hospital B's data.
To reproduce orphaned data creation:
- Authenticate as a hospital user.
- Send a
POST /api/equipment request with a valid payload to add new equipment.
- Query the underlying database for the newly created record.
- Observe that the
hospital_id column is NULL.
Expected Behavior
GET /api/equipment should only return equipment associated with the currently authenticated user's hospital.
DELETE and PUT operations should verify that the requested equipment belongs to the authenticated user's hospital, throwing an authorization error if there is a mismatch.
POST /api/equipment should successfully link the newly created equipment to the authenticated user's hospital in the database.
Actual Behavior
GET /api/equipment exposes all equipment across all hospitals globally.
DELETE and PUT operations execute directly via the repository without verifying ownership, allowing cross-tenant data tampering.
- New equipment added via
POST is saved with a null hospital_id, failing to associate the record with the hospital that created it.
Evidence from Code
In EquipmentService.java:
public void deleteEquipment(Long id) {
// Blindly deletes the record without resolving security context or checking ownership
equipmentRepository.deleteById(id);
}
public Equipment addEquipment(Equipment equipment) {
// ...
// Saves directly without establishing the Hospital relationship
return equipmentRepository.save(equipment);
}
In Equipment.java:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "hospital_id", insertable = false, updatable = false)
private Hospital hospital; // Prevents hospital_id from being saved when equipment is persisted directly
In EquipmentController.java:
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('HOSPITAL')") // Only checks role, not tenant/ownership
public ResponseEntity<Void> deleteEquipment(@PathVariable Long id) { ... }
Environment
- Backend: Spring Boot 3.x
- Spring Data JPA
Impact
This breaks data isolation across the application. It allows unauthorized users to tamper with or destroy medical equipment records belonging to other institutions. Additionally, it corrupts the database by permanently creating orphaned data entries upon every new equipment insertion, rendering the equipment unusable for hospital-specific workflows.
Additional Notes
The issue stems from a disconnect between the JPA relationship mapping and the service layer logic. Filtering repository queries using the current user's security context and properly establishing the Hospital to Equipment association prior to saving will resolve both the unauthorized access and the orphaned record creation.
Problem Description
The Equipment module currently lacks tenant-level data isolation. Although
Equipmentis modeled to belong to a specificHospital, the service layer ignores this association. This leads to two critical vulnerabilities:insertable = false, updatable = falseon thehospitalrelationship), all newly created equipment via thePOST /api/equipmentendpoint is saved without ahospital_id, resulting in permanently orphaned records in the database.Steps to Reproduce
To reproduce unauthorized data modification:
GET /api/equipmentrequest to fetch the global inventory.{id}of an equipment record that belongs to "Hospital B".DELETE /api/equipment/{id}orPUT /api/equipment/{id}request using Hospital A's token.To reproduce orphaned data creation:
POST /api/equipmentrequest with a valid payload to add new equipment.hospital_idcolumn isNULL.Expected Behavior
GET /api/equipmentshould only return equipment associated with the currently authenticated user's hospital.DELETEandPUToperations should verify that the requested equipment belongs to the authenticated user's hospital, throwing an authorization error if there is a mismatch.POST /api/equipmentshould successfully link the newly created equipment to the authenticated user's hospital in the database.Actual Behavior
GET /api/equipmentexposes all equipment across all hospitals globally.DELETEandPUToperations execute directly via the repository without verifying ownership, allowing cross-tenant data tampering.POSTis saved with anullhospital_id, failing to associate the record with the hospital that created it.Evidence from Code
In
EquipmentService.java:In
Equipment.java:In
EquipmentController.java:Environment
Impact
This breaks data isolation across the application. It allows unauthorized users to tamper with or destroy medical equipment records belonging to other institutions. Additionally, it corrupts the database by permanently creating orphaned data entries upon every new equipment insertion, rendering the equipment unusable for hospital-specific workflows.
Additional Notes
The issue stems from a disconnect between the JPA relationship mapping and the service layer logic. Filtering repository queries using the current user's security context and properly establishing the
HospitaltoEquipmentassociation prior to saving will resolve both the unauthorized access and the orphaned record creation.