All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.adorsys.multibanking.hbci.HbciScaHandler Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2018-2019 adorsys GmbH & Co KG
 *
 * Licensed 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 de.adorsys.multibanking.hbci;

import de.adorsys.multibanking.domain.Consent;
import de.adorsys.multibanking.domain.ConsentStatus;
import de.adorsys.multibanking.domain.ScaStatus;
import de.adorsys.multibanking.domain.TanTransportType;
import de.adorsys.multibanking.domain.exception.MultibankingError;
import de.adorsys.multibanking.domain.exception.MultibankingException;
import de.adorsys.multibanking.domain.request.SelectPsuAuthenticationMethodRequest;
import de.adorsys.multibanking.domain.request.TransactionAuthorisationRequest;
import de.adorsys.multibanking.domain.request.TransactionRequest;
import de.adorsys.multibanking.domain.request.UpdatePsuAuthenticationRequest;
import de.adorsys.multibanking.domain.response.*;
import de.adorsys.multibanking.domain.spi.StrongCustomerAuthorisable;
import de.adorsys.multibanking.domain.transaction.PaymentStatusReqest;
import de.adorsys.multibanking.hbci.job.InstantPaymentStatusJob;
import de.adorsys.multibanking.hbci.model.*;
import de.adorsys.multibanking.hbci.util.HbciErrorUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SerializationUtils;
import org.iban4j.Iban;
import org.kapott.hbci.GV.GVTANMediaList;
import org.kapott.hbci.GV_Result.GVRTANMediaList;
import org.kapott.hbci.dialog.AbstractHbciDialog;
import org.kapott.hbci.dialog.HBCIJobsDialog;
import org.kapott.hbci.dialog.HBCIUpdDialog;
import org.kapott.hbci.exceptions.HBCI_Exception;
import org.kapott.hbci.manager.HBCIProduct;
import org.kapott.hbci.manager.HBCIUtils;
import org.kapott.hbci.passport.PinTanPassport;
import org.kapott.hbci.status.HBCIExecStatus;

import java.util.*;
import java.util.stream.Collectors;

import static de.adorsys.multibanking.domain.BankApi.HBCI;
import static de.adorsys.multibanking.domain.ScaApproach.EMBEDDED;
import static de.adorsys.multibanking.domain.ScaStatus.*;
import static de.adorsys.multibanking.domain.exception.MultibankingError.BANK_NOT_SUPPORTED;
import static de.adorsys.multibanking.domain.exception.MultibankingError.INVALID_SCA_METHOD;
import static de.adorsys.multibanking.hbci.model.HbciDialogType.BPD;
import static de.adorsys.multibanking.hbci.model.HbciDialogType.UPD;

@Slf4j
@RequiredArgsConstructor
public class HbciScaHandler implements StrongCustomerAuthorisable {

    private final HBCIProduct hbciProduct;
    private final long sysIdMaxAgeMs;
    private final long updMaxAgeMs;
    private final HbciBpdCacheHolder hbciBpdCacheHolder;

    private final HbciScaMapper hbciScaMapper = new HbciScaMapperImpl();
    private final HbciDialogRequestMapper hbciDialogRequestMapper = new HbciDialogRequestMapperImpl();

    @Override
    public CreateConsentResponse createConsent(Consent consent, boolean redirectPreferred,
                                               String tppRedirectUri, Object bankApiConsentData) {
        String bankCode = Iban.valueOf(consent.getPsuAccountIban()).getBankCode();

        boolean bankSupported = Optional.ofNullable(HBCIUtils.getBankInfo(bankCode))
            .map(bankInfo -> bankInfo.getPinTanVersion() != null)
            .orElse(false);

        if (!bankSupported) {
            log.error("Bank not supported for bank code " + bankCode);
            throw new MultibankingException(BANK_NOT_SUPPORTED, 400, "Bank code " + bankCode + " not supported");
        }

        HbciConsent hbciConsent = new HbciConsent();
        hbciConsent.setStatus(STARTED);
        hbciConsent.setHbciProduct(hbciProduct);

        return hbciScaMapper.toCreateConsentResponse(hbciConsent);
    }

    @Override
    public Consent getConsent(String consentId, Object bankApiConsentData) {
        return null;
    }

    @Override
    public ConsentStatus getConsentStatus(String consentId, Object bankApiConsentData) {
        return null;
    }

