From 6c0a71e131ac4159ae534a2fa5728c7815cb3f13 Mon Sep 17 00:00:00 2001 From: leksinomi Date: Thu, 24 Oct 2024 17:42:36 +0300 Subject: [PATCH] FINERACT-2081: Handle unauthenticated requests in LoanCOBApiFilter --- .../jobs/filter/LoanCOBApiFilter.java | 3 +- ...SpringSecurityPlatformSecurityContext.java | 5 +- .../jobs/filter/LoanCOBApiFilterTest.java | 45 +++++++ ...ngSecurityPlatformSecurityContextTest.java | 114 ++++++++++++++++++ .../AuthenticationIntegrationTest.java | 109 +++++++++++++++++ .../LoanAuditingIntegrationTest.java | 50 +------- .../common/loans/LoanTransactionHelper.java | 30 ++++- 7 files changed, 306 insertions(+), 50 deletions(-) create mode 100644 fineract-provider/src/test/java/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContextTest.java create mode 100644 integration-tests/src/test/java/org/apache/fineract/integrationtests/AuthenticationIntegrationTest.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilter.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilter.java index 9bfffb582da..65073621afe 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilter.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilter.java @@ -32,6 +32,7 @@ import org.apache.fineract.useradministration.exception.UnAuthenticatedUserException; import org.apache.http.HttpStatus; import org.springframework.context.annotation.Conditional; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.web.filter.OncePerRequestFilter; @RequiredArgsConstructor @@ -84,7 +85,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } } } catch (UnAuthenticatedUserException e) { - Reject.reject(null, HttpStatus.SC_UNAUTHORIZED).toServletResponse(response); + throw new AuthenticationCredentialsNotFoundException("Not Authenticated", e); } } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContext.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContext.java index 4a8628d3241..3775c2eacc5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContext.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContext.java @@ -63,7 +63,10 @@ public AppUser authenticatedUser() { if (context != null) { final Authentication auth = context.getAuthentication(); if (auth != null) { - currentUser = (AppUser) auth.getPrincipal(); + Object principal = auth.getPrincipal(); + if (principal instanceof AppUser appUser) { + currentUser = appUser; + } } } diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilterTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilterTest.java index ff78d092dca..37452d3bfbb 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilterTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilterTest.java @@ -20,18 +20,24 @@ import static org.apache.fineract.infrastructure.jobs.filter.LoanCOBFilterHelper.LOAN_GLIMACCOUNT_PATH_PATTERN; import static org.apache.fineract.infrastructure.jobs.filter.LoanCOBFilterHelper.LOAN_PATH_PATTERN; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import com.sun.research.ws.wadl.HTTPMethods; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.PrintWriter; @@ -58,6 +64,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; import org.apache.fineract.useradministration.domain.AppUser; +import org.apache.fineract.useradministration.exception.UnAuthenticatedUserException; import org.apache.http.HttpStatus; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; @@ -71,6 +78,7 @@ import org.mockito.quality.Strictness; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) @@ -445,4 +453,41 @@ void shouldRejectWhenGlimLoanIsHardLocked() throws ServletException, IOException testObj.doFilterInternal(request, response, filterChain); verify(response, times(1)).setStatus(HttpStatus.SC_CONFLICT); } + + @Test + void shouldThrowAuthenticationCredentialsNotFoundException_WhenUnAuthenticatedUserExceptionIsThrown() throws IOException { + LoanCOBFilterHelper spyHelper = spy(helper); + testObj = new LoanCOBApiFilter(spyHelper); + MockHttpServletRequest request = mock(MockHttpServletRequest.class); + MockHttpServletResponse response = mock(MockHttpServletResponse.class); + FilterChain filterChain = mock(FilterChain.class); + + final byte[] cachedBody = new byte[0]; + given(request.getInputStream()) + .willReturn(new BodyCachingHttpServletRequestWrapper.CachedBodyServletInputStream(new ByteArrayInputStream(cachedBody))); + doReturn(true).when(spyHelper).isOnApiList(any(BodyCachingHttpServletRequestWrapper.class)); + doThrow(new UnAuthenticatedUserException()).when(spyHelper).isBypassUser(); + + assertThrows(AuthenticationCredentialsNotFoundException.class, () -> testObj.doFilterInternal(request, response, filterChain)); + verifyNoInteractions(filterChain); + } + + @Test + void shouldProceed_WhenAuthenticatedUser() throws Exception { + LoanCOBFilterHelper spyHelper = spy(helper); + testObj = new LoanCOBApiFilter(spyHelper); + MockHttpServletRequest request = mock(MockHttpServletRequest.class); + MockHttpServletResponse response = mock(MockHttpServletResponse.class); + FilterChain filterChain = mock(FilterChain.class); + + final byte[] cachedBody = new byte[0]; + given(request.getInputStream()) + .willReturn(new BodyCachingHttpServletRequestWrapper.CachedBodyServletInputStream(new ByteArrayInputStream(cachedBody))); + doReturn(true).when(spyHelper).isOnApiList(any(BodyCachingHttpServletRequestWrapper.class)); + doReturn(true).when(spyHelper).isBypassUser(); + + testObj.doFilterInternal(request, response, filterChain); + + verify(filterChain, times(1)).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); + } } diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContextTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContextTest.java new file mode 100644 index 00000000000..d9347c6d99d --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContextTest.java @@ -0,0 +1,114 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.security.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; +import org.apache.fineract.infrastructure.security.exception.ResetPasswordException; +import org.apache.fineract.useradministration.domain.AppUser; +import org.apache.fineract.useradministration.exception.UnAuthenticatedUserException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +@ExtendWith(MockitoExtension.class) +class SpringSecurityPlatformSecurityContextTest { + + @Mock + private SecurityContext securityContext; + + @Mock + private Authentication authentication; + + @Mock + private AppUser appUser; + + @Mock + private ConfigurationDomainService configurationDomainService; + + @InjectMocks + private SpringSecurityPlatformSecurityContext securityContextProvider; + + @BeforeEach + void setUp() { + SecurityContextHolder.setContext(securityContext); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + void shouldReturnAppUserWhenPrincipalIsAppUser() { + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getPrincipal()).thenReturn(appUser); + when(configurationDomainService.isPasswordForcedResetEnable()).thenReturn(false); + + AppUser result = securityContextProvider.authenticatedUser(); + + assertEquals(appUser, result, "authenticatedUser() should return AppUser"); + } + + @Test + void shouldThrowUnAuthenticatedUserExceptionWhenPrincipalIsNotAppUser() { + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getPrincipal()).thenReturn("anonymousUser"); + + assertThrows(UnAuthenticatedUserException.class, + () -> securityContextProvider.authenticatedUser(), + "authenticatedUser() should throw UnAuthenticatedUserException when " + + "Principal is not AppUser"); + } + + @Test + void shouldThrowUnAuthenticatedUserExceptionWhenAuthenticationIsNull() { + when(securityContext.getAuthentication()).thenReturn(null); + + assertThrows(UnAuthenticatedUserException.class, + () -> securityContextProvider.authenticatedUser(), + "authenticatedUser() should throw UnAuthenticatedUserException when " + + "Authentication is null"); + } + + @Test + void shouldThrowResetPasswordExceptionWhenPasswordMustBeReset() { + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getPrincipal()).thenReturn(appUser); + SpringSecurityPlatformSecurityContext spyContextProvider = spy(securityContextProvider); + doReturn(true).when(spyContextProvider).doesPasswordHasToBeRenewed(appUser); + + assertThrows(ResetPasswordException.class, + spyContextProvider::authenticatedUser, + "authenticatedUser() should throw ResetPasswordException when password needs" + + " to be reset"); + } +} diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AuthenticationIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AuthenticationIntegrationTest.java new file mode 100644 index 00000000000..e920c336a1a --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AuthenticationIntegrationTest.java @@ -0,0 +1,109 @@ +package org.apache.fineract.integrationtests; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.restassured.builder.RequestSpecBuilder; +import io.restassured.builder.ResponseSpecBuilder; +import io.restassured.http.ContentType; +import io.restassured.specification.RequestSpecification; +import io.restassured.specification.ResponseSpecification; +import java.util.Collections; +import java.util.HashMap; +import lombok.extern.slf4j.Slf4j; +import org.apache.fineract.integrationtests.common.ClientHelper; +import org.apache.fineract.integrationtests.common.Utils; +import org.apache.fineract.integrationtests.common.accounting.AccountHelper; +import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder; +import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; +import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension; +import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; +import org.apache.fineract.integrationtests.common.organisation.StaffHelper; +import org.apache.fineract.integrationtests.useradministration.users.UserHelper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@Slf4j +@ExtendWith(LoanTestLifecycleExtension.class) +public class AuthenticationIntegrationTest { + + private static final String LOAN_DATE = "11 July 2022"; + private static final String APPROVE_COMMAND = "approve"; + private ResponseSpecification responseSpec; + private RequestSpecification requestSpec; + private LoanTransactionHelper loanTransactionHelper; + private Integer loanID; + + @BeforeEach + public void setup() { + Utils.initializeRESTAssured(); + setupAuthenticatedRequestSpec(); + this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + + AccountHelper accountHelper = new AccountHelper(this.requestSpec, this.responseSpec); + Integer staffId = StaffHelper.createStaff(this.requestSpec, this.responseSpec); + String username = Utils.uniqueRandomStringGenerator("user", 8); + UserHelper.createUser(this.requestSpec, this.responseSpec, 1, staffId, username, "P4ssw0rd", "resourceId"); + Integer clientID = ClientHelper.createClient(requestSpec, responseSpec); + + Integer loanProductID = setupLoanProduct(accountHelper); + this.loanID = loanTransactionHelper.applyForLoanApplicationWithPaymentStrategyAndPastMonth(clientID, loanProductID, + Collections.emptyList(), null, "10000", LoanApplicationTestBuilder.DEFAULT_STRATEGY, "10 July 2022", LOAN_DATE); + } + + @Test + public void shouldAllowAccessForAuthenticatedUser() { + setupAuthenticatedRequestSpec(); + String loanApprovalCommand = createLoanApprovalCommand(); + String loanApprovalRequest = createLoanApprovalRequest(); + + HashMap response = Utils.performServerPost(this.requestSpec, this.responseSpec, loanApprovalCommand, loanApprovalRequest, + "changes"); + HashMap status = (HashMap) response.get("status"); + + assertEquals(200, (Integer) status.get("id")); + } + + @Test + public void shouldReturnUnauthorizedForUnauthenticatedAccess() throws JsonProcessingException { + setupUnauthenticatedRequestSpec(); + this.responseSpec = new ResponseSpecBuilder().expectStatusCode(401).build(); + + String loanApprovalCommand = createLoanApprovalCommand(); + String loanApprovalRequest = createLoanApprovalRequest(); + + String rawResponse = Utils.performServerPost(this.requestSpec, this.responseSpec, loanApprovalCommand, loanApprovalRequest, null); + + ObjectMapper objectMapper = new ObjectMapper(); + HashMap response = objectMapper.readValue(rawResponse, HashMap.class); + + assertEquals(401, (Integer) response.get("status")); + assertEquals("Unauthorized", response.get("error")); + } + + private Integer setupLoanProduct(AccountHelper accountHelper) { + return this.loanTransactionHelper.createLoanProduct("0", "0", LoanProductTestBuilder.DEFAULT_STRATEGY, "2", + accountHelper.createAssetAccount(), accountHelper.createIncomeAccount(), accountHelper.createExpenseAccount(), + accountHelper.createLiabilityAccount()); + } + + private void setupAuthenticatedRequestSpec() { + this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + } + + private void setupUnauthenticatedRequestSpec() { + this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + } + + private String createLoanApprovalRequest() { + return this.loanTransactionHelper.getApproveLoanAsJSON(LOAN_DATE); + } + + private String createLoanApprovalCommand() { + return this.loanTransactionHelper.createLoanOperationURL(APPROVE_COMMAND, loanID); + } +} diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java index 1ff92e392b2..5498d72075d 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java @@ -35,7 +35,6 @@ import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.integrationtests.common.ClientHelper; @@ -93,12 +92,12 @@ public void checkAuditDates() throws InterruptedException { final Account expenseAccount = this.accountHelper.createExpenseAccount(); final Account overpaymentAccount = this.accountHelper.createLiabilityAccount(); - final Integer loanProductID = createLoanProduct("0", "0", LoanProductTestBuilder.DEFAULT_STRATEGY, "2", assetAccount, incomeAccount, - expenseAccount, overpaymentAccount); + final Integer loanProductID = this.loanTransactionHelper.createLoanProduct("0", "0", LoanProductTestBuilder.DEFAULT_STRATEGY, "2", + assetAccount, incomeAccount, expenseAccount, overpaymentAccount); OffsetDateTime now = Utils.getAuditDateTimeToCompare(); - final Integer loanID = applyForLoanApplicationWithPaymentStrategyAndPastMonth(clientID, loanProductID, Collections.emptyList(), - null, "10000", LoanApplicationTestBuilder.DEFAULT_STRATEGY, "10 July 2022", "11 July 2022"); + final Integer loanID = this.loanTransactionHelper.applyForLoanApplicationWithPaymentStrategyAndPastMonth(clientID, loanProductID, + Collections.emptyList(), null, "10000", LoanApplicationTestBuilder.DEFAULT_STRATEGY, "10 July 2022", "11 July 2022"); Assertions.assertNotNull(loanID); HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID); LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap); @@ -141,45 +140,4 @@ public void checkAuditDates() throws InterruptedException { assertEquals(userId, auditFieldsResponse.get(LAST_MODIFIED_BY)); assertTrue(DateUtils.isEqual(now2, lastModifiedDate, ChronoUnit.MINUTES)); } - - private Integer applyForLoanApplicationWithPaymentStrategyAndPastMonth(final Integer clientID, final Integer loanProductID, - List charges, final String savingsId, String principal, final String repaymentStrategy, final String submittedOnDate, - final String disbursementDate) { - LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------"); - - final String loanApplicationJSON = new LoanApplicationTestBuilder() // - .withPrincipal(principal) // - .withLoanTermFrequency("6") // - .withLoanTermFrequencyAsMonths() // - .withNumberOfRepayments("6") // - .withRepaymentEveryAfter("1") // - .withRepaymentFrequencyTypeAsMonths() // - .withInterestRatePerPeriod("2") // - .withAmortizationTypeAsEqualInstallments() // - .withInterestTypeAsFlatBalance() // - .withInterestCalculationPeriodTypeSameAsRepaymentPeriod() // - .withExpectedDisbursementDate(disbursementDate) // - .withSubmittedOnDate(submittedOnDate) // - .withRepaymentStrategy(repaymentStrategy) // - .withCharges(charges).build(clientID.toString(), loanProductID.toString(), savingsId); - return this.loanTransactionHelper.getLoanId(loanApplicationJSON); - } - - private Integer createLoanProduct(final String inMultiplesOf, final String digitsAfterDecimal, final String repaymentStrategy, - final String accountingRule, final Account... accounts) { - LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------"); - final String loanProductJSON = new LoanProductTestBuilder() // - .withPrincipal("10000000.00") // - .withNumberOfRepayments("24") // - .withRepaymentAfterEvery("1") // - .withRepaymentTypeAsMonth() // - .withinterestRatePerPeriod("2") // - .withInterestRateFrequencyTypeAsMonths() // - .withRepaymentStrategy(repaymentStrategy) // - .withAmortizationTypeAsEqualPrincipalPayment() // - .withInterestTypeAsDecliningBalance() // - .currencyDetails(digitsAfterDecimal, inMultiplesOf).withAccounting(accountingRule, accounts).build(null); - return this.loanTransactionHelper.getLoanProductId(loanProductJSON); - } - } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java index 58f04ad234e..128573f6666 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java @@ -91,6 +91,7 @@ import org.apache.fineract.integrationtests.common.CommonConstants; import org.apache.fineract.integrationtests.common.PaymentTypeHelper; import org.apache.fineract.integrationtests.common.Utils; +import org.apache.fineract.integrationtests.common.accounting.Account; import org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; @@ -1133,7 +1134,7 @@ private String getDisburseLoanWithRepaymentRescheduleAsJSON(final String actualD return new Gson().toJson(map); } - private String getApproveLoanAsJSON(final String approvalDate) { + public String getApproveLoanAsJSON(final String approvalDate) { return getApproveLoanAsJSON(approvalDate, null, null, null); } @@ -1397,7 +1398,7 @@ public String getLoanCalculationBodyAsJSON(final String productID) { return new Gson().toJson(map); } - private String createLoanOperationURL(final String command, final Integer loanID) { + public String createLoanOperationURL(final String command, final Integer loanID) { return "/fineract-provider/api/v1/loans/" + loanID + "?command=" + command + "&" + Utils.TENANT_IDENTIFIER; } @@ -2045,4 +2046,29 @@ public PostLoansResponse calculateRepaymentScheduleForApplyLoan(PostLoansRequest return ok(fineract().loans.calculateLoanScheduleOrSubmitLoanApplication(request, command)); } + public Integer createLoanProduct(final String inMultiplesOf, final String digitsAfterDecimal, final String repaymentStrategy, + final String accountingRule, final Account... accounts) { + log.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------"); + final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("10000000.00").withNumberOfRepayments("24") + .withRepaymentAfterEvery("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("2") + .withInterestRateFrequencyTypeAsMonths().withRepaymentStrategy(repaymentStrategy) + .withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsDecliningBalance() + .currencyDetails(digitsAfterDecimal, inMultiplesOf).withAccounting(accountingRule, accounts).build(null); + return getLoanProductId(loanProductJSON); + } + + public Integer applyForLoanApplicationWithPaymentStrategyAndPastMonth(final Integer clientID, final Integer loanProductID, + List charges, final String savingsId, String principal, final String repaymentStrategy, final String submittedOnDate, + final String disbursementDate) { + log.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------"); + + final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(principal).withLoanTermFrequency("6") + .withLoanTermFrequencyAsMonths().withNumberOfRepayments("6").withRepaymentEveryAfter("1") + .withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("2").withAmortizationTypeAsEqualInstallments() + .withInterestTypeAsFlatBalance().withInterestCalculationPeriodTypeSameAsRepaymentPeriod() + .withExpectedDisbursementDate(disbursementDate).withSubmittedOnDate(submittedOnDate) + .withRepaymentStrategy(repaymentStrategy).withCharges(charges) + .build(clientID.toString(), loanProductID.toString(), savingsId); + return getLoanId(loanApplicationJSON); + } }