Skip to content

FINERACT-2688: Fix loan disbursement for checker-only users via maker-checker permission check#6128

Open
aya-abdallah-FOO wants to merge 5 commits into
apache:developfrom
foodeveloper:port/CBS-509-loan-disbursement-checker-permission
Open

FINERACT-2688: Fix loan disbursement for checker-only users via maker-checker permission check#6128
aya-abdallah-FOO wants to merge 5 commits into
apache:developfrom
foodeveloper:port/CBS-509-loan-disbursement-checker-permission

Conversation

@aya-abdallah-FOO

Copy link
Copy Markdown

Summary

Fixes a bug where a user holding only a role-specific _CHECKER permission (e.g. DISBURSE_LOAN_CHECKER) was rejected with a permission error instead of being routed to the
pending maker-checker approval queue.

Root causes:

  • PortfolioCommandSourceWritePlatformServiceImpl.logCommandSource() called validateHasPermissionTo(taskPermission) unconditionally, rejecting checker-only users
  • CommandSourceService.validateMakerChecker() only marked a command as checked for CHECKER_SUPER_USER, ignoring role-specific _CHECKER permissions

Changes:

  • logCommandSource(): detect checker-only users (has _CHECKER but not base permission), auto-approve the pending AWAITING_APPROVAL entry; block makers from duplicate pending
    submissions
  • validateMakerChecker(): replace isCheckerSuperUser() with hasNotPermissionForAnyOf("CHECKER_SUPER_USER", permission + "_CHECKER")
  • Add CommandSourceRepository.findPendingByActionAndEntityAndResource() native SQL query
  • Add MakerCheckerCheckerOnlyInitiationException and MakerCheckerDuplicatePendingSubmissionException (both → HTTP 403)

JIRA

https://issues.apache.org/jira/browse/FINERACT-2688

Test plan

  • Enable maker-checker for loan disbursement
  • Maker submits → queued as AWAITING_APPROVAL
  • Maker submits again → HTTP 403 error.msg.maker.checker.duplicate.pending.submission
  • Checker-only user (has DISBURSE_LOAN_CHECKER) submits → auto-approves and disburses
  • Checker-only with no pending entry → HTTP 403 error.msg.maker.checker.checker.only.cannot.initiate
  • Existing /makerchecker/{id} approve/reject flow unaffected

@aya-abdallah-FOO
aya-abdallah-FOO force-pushed the port/CBS-509-loan-disbursement-checker-permission branch 2 times, most recently from 30e1794 to f830f34 Compare July 13, 2026 15:01

@Aman-Mittal Aman-Mittal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a integration test for this change before merge

Which explain whole process for this fix for verification

Enable maker-checker for loan disbursement
Maker submits → queued as AWAITING_APPROVAL
Maker submits again → HTTP 403 error.msg.maker.checker.duplicate.pending.submission
Checker-only user (has DISBURSE_LOAN_CHECKER) submits → auto-approves and disburses
Checker-only with no pending entry → HTTP 403 error.msg.maker.checker.checker.only.cannot.initiate
Existing /makerchecker/{id} approve/reject flow unaffected

this.context.authenticatedUser(wrapper).validateHasPermissionTo(wrapper.getTaskPermissionName());
final String taskPermission = wrapper.getTaskPermissionName();
final boolean hasBasePermission = !currentUser.hasNotPermissionForAnyOf(taskPermission);
final boolean hasCheckerPermission = !currentUser.hasNotPermissionForAnyOf("CHECKER_SUPER_USER", taskPermission + "_CHECKER");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Logic can be simplified more

@Aman-Mittal

Copy link
Copy Markdown
Member

@aya-abdallah-FOO also please amend your commit to exclude AI co-author

@Aman-Mittal Aman-Mittal added bug Needs Functional Review PRs which pass build and have no obvious technical problems, but need functional review. java Pull requests that update Java code labels Jul 14, 2026
@aya-abdallah-FOO
aya-abdallah-FOO force-pushed the port/CBS-509-loan-disbursement-checker-permission branch from b71128f to 62ec993 Compare July 14, 2026 09:34
@Aman-Mittal

Copy link
Copy Markdown
Member

@aya-abdallah-FOO Also please sign your commits