    @Override
    public UpdateAuthResponse updatePsuAuthentication(UpdatePsuAuthenticationRequest updatePsuAuthentication) {
        //credentials needed in consent for further requests
        HbciConsent hbciConsent = (HbciConsent) updatePsuAuthentication.getBankApiConsentData();
        hbciConsent.setCredentials(updatePsuAuthentication.getCredentials());

        ScaMethodsResponse response = authenticatePsu(updatePsuAuthentication);

        hbciConsent.setTanMethodList(response.getTanTransportTypes());
        hbciConsent.setStatus(PSUAUTHENTICATED);

        return hbciScaMapper.toUpdateAuthResponse(hbciConsent, new UpdateAuthResponse(HBCI, EMBEDDED,
            PSUAUTHENTICATED));
    }

    private ScaMethodsResponse authenticatePsu(UpdatePsuAuthenticationRequest authenticatePsuRequest) {
        try {
            HbciBpdUpdCallback hbciCallback = HbciBpdUpdCallback.createCallback(authenticatePsuRequest.getBank(), hbciBpdCacheHolder);
            HbciDialogRequest dialogRequest = hbciDialogRequestMapper.toHbciDialogRequest(authenticatePsuRequest, hbciCallback);

            HbciConsent hbciConsent = (HbciConsent) authenticatePsuRequest.getBankApiConsentData();
            hbciConsent.checkUpdSysIdCache(sysIdMaxAgeMs, updMaxAgeMs);

            AbstractHbciDialog dialog = HbciDialogFactory.createDialog(BPD, dialogRequest, null, hbciBpdCacheHolder.getBpd(dialogRequest));
            HBCIExecStatus bpdExecStatus = dialog.execute(true);
            boolean withHktan = !bpdExecStatus.hasMessage("9400");
            if (!withHktan) {
                hbciConsent.setWithHktan(false);
            }

            PinTanPassport passport = fetchUpd(dialogRequest, withHktan, dialog.getPassport().getBPD());

            List tanMediaList = null;
            if (passport.jobSupported(GVTANMediaList.getLowlevelName())) {
                tanMediaList = fetchTanMedias(passport);
            }

            ScaMethodsResponse response = ScaMethodsResponse.builder()
                .tanTransportTypes(extractTanTransportTypes(passport, tanMediaList))
                .build();
            response.setBankApiConsentData(hbciCallback.updateConsentUpd(hbciConsent));
            return response;
        } catch (HBCI_Exception e) {
            throw HbciErrorUtils.handleHbciException(e);
        }
    }

    @Override
    public UpdateAuthResponse authorizeConsent(TransactionAuthorisationRequest transactionAuthorisationRequest) {
        HbciConsent hbciConsent = (HbciConsent) transactionAuthorisationRequest.getBankApiConsentData();
        hbciConsent.setScaAuthenticationData(transactionAuthorisationRequest.getScaAuthenticationData());

        return hbciScaMapper.toUpdateAuthResponse(hbciConsent, new UpdateAuthResponse(HBCI, EMBEDDED,
            hbciConsent.getStatus()));
    }

    @Override
    public UpdateAuthResponse selectPsuAuthenticationMethod(SelectPsuAuthenticationMethodRequest selectPsuAuthenticationMethod) {
        HbciConsent hbciConsent = (HbciConsent) selectPsuAuthenticationMethod.getBankApiConsentData();

        TanTransportType selectedMethod = hbciConsent.getTanMethodList().stream()
            .filter(tanTransportType -> tanTransportType.getId().equals(selectPsuAuthenticationMethod.getAuthenticationMethodId()))
            .filter(tanTransportType -> selectPsuAuthenticationMethod.getTanMediaName() == null
                || selectPsuAuthenticationMethod.getTanMediaName().equals(tanTransportType.getMedium()))
            .findFirst()
            .orElseThrow(() -> new MultibankingException(INVALID_SCA_METHOD));

        hbciConsent.setSelectedMethod(selectedMethod);
        hbciConsent.setStatus(SCAMETHODSELECTED);

        return hbciScaMapper.toUpdateAuthResponse(hbciConsent, new UpdateAuthResponse(HBCI, EMBEDDED,
            SCAMETHODSELECTED));
    }

    @Override
    public void revokeConsent(String consentId, Object bankApiConsentData) {
        //noop
    }

    @Override
    public UpdateAuthResponse getAuthorisationStatus(String consentId, String authorisationId,
                                                     Object bankApiConsentData) {
        HbciConsent hbciConsent = (HbciConsent) bankApiConsentData;
        return hbciScaMapper.toUpdateAuthResponse(hbciConsent, new UpdateAuthResponse(HBCI, EMBEDDED,
            hbciConsent.getStatus()));
    }

