priyam99 opened a new pull request, #170:
URL: https://github.com/apache/fineract-loan-origination/pull/170

   ## Description
   
   When a customer asks for an application that belongs to someone else, the 
ownership check correctly blocks it, but the API answers with a 500. Besides 
looking like a server crash, that also gives something away: a reference that 
doesn't exist comes back as 404, so a 500 tells you the application is real and 
just isn't yours. Since references are sequential (`LOS-2026-00001`, `00002`, 
…), that's enough to probe for other customers' applications.
   
   The cause is in `CustomerLoanApplicationController.assertOwnedByCaller()`. 
It throws `AccessDeniedException`, which `GlobalExceptionHandler` doesn't 
handle, so it ends up in the catch-all 500.
   
   I changed it to throw `ApplicationNotFoundException`, the same exception the 
service throws when a reference doesn't exist. A non-owner now gets exactly the 
same 404 and message as for a missing application, so the two can't be told 
apart. No new handler was needed, since that exception already maps to 404. 
This is also what `docs/api/rest-api.adoc` already promises: "Returns `404` if 
not found or owned by a different customer."
   
   The same check guards three endpoints, so all of them are covered: 
application details, credit score, and submit. The issue only mentions the 
first two, but submit had the same problem.
   
   I kept this separate from #159 on purpose. Once ownership failures stop 
using `AccessDeniedException`, mapping that exception to 403 in #159 can't 
accidentally turn these into 403s and leak the same information.
   
   Closes #160
   
   ## Verification
   
   I ran `./mvnw spotless:apply`, `./mvnw apache-rat:check` and the full 
`./mvnw clean verify` with Docker running. Everything passes: 196 unit tests 
and 7 Cucumber scenarios.
   
   New tests in `CustomerLoanApplicationControllerTest` (MockMvc with the real 
service and a logged-in customer, only the repositories mocked):
   
   - the owner can view their application and its credit score (200)
   - another customer gets 404 with no application data for details and credit 
score, and the score is never read
   - another customer gets 404 trying to submit it, and it stays `DRAFT`
   - the 404 another customer gets is the same as the 404 for a reference that 
doesn't exist
   
   With the fix taken out, the four "another customer" tests fail with 500.
   
   I also ran the app locally (dev profile, mock Fineract) with two customers, 
Alice and Bob. Alice created `LOS-2026-00001` and Bob tried to read it.
   
   Before:
   
   ```
   $ curl -H 'Authorization: Bearer <bob>' 
.../api/v1/customer/loan-applications/LOS-2026-00001
   {"error":"Internal Server Error","message":"An unexpected error 
occurred.","status":500,...}
   HTTP 500
   
   $ curl -H 'Authorization: Bearer <bob>' 
.../api/v1/customer/loan-applications/LOS-2026-99999
   {"error":"Not Found","message":"No loan application found with reference 
[LOS-2026-99999] for tenant [default].","status":404,...}
   HTTP 404
   ```
   
   After:
   
   ```
   $ curl -H 'Authorization: Bearer <bob>' 
.../api/v1/customer/loan-applications/LOS-2026-00001
   {"error":"Not Found","message":"No loan application found with reference 
[LOS-2026-00001] for tenant [default].","status":404,...}
   HTTP 404
   
   $ curl -H 'Authorization: Bearer <bob>' 
.../api/v1/customer/loan-applications/LOS-2026-00001/credit-score
   HTTP 404   (same body)
   
   $ curl -X POST -H 'Authorization: Bearer <bob>' 
.../api/v1/customer/loan-applications/LOS-2026-00001/submit
   HTTP 404   (same body)
   
   $ curl -H 'Authorization: Bearer <alice>' 
.../api/v1/customer/loan-applications/LOS-2026-00001
   HTTP 200   (owner still works, status still DRAFT after Bob's submit attempt)
   ```
   
   No frontend changes.
   
   One thing I noticed but left out of scope: if an application somehow has no 
applicant profile, the ownership check fails with a different "profile not 
found" 404 before the comparison runs. That shouldn't happen with normal data, 
so I didn't change it here.
   
   ## AI assistance (optional)
   
   - Tool / model: Claude Code (Claude Opus 5.5)
   - How it was used: helped me trace the ownership check, draft the change and 
tests, and run the build and the local before/after check. I reviewed every 
line before pushing.
   
   ## Checklist
   
   - [x] This PR is one logical change, not a code dump.
   - [x] I added or updated tests, or explained why tests are not needed.
   - [x] New files carry the ASF license header.
   - [x] Docs are updated. (Nothing to change; the API docs already say 404 for 
this case.)
   - [x] All commits are signed.
   - [x] I followed the [AI 
Policy](https://github.com/apache/fineract/blob/develop/CONTRIBUTING.md#ai-policy)
 and the [ASF Generative Tooling 
Guidance](https://www.apache.org/legal/generative-tooling.html).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to