@aya-abdallah-FOO
aya-abdallah-FOO force-pushed the port/CBS-509-loan-disbursement-checker-permission branch from 549814c to b0bd55d Compare July 14, 2026 10:19
…-checker permission check

When maker-checker is enabled, a user holding only the <ACTION>_CHECKER
permission (e.g. DISBURSE_LOAN_CHECKER) was rejected with a permission
error instead of being routed to the pending approval queue.

- logCommandSource(): detect checker-only users, look up the pending
  AWAITING_APPROVAL command for the same action/entity/resource, and
  auto-approve it; block makers from duplicate pending submissions
- validateMakerChecker(): broaden approval condition from isCheckerSuperUser()
  to also accept role-specific _CHECKER permissions
- Add CommandSourceRepository.findPendingByActionAndEntityAndResource()
  native query (searches all resource-id columns)
- Add MakerCheckerCheckerOnlyInitiationException and
  MakerCheckerDuplicatePendingSubmissionException

Signed-off-by: aya.abdallah <aya.abdallah@foo.mobi>
…ix MakercheckerTest

The previous change replaced user.isCheckerSuperUser() with a broader
userHasCheckerPermission check that also matched task-specific _CHECKER
permissions. This caused makers who hold both base and _CHECKER permissions
to have their commands auto-approved instead of routed to AWAITING_APPROVAL,
breaking MakercheckerTest across all three databases.

The checker-only flow introduced by FINERACT-2688 is handled upstream in
logCommandSource, so validateMakerChecker never sees checker-only users and
needs no change.

Signed-off-by: aya.abdallah <aya.abdallah@foo.mobi>
@aya-abdallah-FOO
aya-abdallah-FOO force-pushed the port/CBS-509-loan-disbursement-checker-permission branch from b0bd55d to 89d50b9 Compare July 14, 2026 10:21
@aya-abdallah-FOO

Copy link
Copy Markdown
Author

@aya-abdallah-FOO Also please sign your commits

@Aman-Mittal can you please check if all is good?

@Aman-Mittal

Copy link
Copy Markdown
Member
  • Fineract Signed Commits Check / Verify Commit Signatures (pull_request)

yes now properly signed

…t maker-checker flow

Covers all five scenarios:
- Maker submits disbursement → queued as AWAITING_APPROVAL
- Maker submits again → HTTP 403 duplicate pending submission
- Checker-only user with no pending entry → HTTP 403 cannot initiate
- Checker-only user auto-approves pending entry and disburses
- Traditional /makerchecker/{id}?command=approve flow unaffected

Signed-off-by: aya.abdallah <aya.abdallah@foo.mobi>
@aya-abdallah-FOO
aya-abdallah-FOO force-pushed the port/CBS-509-loan-disbursement-checker-permission branch from 89d50b9 to 106bac4 Compare July 14, 2026 10:59
Two fixes for the flaky CI failures in MySQL/MariaDB shards:

1. Move global config updates (MAKER_CHECKER, ENABLE_SAME_MAKER_CHECKER) inside
   the try block so the finally cleanup is guaranteed even if the second update
   throws — preventing MAKER_CHECKER=true from leaking to subsequent tests in the
   same shard and causing unexpected maker-checker queuing.

2. Narrow findPendingByActionAndEntityAndResource to match only resource_id,
   loan_id, and savings_account_id. The previous query also matched client_id,
   office_id, group_id, product_id, and other contextual columns. When a loan ID
   coincidentally equalled a contextual ID stored on another pending entry, the
   wrong command was returned — causing Scenario 3 (checker-only no-entry → 403)
   to auto-approve the wrong loan instead of throwing, breaking the assertion.
@aya-abdallah-FOO

Copy link
Copy Markdown
Author

@Aman-Mittal can you please run the CI and if you know why would any of them still fail?

@aya-abdallah-FOO

Copy link
Copy Markdown
Author

CI Results — Shard 6 passed; Shard 4 and Kafka failures explained below

Shard 6 (our test shard) - Passed on MySQL, MariaDB, PostgreSQL

LoanDisbursementMakerCheckerIntegrationTest lands in Shard 6 of 15 (confirmed via the round-robin sort order). All three DB variants passed on the latest commit (b1fc327).