    @Override
    public void validateConsent(String consentId, String authorisationId, ScaStatus expectedConsentStatus,
                                Object bankApiConsentData) {
        HbciConsent hbciConsent = (HbciConsent) bankApiConsentData;

        if (hbciConsent.getHbciTanSubmit() != null && hbciConsent.getScaAuthenticationData() == null) {
            throw new MultibankingException(MultibankingError.INVALID_CONSENT_STATUS);
        }

        if (hbciConsent.getStatus() == SCAMETHODSELECTED || hbciConsent.getStatus() == FINALISED) {
            return;
        }
        throw new MultibankingException(MultibankingError.INVALID_CONSENT_STATUS);
    }

    @Override
    public void afterExecute(Object bankApiConsentData, AuthorisationCodeResponse authorisationCodeResponse) {
        //tansubmit persistence fur further call
        HbciConsent hbciConsent = (HbciConsent) bankApiConsentData;
        hbciConsent.setHbciTanSubmit(authorisationCodeResponse.getTanSubmit());
    }

    @Override
    public void submitAuthorisationCode(Object bankApiConsentData, String authorisationCode) {
        throw new UnsupportedOperationException();
    }

    @Override
    public PaymentStatusResponse getPaymentStatus(TransactionRequest request) {
        try {
            HbciConsent hbciConsent = (HbciConsent) request.getBankApiConsentData();
            hbciConsent.checkUpdSysIdCache(sysIdMaxAgeMs, updMaxAgeMs);

            HbciBpdUpdCallback hbciCallback = HbciBpdUpdCallback.createCallback(request.getBank(), hbciBpdCacheHolder);

            InstantPaymentStatusJob instantPaymentStatusJob = new InstantPaymentStatusJob(request, hbciBpdCacheHolder);
            PaymentStatusResponse response = instantPaymentStatusJob.execute(hbciCallback);
            response.setBankApiConsentData(hbciCallback.updateConsentUpd(hbciConsent));

            return response;
        } catch (HBCI_Exception e) {
            throw HbciErrorUtils.handleHbciException(e);
        }
    }

    private PinTanPassport fetchUpd(HbciDialogRequest dialogRequest, boolean withHktan, Map bpd) {
        HBCIUpdDialog dialog = (HBCIUpdDialog) HbciDialogFactory.createDialog(UPD, dialogRequest, null, bpd);
        dialog.setWithHktan(withHktan);
        dialog.execute(true);

        return dialog.getPassport();
    }

    private List fetchTanMedias(PinTanPassport passport) {
        GVTANMediaList gvtanMediaList = new GVTANMediaList(passport);

        HBCIJobsDialog dialog = new HBCIJobsDialog(passport);
        dialog.dialogInit(true, "HKTAB");
        dialog.addTask(gvtanMediaList);
        dialog.execute(true);

        return ((GVRTANMediaList) gvtanMediaList.getJobResult()).mediaList();
    }

    private List extractTanTransportTypes(PinTanPassport hbciPassport,
                                                            List tanMediaList) {
        return hbciPassport.getUserTwostepMechanisms()
            .stream()
            .map(id -> hbciPassport.getBankTwostepMechanisms().get(id))
            .filter(Objects::nonNull)
            .map(hbciTwoStepMechanism -> TanTransportType.builder()
                .id(hbciTwoStepMechanism.getSecfunc())
                .name(hbciTwoStepMechanism.getName())
                .inputInfo(hbciTwoStepMechanism.getInputinfo())
                .needTanMedia(hbciTwoStepMechanism.getNeedtanmedia().equals("2"))
                .timeoutDecoupledFirstStatusRequest(hbciTwoStepMechanism.getTimeoutDecoupledFirstStatusRequest())
                .timeoutDecoupledNextStatusRequest(hbciTwoStepMechanism.getTimeoutDecoupledNextStatusRequest())
                .decoupledMaxStatusRequests(hbciTwoStepMechanism.getMaxDecoupledStatusRequests())
                .build())
            .map(tanTransportType -> {
                if (!tanTransportType.isNeedTanMedia()) {
                    return Collections.singletonList(tanTransportType);
                } else {
                    return tanMediaList.stream()
                        .map(tanMediaInfo -> {
                            TanTransportType clone = SerializationUtils.clone(tanTransportType);
                            clone.setMedium(tanMediaInfo.mediaName);
                            return clone;
                        })
                        .collect(Collectors.toList());
                }
            })
            .flatMap(Collection::stream)
            .collect(Collectors.toList());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy