Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public CommandProcessingResult activateFDAccount(final Long savingsId, final Jso
final boolean postReversals = false;
this.depositAccountTransactionDataValidator.validateActivation(command);
final MathContext mc = MathContext.DECIMAL64;
final FixedDepositAccount account = (FixedDepositAccount) this.depositAccountAssembler.assembleFrom(savingsId,
final FixedDepositAccount account = (FixedDepositAccount) this.depositAccountAssembler.assembleFromInUserScope(savingsId,
DepositAccountType.FIXED_DEPOSIT);
checkClientOrGroupActive(account);

Expand Down Expand Up @@ -250,7 +250,7 @@ public CommandProcessingResult activateRDAccount(final Long savingsId, final Jso
final boolean postReversals = false;
this.depositAccountTransactionDataValidator.validateActivation(command);

final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(savingsId,
final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFromInUserScope(savingsId,
DepositAccountType.RECURRING_DEPOSIT);
checkClientOrGroupActive(account);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public CommandProcessingResult activate(final Long savingsId, final JsonCommand

this.savingsAccountTransactionDataValidator.validateActivation(command);

final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, false);
final SavingsAccount account = this.savingAccountAssembler.assembleFromInUserScope(savingsId, false);

checkClientOrGroupActive(account);
final Set<Long> existingTransactionIds = new HashSet<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.holiday.domain.HolidayRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
Expand Down Expand Up @@ -389,6 +390,19 @@ void undoTransaction_shouldSkipUserScopeForAccountTransferReversal() {
verify(savingAccountAssembler, never()).assembleFromInUserScope(anyLong(), anyBoolean());
}

@Test
void activate_shouldRejectUserOutsideAccountOfficeHierarchy() {
Long savingsId = 1L;
JsonCommand command = mock(JsonCommand.class);

when(savingAccountAssembler.assembleFromInUserScope(savingsId, false))
.thenThrow(new NoAuthorizationException("The user doesn't have enough permissions to access the resource."));

assertThatThrownBy(() -> service.activate(savingsId, command)).isInstanceOf(NoAuthorizationException.class);
verify(savingAccountAssembler, never()).assembleFrom(anyLong(), anyBoolean());
verify(savingAccountRepositoryWrapper, never()).saveAndFlush(any());
}

@Test
void postInterest_shouldValidateRequestAndUpdateManualInterestPostingExternalId() {
Long savingsId = 1L;
Expand Down
Original file line number Diff line number Diff line change
@@ -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.integrationtests;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.feign.util.FeignCalls;
import org.apache.fineract.client.models.PostClientsRequest;
import org.apache.fineract.client.models.PostFixedDepositAccountsAccountIdResponse;
import org.apache.fineract.client.models.PostUsersRequest;
import org.apache.fineract.integrationtests.client.feign.FeignDepositTestBase;
import org.apache.fineract.integrationtests.client.feign.helpers.FeignOfficeHelper;
import org.apache.fineract.integrationtests.client.feign.helpers.FeignRoleHelper;
import org.apache.fineract.integrationtests.client.feign.helpers.FeignUserHelper;
import org.apache.fineract.integrationtests.client.feign.modules.DepositRequestBuilders;
import org.apache.fineract.integrationtests.client.feign.modules.DepositTestData;
import org.apache.fineract.integrationtests.client.feign.modules.DepositTestValidators;
import org.apache.fineract.integrationtests.client.feign.modules.SavingsTestData;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.FineractFeignClientHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.junit.jupiter.api.Test;

/**
* FINERACT-2982: activating a fixed deposit account, which posts the initial deposit, is only allowed for users whose
* office is the account's office or one of its parents.
*/
public class FixedDepositOfficeHierarchyAccessIntegrationTest extends FeignDepositTestBase {

private static final String PASSWORD = "A1b2c3d4e5f$";
private static final LocalDate OFFICE_OPENING_DATE = LocalDate.of(2010, 1, 1);
private static final String HIERARCHY_ERROR_MESSAGE = "The user doesn't have enough permissions to access the resource.";
private static final Map<String, Boolean> ACTIVATION_PERMISSIONS = Map.of("ACTIVATE_FIXEDDEPOSITACCOUNT", true,
"READ_FIXEDDEPOSITACCOUNT", true);

private final FeignOfficeHelper officeHelper = new FeignOfficeHelper(FineractFeignClientHelper.getFineractFeignClient());

@Test
public void testFixedDepositActivationIsRestrictedToUserOfficeHierarchy() {
LocalDate today = Utils.getLocalDateOfTenant();
String validFrom = Utils.dateFormatter.format(today.minusMonths(3));
String validTo = Utils.dateFormatter.format(today.plusYears(10));
String startDate = Utils.dateFormatter.format(today.minusMonths(1));
Long accountOfficeId = officeHelper.createOffice(OFFICE_OPENING_DATE).getOfficeId();
Long siblingOfficeId = officeHelper.createOffice(OFFICE_OPENING_DATE).getOfficeId();

Long clientId = createClientInOffice(accountOfficeId);
Long productId = fixedDepositProductHelper.createProduct(DepositRequestBuilders.withChart(
DepositRequestBuilders.fixedDepositProduct().accountingRule(SavingsTestData.AccountingRule.NONE), validFrom, validTo,
DepositTestData.periodRangeChartSlabs())).getResourceId();
Long accountId = fixedDepositHelper.submitApplication(DepositRequestBuilders.fixedDepositAccount(clientId, productId, startDate,
DepositTestData.PreClosurePenalInterestOnType.WHOLE_TERM)).getSavingsId();
fixedDepositHelper.approve(accountId, startDate);

FineractFeignClient siblingOfficeUser = createUserInOffice(siblingOfficeId);
FineractFeignClient accountOfficeUser = createUserInOffice(accountOfficeId);

CallFailedRuntimeException failure = FeignCalls.failVoid(() -> activate(siblingOfficeUser, accountId, startDate));
assertEquals(403, failure.getStatus());
assertTrue(failure.getResponseBody().contains(HIERARCHY_ERROR_MESSAGE),
"Expected office hierarchy error but was: " + failure.getResponseBody());
DepositTestValidators.verifyFixedDepositIsApproved(fixedDepositHelper.getAccount(accountId).getStatus());

assertNotNull(ok(() -> activate(accountOfficeUser, accountId, startDate)).getResourceId(),
"User from the account's office must be able to activate the account");
DepositTestValidators.verifyFixedDepositIsActive(fixedDepositHelper.getAccount(accountId).getStatus());
assertEquals(0, DepositTestData.DEPOSIT_AMOUNT.compareTo(fixedDepositHelper.getSummary(accountId).getTotalDeposits()),
"Activation must post the deposit amount as the initial deposit");
}

private Long createClientInOffice(Long officeId) {
return clientHelper.createClient(new PostClientsRequest().officeId(officeId).legalFormId(1L)
.firstname(Utils.randomFirstNameGenerator()).lastname(Utils.randomLastNameGenerator()).active(true)
.activationDate(ClientHelper.DEFAULT_DATE).dateFormat(SavingsTestData.DATETIME_PATTERN).locale(SavingsTestData.LOCALE))
.getClientId();
}

private FineractFeignClient createUserInOffice(Long officeId) {
String username = Utils.uniqueRandomStringGenerator("usr", 8);
Long roleId = FeignRoleHelper.createRole();
FeignRoleHelper.addPermissionsToRole(roleId, ACTIVATION_PERMISSIONS);
FeignUserHelper.createUser(new PostUsersRequest().username(username).firstname("Test").lastname("User").email("test@localhost")
.officeId(officeId).roles(List.of(roleId)).password(PASSWORD).repeatPassword(PASSWORD).sendPasswordToEmail(false));
return FineractFeignClientHelper.createNewFineractFeignClient(username, PASSWORD);
}

private static PostFixedDepositAccountsAccountIdResponse activate(FineractFeignClient client, Long accountId, String date) {
return client.fixedDepositAccount().handleCommandsFixedDepositAccount(accountId, DepositRequestBuilders.activateFixedDeposit(date),
"activate");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
import org.apache.fineract.client.feign.util.FeignCalls;
import org.apache.fineract.client.models.PostClientsRequest;
import org.apache.fineract.client.models.PostRecurringDepositAccountsAccountIdResponse;
import org.apache.fineract.client.models.PostRecurringDepositAccountsRecurringDepositAccountIdTransactionsResponse;
import org.apache.fineract.client.models.PostUsersRequest;
import org.apache.fineract.integrationtests.client.feign.FeignDepositTestBase;
Expand All @@ -38,6 +39,7 @@
import org.apache.fineract.integrationtests.client.feign.helpers.FeignUserHelper;
import org.apache.fineract.integrationtests.client.feign.modules.DepositRequestBuilders;
import org.apache.fineract.integrationtests.client.feign.modules.DepositTestData;
import org.apache.fineract.integrationtests.client.feign.modules.DepositTestValidators;
import org.apache.fineract.integrationtests.client.feign.modules.SavingsTestData;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.FineractFeignClientHelper;
Expand All @@ -46,13 +48,17 @@

/**
* FINERACT-2639: monetary recurring deposit transactions are only allowed for users whose office is the account's
* office or one of its parents.
* office or one of its parents. FINERACT-2982: the same applies to account activation.
*/
public class RecurringDepositOfficeHierarchyAccessIntegrationTest extends FeignDepositTestBase {

private static final String PASSWORD = "A1b2c3d4e5f$";
private static final LocalDate OFFICE_OPENING_DATE = LocalDate.of(2010, 1, 1);
private static final String HIERARCHY_ERROR_MESSAGE = "The user doesn't have enough permissions to access the resource.";
private static final Map<String, Boolean> DEPOSIT_PERMISSIONS = Map.of("DEPOSIT_RECURRINGDEPOSITACCOUNT", true,
"READ_RECURRINGDEPOSITACCOUNT", true);
private static final Map<String, Boolean> ACTIVATION_PERMISSIONS = Map.of("ACTIVATE_RECURRINGDEPOSITACCOUNT", true,
"READ_RECURRINGDEPOSITACCOUNT", true);

private final FeignOfficeHelper officeHelper = new FeignOfficeHelper(FineractFeignClientHelper.getFineractFeignClient());

Expand All @@ -75,8 +81,8 @@ public void testRecurringDepositIsRestrictedToUserOfficeHierarchy() {
recurringDepositHelper.activate(accountId, startDate);
BigDecimal depositAmount = recurringDepositHelper.getAccount(accountId).getMandatoryRecommendedDepositAmount();

FineractFeignClient siblingOfficeUser = createUserInOffice(siblingOfficeId);
FineractFeignClient accountOfficeUser = createUserInOffice(accountOfficeId);
FineractFeignClient siblingOfficeUser = createUserInOffice(siblingOfficeId, DEPOSIT_PERMISSIONS);
FineractFeignClient accountOfficeUser = createUserInOffice(accountOfficeId, DEPOSIT_PERMISSIONS);

CallFailedRuntimeException failure = FeignCalls.failVoid(() -> deposit(siblingOfficeUser, accountId, startDate, depositAmount));
assertEquals(403, failure.getStatus());
Expand All @@ -87,22 +93,58 @@ public void testRecurringDepositIsRestrictedToUserOfficeHierarchy() {
"User from the account's office must be able to make a deposit");
}

@Test
public void testRecurringDepositActivationIsRestrictedToUserOfficeHierarchy() {
LocalDate today = Utils.getLocalDateOfTenant();
String validFrom = Utils.dateFormatter.format(today.minusMonths(3));
String validTo = Utils.dateFormatter.format(today.plusYears(10));
String startDate = Utils.dateFormatter.format(today.minusMonths(1));
Long accountOfficeId = officeHelper.createOffice(OFFICE_OPENING_DATE).getOfficeId();
Long siblingOfficeId = officeHelper.createOffice(OFFICE_OPENING_DATE).getOfficeId();

Long clientId = createClientInOffice(accountOfficeId);
Long productId = recurringDepositProductHelper.createProduct(DepositRequestBuilders.withChart(
DepositRequestBuilders.recurringDepositProduct().accountingRule(SavingsTestData.AccountingRule.NONE), validFrom, validTo,
DepositTestData.recurringChartSlabsFor("period"))).getResourceId();
Long accountId = recurringDepositHelper.submitApplication(DepositRequestBuilders.recurringDepositAccount(clientId, productId,
startDate, startDate, DepositTestData.PreClosurePenalInterestOnType.WHOLE_TERM)).getSavingsId();
recurringDepositHelper.approve(accountId, startDate);

FineractFeignClient siblingOfficeUser = createUserInOffice(siblingOfficeId, ACTIVATION_PERMISSIONS);
FineractFeignClient accountOfficeUser = createUserInOffice(accountOfficeId, ACTIVATION_PERMISSIONS);

CallFailedRuntimeException failure = FeignCalls.failVoid(() -> activate(siblingOfficeUser, accountId, startDate));
assertEquals(403, failure.getStatus());
assertTrue(failure.getResponseBody().contains(HIERARCHY_ERROR_MESSAGE),
"Expected office hierarchy error but was: " + failure.getResponseBody());
DepositTestValidators.verifyRecurringDepositIsApproved(recurringDepositHelper.getAccount(accountId).getStatus());

assertNotNull(ok(() -> activate(accountOfficeUser, accountId, startDate)).getResourceId(),
"User from the account's office must be able to activate the account");
DepositTestValidators.verifyRecurringDepositIsActive(recurringDepositHelper.getAccount(accountId).getStatus());
}

private Long createClientInOffice(Long officeId) {
return clientHelper.createClient(new PostClientsRequest().officeId(officeId).legalFormId(1L)
.firstname(Utils.randomFirstNameGenerator()).lastname(Utils.randomLastNameGenerator()).active(true)
.activationDate(ClientHelper.DEFAULT_DATE).dateFormat(SavingsTestData.DATETIME_PATTERN).locale(SavingsTestData.LOCALE))
.getClientId();
}

private FineractFeignClient createUserInOffice(Long officeId) {
private FineractFeignClient createUserInOffice(Long officeId, Map<String, Boolean> permissions) {
String username = Utils.uniqueRandomStringGenerator("usr", 8);
Long roleId = FeignRoleHelper.createRole();
FeignRoleHelper.addPermissionsToRole(roleId, Map.of("DEPOSIT_RECURRINGDEPOSITACCOUNT", true, "READ_RECURRINGDEPOSITACCOUNT", true));
FeignRoleHelper.addPermissionsToRole(roleId, permissions);
FeignUserHelper.createUser(new PostUsersRequest().username(username).firstname("Test").lastname("User").email("test@localhost")
.officeId(officeId).roles(List.of(roleId)).password(PASSWORD).repeatPassword(PASSWORD).sendPasswordToEmail(false));
return FineractFeignClientHelper.createNewFineractFeignClient(username, PASSWORD);
}

private static PostRecurringDepositAccountsAccountIdResponse activate(FineractFeignClient client, Long accountId, String date) {
return client.recurringDepositAccount().handleCommandsRecurringDepositAccount(accountId,
DepositRequestBuilders.activateRecurringDeposit(date), "activate");
}

private static PostRecurringDepositAccountsRecurringDepositAccountIdTransactionsResponse deposit(FineractFeignClient client,
Long accountId, String date, BigDecimal amount) {
return client.recurringDepositAccountTransactions().transactionRecurringDepositAccountTransaction(accountId,
Expand Down
Loading
Loading