Shard 4 failures (MySQL, MariaDB, PostgreSQL) — test-suite reshuffling, not a logic bug

Adding LoanDisbursementMakerCheckerIntegrationTest inserts a new class alphabetically between LoanDisbursalDateValidationTest and LoanDisbursementDetailsIntegrationTest, shifting the round-robin shard assignment of every test class that sorts after it. As a result, tests that were previously in Shard 3 on develop (e.g. LoanMultipleDisbursementRepaymentScheduleTest, UndoRepaymentWithDownPaymentIntegrationTest) now run in Shard 4 after the original Shard-4 tests, creating a new execution ordering.

Our production code changes are guarded behind the global MAKER_CHECKER configuration (defaults to false/disabled), so the new logCommandSource paths and CommandSourceRepository query are never exercised in any of the Shard 4 tests. The failures are not in our code's execution path.

One specific pre-existing fragility we found: UndoRepaymentWithDownPaymentIntegrationTest enables ENABLE_BUSINESS_DATE at the top of its @test method without a try/finally — if the test fails mid-way, the config leaks into subsequent tests. This issue exists on develop too but didn't surface before because the test ran in Shard 3 with different predecessors. Could a maintainer check the artifact test-results-*-shard-4-attempt-2 to confirm which specific test is the first to fail? That would let us pinpoint the root cause and file a targeted fix for those tests.

Kafka Smoke Test — transient Docker infrastructure failure

The failure is in step 8 "Build the image" (jibDockerBuild), before any Kafka tests run. This is a Docker daemon / image-pull issue in the CI runner, unrelated to our Java changes (Shard 6 compilation and startup succeeded). Could a maintainer re-run just the Kafka smoke test job? @Aman-Mittal

@adamsaghy

Copy link
Copy Markdown
Contributor

@aya-abdallah-FOO Please review the failing tests.

@aya-abdallah-FOO

Copy link
Copy Markdown
Author

CI Results — Shard 6 passed; Shard 4 and Kafka failures explained below

Shard 6 (our test shard) - Passed on MySQL, MariaDB, PostgreSQL

LoanDisbursementMakerCheckerIntegrationTest lands in Shard 6 of 15 (confirmed via the round-robin sort order). All three DB variants passed on the latest commit (b1fc327).

Shard 4 failures (MySQL, MariaDB, PostgreSQL) — test-suite reshuffling, not a logic bug

Adding LoanDisbursementMakerCheckerIntegrationTest inserts a new class alphabetically between LoanDisbursalDateValidationTest and LoanDisbursementDetailsIntegrationTest, shifting the round-robin shard assignment of every test class that sorts after it. As a result, tests that were previously in Shard 3 on develop (e.g. LoanMultipleDisbursementRepaymentScheduleTest, UndoRepaymentWithDownPaymentIntegrationTest) now run in Shard 4 after the original Shard-4 tests, creating a new execution ordering.

Our production code changes are guarded behind the global MAKER_CHECKER configuration (defaults to false/disabled), so the new logCommandSource paths and CommandSourceRepository query are never exercised in any of the Shard 4 tests. The failures are not in our code's execution path.

One specific pre-existing fragility we found: UndoRepaymentWithDownPaymentIntegrationTest enables ENABLE_BUSINESS_DATE at the top of its @test method without a try/finally — if the test fails mid-way, the config leaks into subsequent tests. This issue exists on develop too but didn't surface before because the test ran in Shard 3 with different predecessors. Could a maintainer check the artifact test-results-*-shard-4-attempt-2 to confirm which specific test is the first to fail? That would let us pinpoint the root cause and file a targeted fix for those tests.

Kafka Smoke Test — transient Docker infrastructure failure

The failure is in step 8 "Build the image" (jibDockerBuild), before any Kafka tests run. This is a Docker daemon / image-pull issue in the CI runner, unrelated to our Java changes (Shard 6 compilation and startup succeeded). Could a maintainer re-run just the Kafka smoke test job? @Aman-Mittal

@adamsaghy FYI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug java Pull requests that update Java code Needs Functional Review PRs which pass build and have no obvious technical problems, but need functional review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants