/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Cyclos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package nl.strohalm.cyclos.services.transactions;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import nl.strohalm.cyclos.access.AdminMemberPermission;
import nl.strohalm.cyclos.access.BrokerPermission;
import nl.strohalm.cyclos.access.MemberPermission;
import nl.strohalm.cyclos.access.OperatorPermission;
import nl.strohalm.cyclos.entities.Relationship;
import nl.strohalm.cyclos.entities.accounts.Account;
import nl.strohalm.cyclos.entities.accounts.AccountOwner;
import nl.strohalm.cyclos.entities.accounts.AccountType;
import nl.strohalm.cyclos.entities.accounts.MemberAccount;
import nl.strohalm.cyclos.entities.accounts.transactions.Invoice;
import nl.strohalm.cyclos.entities.accounts.transactions.Payment;
import nl.strohalm.cyclos.entities.accounts.transactions.Transfer;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferQuery;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferType;
import nl.strohalm.cyclos.entities.exceptions.EntityNotFoundException;
import nl.strohalm.cyclos.entities.exceptions.UnexpectedEntityException;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.exceptions.PermissionDeniedException;
import nl.strohalm.cyclos.services.BaseServiceSecurity;
import nl.strohalm.cyclos.services.accounts.AccountDTO;
import nl.strohalm.cyclos.services.accounts.AccountServiceLocal;
import nl.strohalm.cyclos.services.accounts.rates.ConversionSimulationDTO;
import nl.strohalm.cyclos.services.stats.StatisticalResultDTO;
import nl.strohalm.cyclos.services.transactions.exceptions.AuthorizedPaymentInPastException;
import nl.strohalm.cyclos.services.transactions.exceptions.MaxAmountPerDayExceededException;
import nl.strohalm.cyclos.services.transactions.exceptions.NotEnoughCreditsException;
import nl.strohalm.cyclos.services.transactions.exceptions.UpperCreditLimitReachedException;
import nl.strohalm.cyclos.services.transfertypes.TransactionFeePreviewForRatesDTO;
import nl.strohalm.cyclos.webservices.accounts.AccountHistoryResultPage;
import nl.strohalm.cyclos.webservices.model.AccountHistoryTransferVO;
import nl.strohalm.cyclos.webservices.payments.AccountHistoryParams;
import nl.strohalm.cyclos.webservices.utils.PaymentHelper;
/**
* Security for {@link PaymentService}
*
* @author luis
*/
public class PaymentServiceSecurity extends BaseServiceSecurity implements PaymentService {
private PaymentServiceLocal paymentService;
private AccountServiceLocal accountService;
private InvoiceServiceLocal invoiceService;
private PaymentHelper paymentHelper;
@Override
public List<ScheduledPaymentDTO> calculatePaymentProjection(final ProjectionDTO params) {
// No security is needed here
return paymentService.calculatePaymentProjection(params);
}
@Override
public boolean canChargeback(final Transfer transfer, final boolean ignorePendingPayment) {
// No security is needed here
return paymentService.canChargeback(transfer, ignorePendingPayment);
}
@Override
public Transfer chargeback(final Transfer transfer) throws UnexpectedEntityException {
checkChargeback(transfer);
return paymentService.chargeback(transfer);
}
@Override
public Payment doPayment(final DoPaymentDTO params) throws NotEnoughCreditsException, MaxAmountPerDayExceededException, UnexpectedEntityException, UpperCreditLimitReachedException, AuthorizedPaymentInPastException {
checkPayment(params);
return paymentService.doPayment(params);
}
@Override
public AccountHistoryResultPage getAccountHistoryResultPage(final AccountHistoryParams params) {
TransferQuery query = paymentHelper.toTransferQuery(params);
checkQuery(query);
return paymentService.getAccountHistoryResultPage(params);
}
@Override
public AccountHistoryTransferVO getAccountHistoryTransferVO(final Long id) {
check(load(id));
return paymentService.getAccountHistoryTransferVO(id);
}
@Override
public ConversionSimulationDTO getDefaultConversionDTO(final MemberAccount account, final List<TransferType> transferTypes) {
Member member = fetchService.fetch(account, MemberAccount.Relationships.MEMBER).getMember();
checkConversionSimulation(member);
return paymentService.getDefaultConversionDTO(account, transferTypes);
}
@Override
public BigDecimal getMinimumPayment() {
// No permission check is needed
return paymentService.getMinimumPayment();
}
@Override
public StatisticalResultDTO getSimulateConversionGraph(final ConversionSimulationDTO dto) {
Member member = fetchService.fetch(dto.getAccount(), MemberAccount.Relationships.MEMBER).getMember();
checkConversionSimulation(member);
return paymentService.getSimulateConversionGraph(dto);
}
@Override
public boolean hasPermissionsToChargeback(final Transfer transfer) {
return paymentService.hasPermissionsToChargeback(transfer);
}
@Override
public boolean isVisible(final Payment payment) {
return paymentService.isVisible(payment);
}
@Override
public Transfer load(final Long id, final Relationship... fetch) throws EntityNotFoundException {
Set<Relationship> relationships = new HashSet<Relationship>(Arrays.asList(fetch));
relationships.add(Payment.Relationships.FROM);
relationships.add(Payment.Relationships.TO);
Transfer transfer = paymentService.load(id, relationships.toArray(new Relationship[relationships.size()]));
check(transfer);
return transfer;
}
@Override
public List<Transfer> search(final TransferQuery query) {
checkQuery(query);
return paymentService.search(query);
}
public void setAccountServiceLocal(final AccountServiceLocal accountService) {
this.accountService = accountService;
}
public void setInvoiceServiceLocal(final InvoiceServiceLocal invoiceService) {
this.invoiceService = invoiceService;
}
public void setPaymentHelper(final PaymentHelper paymentHelper) {
this.paymentHelper = paymentHelper;
}
public void setPaymentServiceLocal(final PaymentServiceLocal paymentService) {
this.paymentService = paymentService;
}
@Override
public TransactionFeePreviewForRatesDTO simulateConversion(final ConversionSimulationDTO dto) {
Member member = fetchService.fetch(dto.getAccount(), MemberAccount.Relationships.MEMBER).getMember();
checkConversionSimulation(member);
return paymentService.simulateConversion(dto);
}
@Override
public Payment simulatePayment(final DoPaymentDTO params) throws NotEnoughCreditsException, MaxAmountPerDayExceededException, UnexpectedEntityException, UpperCreditLimitReachedException, AuthorizedPaymentInPastException {
checkPayment(params.getFrom(), params.getTo());
return paymentService.simulatePayment(params);
}
@Override
public void validate(final ConversionSimulationDTO dto) {
// No permission needed for validating
paymentService.validate(dto);
}
@Override
public void validate(final DoPaymentDTO payment) {
// No permission needed for validating
paymentService.validate(payment);
}
@Override
public boolean wouldRequireAuthorization(final DoPaymentDTO dto) {
checkPayment(dto.getFrom(), dto.getTo());
return paymentService.wouldRequireAuthorization(dto);
}
@Override
public boolean wouldRequireAuthorization(final Invoice invoice) {
if (!invoiceService.canAccept(invoice)) {
throw new PermissionDeniedException();
}
return paymentService.wouldRequireAuthorization(invoice);
}
@Override
public boolean wouldRequireAuthorization(Transfer transfer) {
transfer = fetchService.fetch(transfer, Payment.Relationships.FROM, Payment.Relationships.TO);
checkPayment(transfer.getFromOwner(), transfer.getToOwner());
return paymentService.wouldRequireAuthorization(transfer);
}
@Override
public boolean wouldRequireAuthorization(final TransferType transferType, final BigDecimal amount, final AccountOwner from) {
// Nothing to check
return paymentService.wouldRequireAuthorization(transferType, amount, from);
}
private void check(final Payment payment) {
if (payment != null && !isVisible(payment)) {
throw new PermissionDeniedException();
}
}
private void checkAccountAccess(final AccountOwner owner, final AccountType type) {
if (owner == null) {
return;
}
boolean ok;
if (type == null) {
ok = accountService.canViewAccountsOf(owner);
} else {
Account account = accountService.getAccount(new AccountDTO(owner, type));
ok = accountService.canView(account);
}
if (!ok) {
throw new PermissionDeniedException();
}
}
private void checkChargeback(final Transfer transfer) {
if (!hasPermissionsToChargeback(transfer)) {
throw new PermissionDeniedException();
}
}
private void checkConversionSimulation(final Member member) {
}
private void checkPayment(final AccountOwner from, final AccountOwner to) {
if (!paymentService.canMakePayment(from, to, null)) {
throw new PermissionDeniedException();
}
}
private void checkPayment(final DoPaymentDTO params) {
if (params.getDate() != null) {
// Payments in past require a separate permission check
permissionService.permission().admin(AdminMemberPermission.PAYMENTS_PAYMENT_WITH_DATE).check();
}
// we dont's set the transfer type as a parameter because it will be checked later by the payment service
// when the payment is done.
checkPayment(params.getFrom(), params.getTo());
}
private void checkQuery(final TransferQuery query) {
if (query.getOwner() == null) {
throw new PermissionDeniedException();
}
// Check all possible use cases
checkAccountAccess(query.getOwner(), query.getType());
checkAccountAccess(query.getFromAccountOwner(), query.getFromAccountType());
checkAccountAccess(query.getToAccountOwner(), query.getToAccountType());
check(query.getParent());
}
}