/*
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.transfertypes;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import nl.strohalm.cyclos.access.AdminMemberPermission;
import nl.strohalm.cyclos.access.AdminSystemPermission;
import nl.strohalm.cyclos.access.BrokerPermission;
import nl.strohalm.cyclos.access.PermissionCheck;
import nl.strohalm.cyclos.entities.Relationship;
import nl.strohalm.cyclos.entities.accounts.AccountOwner;
import nl.strohalm.cyclos.entities.accounts.AccountType;
import nl.strohalm.cyclos.entities.accounts.MemberAccountType;
import nl.strohalm.cyclos.entities.accounts.MemberGroupAccountSettings;
import nl.strohalm.cyclos.entities.accounts.fees.transaction.BrokerCommission;
import nl.strohalm.cyclos.entities.accounts.fees.transaction.SimpleTransactionFee;
import nl.strohalm.cyclos.entities.accounts.fees.transaction.SimpleTransactionFee.ARateRelation;
import nl.strohalm.cyclos.entities.accounts.fees.transaction.TransactionFee;
import nl.strohalm.cyclos.entities.accounts.fees.transaction.TransactionFee.ChargeType;
import nl.strohalm.cyclos.entities.accounts.fees.transaction.TransactionFee.Nature;
import nl.strohalm.cyclos.entities.accounts.fees.transaction.TransactionFee.Subject;
import nl.strohalm.cyclos.entities.accounts.fees.transaction.TransactionFeeQuery;
import nl.strohalm.cyclos.entities.accounts.transactions.Invoice;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferType;
import nl.strohalm.cyclos.entities.groups.Group;
import nl.strohalm.cyclos.entities.groups.GroupQuery;
import nl.strohalm.cyclos.entities.groups.MemberGroup;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.exceptions.PermissionDeniedException;
import nl.strohalm.cyclos.services.BaseServiceSecurity;
import nl.strohalm.cyclos.services.groups.GroupServiceLocal;
import nl.strohalm.cyclos.services.transactions.InvoiceServiceLocal;
import nl.strohalm.cyclos.services.transactions.PaymentServiceLocal;
import nl.strohalm.cyclos.utils.access.LoggedUser;
import nl.strohalm.cyclos.utils.access.PermissionHelper;
import nl.strohalm.cyclos.webservices.model.TransactionFeeVO;
import org.apache.commons.collections.CollectionUtils;
/**
* Security implementation for {@link TransactionFeeService}
*
* @author ameyer
* @author jcomas
*/
public class TransactionFeeServiceSecurity extends BaseServiceSecurity implements TransactionFeeService {
private TransactionFeeServiceLocal transactionFeeService;
private TransferTypeServiceLocal transferTypeService;
private InvoiceServiceLocal invoiceService;
private PaymentServiceLocal paymentService;
private GroupServiceLocal groupService;
@Override
public Collection<ChargeType> getPossibleChargeType(final TransferType originalTransferType, final Nature feeNature) {
permissionService.permission()
.admin(AdminSystemPermission.ACCOUNTS_MANAGE)
.check();
return transactionFeeService.getPossibleChargeType(originalTransferType, feeNature);
}
@Override
public Collection<Subject> getPossibleSubjects(final TransferType originalTransferType, final Nature nature) {
permissionService.permission()
.admin(AdminSystemPermission.ACCOUNTS_MANAGE)
.check();
return transactionFeeService.getPossibleSubjects(originalTransferType, nature);
}
@Override
public List<TransactionFeeVO> getTransactionFeeVOs(final TransactionFeePreviewDTO preview) {
Map<TransactionFee, BigDecimal> fees = preview.getFees();
if (fees == null) {
return null;
}
for (TransactionFee k : fees.keySet()) {
checkView(k);
}
return transactionFeeService.getTransactionFeeVOs(preview);
}
@Override
public TransactionFee load(final Long id, final Relationship... fetch) {
TransactionFee fee = transactionFeeService.load(id, fetch);
checkView(fee);
return fee;
}
@Override
public TransactionFeePreviewDTO preview(final AccountOwner from, final AccountOwner to, final TransferType transferType, final BigDecimal amount) {
if (!paymentService.canMakePayment(from, to, transferType)) {
throw new PermissionDeniedException();
}
return transactionFeeService.preview(from, to, transferType, amount);
}
@Override
public TransactionFeePreviewDTO preview(final Invoice invoice) {
if (!invoiceService.canAccept(invoice)) {
throw new PermissionDeniedException();
}
return transactionFeeService.preview(invoice);
}
@Override
public int remove(final Long... ids) {
permissionService.permission()
.admin(AdminSystemPermission.ACCOUNTS_MANAGE)
.check();
return transactionFeeService.remove(ids);
}
@Override
public BrokerCommission save(final BrokerCommission brokerCommission) {
permissionService.permission()
.admin(AdminSystemPermission.ACCOUNTS_MANAGE)
.check();
checkTransactionFee(brokerCommission);
return transactionFeeService.save(brokerCommission);
}
@Override
public SimpleTransactionFee save(final SimpleTransactionFee transactionFee, final ARateRelation aRateRelation) {
permissionService.permission()
.admin(AdminSystemPermission.ACCOUNTS_MANAGE)
.check();
checkTransactionFee(transactionFee);
return transactionFeeService.save(transactionFee, aRateRelation);
}
@Override
public List<? extends TransactionFee> search(final TransactionFeeQuery query) {
if (query.getMemberGroup() != null) {
PermissionHelper.checkContains(permissionService.getManagedMemberGroups(), query.getMemberGroup());
}
if (query.getBrokerGroup() != null) {
PermissionHelper.checkContains(permissionService.getManagedMemberGroups(), query.getBrokerGroup());
}
checkView(null);
return transactionFeeService.search(query);
}
@Override
public List<TransferType> searchGeneratedTransferTypes(final TransactionFee fee, final boolean allowAnyAccount, final boolean onlyFromSystem) {
permissionService.permission()
.admin(AdminSystemPermission.ACCOUNTS_MANAGE)
.check();
return transactionFeeService.searchGeneratedTransferTypes(fee, allowAnyAccount, onlyFromSystem);
}
public void setGroupServiceLocal(final GroupServiceLocal groupService) {
this.groupService = groupService;
}
public void setInvoiceServiceLocal(final InvoiceServiceLocal invoiceService) {
this.invoiceService = invoiceService;
}
public void setPaymentServiceLocal(final PaymentServiceLocal paymentService) {
this.paymentService = paymentService;
}
public void setTransactionFeeServiceLocal(final TransactionFeeServiceLocal transactionFeeService) {
this.transactionFeeService = transactionFeeService;
}
public void setTransferTypeServiceLocal(final TransferTypeServiceLocal transferTypeService) {
this.transferTypeService = transferTypeService;
}
@Override
public void validate(final BrokerCommission brokerCommission) {
transactionFeeService.validate(brokerCommission);
}
@Override
public void validate(final SimpleTransactionFee transactionFee, final ARateRelation aRateRelation) {
transactionFeeService.validate(transactionFee, aRateRelation);
}
/**
* Checks that the given transactionFee comes with an allowed generated transfer type. It is assumed that the method @see
* {@link #checkReadOnlyValues(TransactionFee)} is called before
* @param transactionFee
*/
private void checkGeneratedTransferType(final TransactionFee transactionFee) {
List<TransferType> allowedGeneratedTransferTypes;
TransferType generatedTransferType = transferTypeService.load(transactionFee.getGeneratedTransferType().getId());
transactionFee.setGeneratedTransferType(generatedTransferType);
if (!transactionFee.isTransient() && transactionFee.isFromSystem()) {
allowedGeneratedTransferTypes = transactionFeeService.searchGeneratedTransferTypes(transactionFee, true, true);
} else {
allowedGeneratedTransferTypes = transactionFeeService.searchGeneratedTransferTypes(transactionFee, true, false);
}
PermissionHelper.checkContains(allowedGeneratedTransferTypes, transactionFee.getGeneratedTransferType());
}
private void checkGroups(final TransactionFee transactionFee) {
TransferType transferType = fetchService.fetch(transactionFee.getOriginalTransferType(), TransferType.Relationships.FROM, TransferType.Relationships.TO);
// If from member, search for groups related to the from account type...
if (transferType.isFromMember() && !transactionFee.isFromAllGroups() && CollectionUtils.isNotEmpty(transactionFee.getFromGroups())) {
checkGroups(transferType, transactionFee.getFromGroups(), false, true);
}
// ... same for to
if (transferType.isToMember() && !transactionFee.isToAllGroups() && CollectionUtils.isNotEmpty(transactionFee.getToGroups())) {
checkGroups(transferType, transactionFee.getToGroups(), false, false);
}
// For broker groups, list only active
if (transactionFee.getNature() == Nature.BROKER) {
BrokerCommission brokerCommission = (BrokerCommission) transactionFee;
if (!brokerCommission.isAllBrokerGroups() && CollectionUtils.isNotEmpty(brokerCommission.getBrokerGroups())) {
checkGroups(transferType, brokerCommission.getBrokerGroups(), true, null);
}
}
}
@SuppressWarnings("unchecked")
private void checkGroups(final TransferType transferType, final Collection<? extends MemberGroup> groups, final boolean activeBrokersOnly, final Boolean from) {
final GroupQuery groupQuery = new GroupQuery();
if (activeBrokersOnly) {
groupQuery.setNature(Group.Nature.BROKER);
groupQuery.setOnlyActive(true);
} else {
groupQuery.setNatures(Group.Nature.MEMBER, Group.Nature.BROKER);
}
groupQuery.setStatus(Group.Status.NORMAL);
if (from != null) {
groupQuery.setMemberAccountType((MemberAccountType) (from ? transferType.getFrom() : transferType.getTo()));
}
List<MemberGroup> allowedGroups = (List<MemberGroup>) groupService.search(groupQuery);
PermissionHelper.checkSelection(allowedGroups, (Collection<MemberGroup>) groups);
}
/**
* Ensures that certain values didn't changed when an already existing transaction fee is modified.
* @param modifiedTF The transaction fee being modified.
*/
private void checkReadOnlyValues(final TransactionFee modifiedTF) {
if (modifiedTF.isPersistent()) {
TransactionFee savedTF = load(modifiedTF.getId());
PermissionHelper.checkEquals(savedTF.getOriginalTransferType(), modifiedTF.getOriginalTransferType());
PermissionHelper.checkEquals(savedTF.getPayer(), modifiedTF.getPayer());
if (savedTF.getNature() == Nature.SIMPLE) {
SimpleTransactionFee modifiedSTF = (SimpleTransactionFee) modifiedTF;
SimpleTransactionFee savedSTF = (SimpleTransactionFee) savedTF;
PermissionHelper.checkEquals(savedSTF.getReceiver(), modifiedSTF.getReceiver());
PermissionHelper.checkEquals(savedSTF.getFromFixedMember(), modifiedSTF.getFromFixedMember());
PermissionHelper.checkEquals(savedSTF.getToFixedMember(), modifiedSTF.getToFixedMember());
} else if (savedTF.getNature() == Nature.BROKER) {
BrokerCommission modifiedBC = (BrokerCommission) modifiedTF;
BrokerCommission savedBC = (BrokerCommission) savedTF;
if (!savedTF.getGeneratedTransferType().isFromSystem()) {
PermissionHelper.checkEquals(savedTF.getGeneratedTransferType(), modifiedTF.getGeneratedTransferType());
}
PermissionHelper.checkEquals(savedBC.getWhichBroker(), modifiedBC.getWhichBroker());
}
}
}
/**
* Check that the subjects are the allowed.
*/
private void checkSubjects(final TransactionFee transactionFee) {
Collection<Subject> possibleSubjects = transactionFeeService.getPossibleSubjects(transactionFee.getOriginalTransferType(), transactionFee.getNature());
// check that the payer is a possible subject
PermissionHelper.checkContains(possibleSubjects, transactionFee.getPayer());
if (transactionFee.getNature() == Nature.SIMPLE) {
// check that the receiver is a possible subject
Subject receiver = ((SimpleTransactionFee) transactionFee).getReceiver();
PermissionHelper.checkContains(possibleSubjects, receiver);
}
}
private void checkTransactionFee(final TransactionFee transactionFee) {
checkReadOnlyValues(transactionFee);
// Check if manages the from fixed member
if (transactionFee.getFromFixedMember() != null) {
permissionService.checkManages(transactionFee.getFromFixedMember());
}
// Check if manages the to fixed member
if (transactionFee.getNature() == Nature.SIMPLE) {
Member toFixedMember = ((SimpleTransactionFee) transactionFee).getToFixedMember();
if (toFixedMember != null) {
permissionService.checkManages(toFixedMember);
}
}
checkGroups(transactionFee);
checkGeneratedTransferType(transactionFee);
checkSubjects(transactionFee);
// Check the charge type
PermissionHelper.checkContains(transactionFeeService.getPossibleChargeType(transactionFee.getOriginalTransferType(), transactionFee.getNature()), transactionFee.getChargeType());
}
private void checkView(final TransactionFee fee) {
PermissionCheck check = permissionService.permission()
.admin(
AdminMemberPermission.BROKERINGS_MANAGE_COMMISSIONS, AdminSystemPermission.ACCOUNTS_VIEW)
.broker(BrokerPermission.MEMBERS_MANAGE_DEFAULTS, BrokerPermission.MEMBERS_MANAGE_CONTRACTS);
boolean hasPermission = check.hasPermission();
if (!hasPermission) {
if (fee != null) {
// we allow access if the fee's from account type is associated to the member's group
AccountType fromAccType = fee.getOriginalTransferType().getFrom();
AccountOwner accountOwner = LoggedUser.accountOwner();
if (accountOwner instanceof Member) { // member, broker or operator
MemberGroup mGroup = ((Member) accountOwner).getMemberGroup();
Collection<MemberGroupAccountSettings> mgaccSettings = mGroup.getAccountSettings();
if (mgaccSettings != null) {
for (MemberGroupAccountSettings mgaccSetting : mGroup.getAccountSettings()) {
if (mgaccSetting.getAccountType().equals(fromAccType)) {
return; // ok
}
}
}
}
}
throw new PermissionDeniedException();
}
}
}