org.kapott.hbci.manager.ChallengeInfo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hbci4j-adorsys Show documentation
Show all versions of hbci4j-adorsys Show documentation
HBCI4j - Home Banking Computer Interface for Java - Clone from https://github.com/hbci4j/hbci4java
/* $Id: ChallengeInfo.java,v 1.9 2011/05/30 12:47:56 willuhn Exp $
This file is part of HBCI4Java
Copyright (C) 2001-2008 Stefan Palme
HBCI4Java 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.
HBCI4Java 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.kapott.hbci.manager;
import lombok.extern.slf4j.Slf4j;
import org.kapott.hbci.GV.AbstractHBCIJob;
import org.kapott.hbci.datatypes.SyntaxDE;
import org.kapott.hbci.datatypes.factory.SyntaxDEFactory;
import org.kapott.hbci.exceptions.HBCI_Exception;
import org.kapott.hbci.exceptions.InvalidUserDataException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Diese Klasse ermittelt die noetigen HKTAN-Challenge-Parameter fuer einen
* Geschaeftsvorfall
*/
@Slf4j
public class ChallengeInfo {
/**
* Das Singleton.
*/
private static ChallengeInfo singleton = null;
private Map data; // Die Parameter-Daten aus der XML-Datei.
/**
* ct.
*/
private ChallengeInfo() {
log.debug("initializing challenge info engine");
////////////////////////////////////////////////////////////////////////////
// XML-Datei lesen
InputStream dataStream;
String filename = "challengedata.xml";
dataStream = ChallengeInfo.class.getClassLoader().getResourceAsStream(filename);
if (dataStream == null)
throw new InvalidUserDataException("*** can not load challenge information from " + filename);
// mit den so gefundenen xml-daten ein xml-dokument bauen
Document doc;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setIgnoringComments(true);
dbf.setValidating(true);
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(dataStream);
dataStream.close();
} catch (Exception e) {
throw new HBCI_Exception("*** can not load challengedata from file " + filename, e);
}
//
////////////////////////////////////////////////////////////////////////////
data = new HashMap<>();
////////////////////////////////////////////////////////////////////////////
// Parsen
NodeList jobs = doc.getElementsByTagName("job");
int size = jobs.getLength();
for (int i = 0; i < size; ++i) {
Element job = (Element) jobs.item(i);
String code = job.getAttribute("code");
data.put(code, new Job(job));
}
//
////////////////////////////////////////////////////////////////////////////
log.debug("challenge information loaded");
}
/**
* Erzeugt ein neues Challenge-Info-Objekt.
*
* @return das Challenge-Info-Objekt.
*/
public static synchronized ChallengeInfo getInstance() {
if (singleton == null)
singleton = new ChallengeInfo();
return singleton;
}
/**
* Liefert die Challenge-Daten fuer einen Geschaeftsvorfall.
*
* @param code die Segmentkennung des Geschaeftsvorfalls.
* @return die Challenge-Daten.
*/
public Job getData(String code) {
return data.get(code);
}
/**
* Uebernimmt die Challenge-Parameter in den HKTAN-Geschaeftsvorfall.
*
* @param task der Job, zu dem die Challenge-Parameter ermittelt werden sollen.
* @param hktan der HKTAN-Geschaeftsvorfall, in dem die Parameter gesetzt werden sollen.
* @param hbciTwoStepMechanism die BPD-Informationen zum TAN-Verfahren.
*/
public void applyParams(AbstractHBCIJob task, AbstractHBCIJob hktan, HBCITwoStepMechanism hbciTwoStepMechanism) {
String code = task.getHBCICode(); // Code des Geschaeftsvorfalls
// Job-Parameter holen
Job job = this.getData(code);
// Den Geschaeftsvorfall kennen wir nicht. Dann brauchen wir
// auch keine Challenge-Parameter setzen
if (job == null) {
log.info("have no challenge data for " + code + ", will not apply challenge params");
return;
}
HHDVersion version = HHDVersion.find(hbciTwoStepMechanism);
log.debug("using hhd version " + version);
// Parameter fuer die passende HHD-Version holen
HhdVersion hhd = job.getVersion(version.getChallengeVersion());
// Wir haben keine Parameter fuer diese HHD-Version
if (hhd == null) {
log.info("have no challenge data for " + code + " in " + version + ", will not apply challenge params");
return;
}
// Schritt 1: Challenge-Klasse uebernehmen
String klass = hhd.getKlass();
log.debug("using challenge klass " + klass);
hktan.setParam("challengeklass", klass);
// Schritt 2: Challenge-Parameter uebernehmen
List params = hhd.getParams();
for (int i = 0; i < params.size(); ++i) {
int num = i + 1; // Die Job-Parameter beginnen bei 1
Param param = params.get(i);
// Checken, ob der Parameter angewendet werden soll.
if (!param.isComplied(hbciTwoStepMechanism)) {
log.debug("skipping challenge parameter " + num + " (" + param.path + "), condition " + param.conditionName + "=" + param.conditionValue + " not complied");
continue;
}
// Parameter uebernehmen. Aber nur wenn er auch einen Wert hat.
// Seit HHD 1.4 duerfen Parameter mittendrin optional sein, sie
// werden dann freigelassen
String value = param.getValue(task);
if (value == null || value.length() == 0) {
log.debug("challenge parameter " + num + " (" + param.path + ") is empty");
continue;
}
log.debug("adding challenge parameter " + num + " " + param.path + "=" + value);
hktan.setParam("ChallengeKlassParam" + num, value);
}
}
/**
* Eine Bean fuer die Parameter-Saetze eines Geschaeftsvorfalles fuer die HHD-Versionen.
*/
public static class Job {
/**
* Die Parameter fuer die jeweilige HHD-Version.
*/
private Map versions = new HashMap<>();
/**
* ct.
*
* @param job der XML-Knoten, in dem die Daten stehen.
*/
private Job(Element job) {
NodeList specs = job.getElementsByTagName("challengeinfo");
int size = specs.getLength();
for (int i = 0; i < size; ++i) {
Element spec = (Element) specs.item(i);
String version = spec.getAttribute("spec");
this.versions.put(version, new HhdVersion(spec));
}
}
/**
* Liefert die Challenge-Parameter fuer die angegeben HHD-Version.
*
* @param version die HHD-Version.
* @return die Challenge-Parameter fuer die HHD-Version.
*/
HhdVersion getVersion(String version) {
return this.versions.get(version);
}
}
/**
* Eine Bean fuer den Parameter-Satz eines Geschaeftvorfalles innerhalb einer HHD-Version.
*/
public static class HhdVersion {
/**
* Die Challenge-Klasse.
*/
private String klass;
/**
* Liste der Challenge-Parameter.
*/
private List params = new ArrayList<>();
/**
* ct.
*
* @param spec der XML-Knoten mit den Daten.
*/
private HhdVersion(Element spec) {
this.klass = spec.getElementsByTagName("klass").item(0).getFirstChild().getNodeValue();
NodeList list = spec.getElementsByTagName("param");
int size = list.getLength();
for (int i = 0; i < size; ++i) {
Element param = (Element) list.item(i);
this.params.add(new Param(param));
}
}
/**
* Liefert die Challenge-Klasse.
*
* @return die Challenge-Klasse.
*/
public String getKlass() {
return this.klass;
}
/**
* Liefert die Challenge-Parameter fuer den Geschaeftsvorfall in dieser HHD-Version.
*
* @return die Challenge-Parameter fuer den Geschaeftsvorfall in dieser HHD-Version.
*/
public List getParams() {
return this.params;
}
}
/**
* Eine Bean fuer einen einzelnen Challenge-Parameter.
*/
public static class Param {
/**
* Der Typ des Parameters.
*/
private String type;
/**
* Der Pfad in den Geschaeftsvorfall-Parametern, unter dem der Wert steht.
*/
private String path;
/**
* Optional: Der Name einer Bedingung, die erfuellt sein muss, damit
* der Parameter verwendet wird. Konkret ist hier der Name eines Property
* aus secmechInfo gemeint. Also ein BPD-Parameter.
*/
private String conditionName;
/**
* Optional: Der Wert, den der BPD-Parameter haben muss, damit der Challenge-Parameter
* verwendet wird.
*/
private String conditionValue;
/**
* ct.
*
* @param param der XML-Knoten mit den Daten.
*/
private Param(Element param) {
Node content = param.getFirstChild();
this.path = content != null ? content.getNodeValue() : null;
this.type = param.getAttribute("type");
this.conditionName = param.getAttribute("condition-name");
this.conditionValue = param.getAttribute("condition-value");
}
/**
* Liefert true, wenn entweder keine Bedingung angegeben ist oder
* die Bedingung erfuellt ist und der Parameter verwendet werden kann.
*
* @param hbciTwoStepMechanism die BPD-Informationen zum TAN-Verfahren.
* @return true, wenn der Parameter verwendet werden kann.
*/
public boolean isComplied(HBCITwoStepMechanism hbciTwoStepMechanism) {
if (this.conditionName == null || this.conditionName.length() == 0)
return true;
// Wir haben eine Bedingung. Mal schauen, ob sie erfuellt ist.
String value = hbciTwoStepMechanism.getNeedchallengevalue();
return value != null && value.equals(this.conditionValue);
}
/**
* Liefert den Typ des Parameters.
*
* @return der Typ des Parameters.
*/
public String getType() {
return this.type;
}
/**
* Liefert den Pfad zum Wert.
*
* @return der Pfad zum Wert.
*/
public String getPath() {
return this.path;
}
/**
* Liefert den Wert des Parameters.
*
* @param job der Geschaeftsvorfall.
* @return der Wert des Parameters.
*/
private String getValue(AbstractHBCIJob job) {
// Leerer Parameter
if (this.path == null || this.path.length() == 0)
return null;
String value = job.getChallengeParam(this.path);
// Wert im passenden Format zurueckliefern
return format(value);
}
/**
* Formatiert den Text abhaengig vom Typ.
* Wenn kein Typ angegeben ist, wird der Wert unformatiert zurueckgegeben.
*
* @param value der zu formatierende Wert.
* @return der formatierte Wert.
*/
public String format(String value) {
// Bei leeren Werten lieferen wir generell NULL.
// Die Parameter werden dann uebersprungen.
if (value == null || value.trim().length() == 0)
return null;
// Wenn kein Typ angegeben ist, gibts auch nichts zu formatieren.
// Nein, wir duerfen NICHT SyntaxAN verwenden. Denn die Parameter
// in ChallengeKlassParams#param[1-9] sind ja bereits als Type AN
// deklariert. Wuerden wir hier SyntaxAN verwenden, wuerden die
// Werte dann doppelt codiert werden (das zweite Codieren macht ja
// anschliessend HBCI4Java intern beim Zusammenbauen des Segments).
// Was zum Beispiel dazu fuehren wuerde, dass ein Sonderzeichen wie
// "+" oder "?" doppelt escaped werden wuerde.
if (this.type == null || this.type.trim().length() == 0)
return value;
SyntaxDE syntax = SyntaxDEFactory.createSyntaxDE(this.type, this.path, value, 0, 0);
return syntax.toString(0);
}
}
}