[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated: prevent http client from throwing exce
From: |
gnunet |
Subject: |
[libeufin] branch master updated: prevent http client from throwing exceptions.. |
Date: |
Fri, 08 Nov 2019 12:57:55 +0100 |
This is an automated email from the git hooks/post-receive script.
marcello pushed a commit to branch master
in repository libeufin.
The following commit(s) were added to refs/heads/master by this push:
new d9e44a7 prevent http client from throwing exceptions..
d9e44a7 is described below
commit d9e44a79da332e58d33f29544d6dd4a8762d4c58
Author: Marcello Stanisci <address@hidden>
AuthorDate: Fri Nov 8 12:56:52 2019 +0100
prevent http client from throwing exceptions..
.. on != 200 responses.
---
nexus/src/main/kotlin/DB.kt | 4 +
nexus/src/main/kotlin/Main.kt | 78 ++++++++-----
sandbox/src/main/python/libeufin-cli | 211 ++++++-----------------------------
3 files changed, 89 insertions(+), 204 deletions(-)
diff --git a/nexus/src/main/kotlin/DB.kt b/nexus/src/main/kotlin/DB.kt
index ba2cbe2..8c31384 100644
--- a/nexus/src/main/kotlin/DB.kt
+++ b/nexus/src/main/kotlin/DB.kt
@@ -17,6 +17,8 @@ object EbicsSubscribersTable : IntIdTable() {
val signaturePrivateKey = blob("signaturePrivateKey")
val encryptionPrivateKey = blob("encryptionPrivateKey")
val authenticationPrivateKey = blob("authenticationPrivateKey")
+ val bankEncryptionPublicKey = blob("bankEncryptionPublicKey").nullable()
+ val bankAuthenticationPublicKey =
blob("bankAuthenticationPublicKey").nullable()
}
class EbicsSubscriberEntity(id: EntityID<Int>) : IntEntity(id) {
@@ -30,6 +32,8 @@ class EbicsSubscriberEntity(id: EntityID<Int>) :
IntEntity(id) {
var signaturePrivateKey by EbicsSubscribersTable.signaturePrivateKey
var encryptionPrivateKey by EbicsSubscribersTable.encryptionPrivateKey
var authenticationPrivateKey by
EbicsSubscribersTable.authenticationPrivateKey
+ var bankEncryptionPublicKey by
EbicsSubscribersTable.bankEncryptionPublicKey
+ var bankAuthenticationPublicKey by
EbicsSubscribersTable.bankAuthenticationPublicKey
}
fun dbCreateTables() {
diff --git a/nexus/src/main/kotlin/Main.kt b/nexus/src/main/kotlin/Main.kt
index ca6a90a..819dd79 100644
--- a/nexus/src/main/kotlin/Main.kt
+++ b/nexus/src/main/kotlin/Main.kt
@@ -38,6 +38,7 @@ import io.ktor.routing.post
import io.ktor.routing.routing
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
+import org.apache.commons.codec.digest.Crypt
import org.apache.xml.security.binding.xmldsig.RSAKeyValueType
import org.apache.xml.security.binding.xmldsig.SignatureType
import org.jetbrains.exposed.sql.transactions.transaction
@@ -90,8 +91,7 @@ fun expectId(param: String?) : Int {
* @return null when the bank could not be reached, otherwise returns the
* response already converted in JAXB.
*/
-// suspend inline fun <reified S, reified T>HttpClient.postToBank(url: String,
body: JAXBElement<T>) : JAXBElement<S>? {
-suspend inline fun <reified S>HttpClient.postToBank(url: String, body:
String): JAXBElement<S>? {
+suspend inline fun <reified S>HttpClient.postToBank(url: String, body:
String): JAXBElement<S> {
val response = try {
this.post<String>(
@@ -101,21 +101,21 @@ suspend inline fun <reified S>HttpClient.postToBank(url:
String, body: String):
}
)
} catch (e: Exception) {
- e.printStackTrace()
- return null
+ throw UnreachableBankError(HttpStatusCode.InternalServerError)
}
- // note: not checking status code, as EBICS mandates to return "200 OK"
for ANY outcome.
- return XMLUtil.convertStringToJaxb(response)
+ try {
+ return XMLUtil.convertStringToJaxb(response)
+ } catch (e: Exception) {
+ throw UnparsableResponse(HttpStatusCode.BadRequest)
+ }
}
-// takes JAXB
-suspend inline fun <reified T, reified S>HttpClient.postToBank(url: String,
body: T): JAXBElement<S>? {
+suspend inline fun <reified T, reified S>HttpClient.postToBank(url: String,
body: T): JAXBElement<S> {
return this.postToBank<S>(url, XMLUtil.convertJaxbToString(body))
}
-// takes DOM
-suspend inline fun <reified S>HttpClient.postToBank(url: String, body:
Document): JAXBElement<S>? {
+suspend inline fun <reified S>HttpClient.postToBank(url: String, body:
Document): JAXBElement<S> {
return this.postToBank<S>(url, XMLUtil.convertDomToString(body))
}
@@ -138,6 +138,8 @@ fun getGregorianDate(): XMLGregorianCalendar {
data class NotAnIdError(val statusCode: HttpStatusCode) : Exception("String ID
not convertible in number")
data class SubscriberNotFoundError(val statusCode: HttpStatusCode) :
Exception("Subscriber not found in database")
data class UnreachableBankError(val statusCode: HttpStatusCode) :
Exception("Could not reach the bank")
+data class UnparsableResponse(val statusCode: HttpStatusCode) :
Exception("Bank responded with non-XML / non-EBICS " +
+ "content")
data class EbicsError(val codeError: String) : Exception("Bank did not
accepted EBICS request, error is: " + codeError
)
@@ -145,7 +147,9 @@ data class EbicsError(val codeError: String) :
Exception("Bank did not accepted
fun main() {
dbCreateTables()
testData() // gets always id == 1
- val client = HttpClient()
+ val client = HttpClient(){
+ expectSuccess = false // this way, does not throw exceptions on != 200
responses
+ }
val logger = LoggerFactory.getLogger("tech.libeufin.nexus")
@@ -169,6 +173,12 @@ fun main() {
call.respondText("Bad request\n", ContentType.Text.Plain,
HttpStatusCode.BadRequest)
}
+ exception<UnparsableResponse> { cause ->
+ logger.error("Exception while handling '${call.request.uri}'",
cause)
+ call.respondText("Could not parse bank response\n",
ContentType.Text.Plain, HttpStatusCode
+ .InternalServerError)
+ }
+
exception<UnreachableBankError> { cause ->
logger.error("Exception while handling '${call.request.uri}'",
cause)
call.respondText("Could not reach the bank\n",
ContentType.Text.Plain, HttpStatusCode.InternalServerError)
@@ -308,19 +318,11 @@ fun main() {
throw EbicsError(responseJaxb.value.body.returnCode.value)
}
- call.respond(
- HttpStatusCode.OK,
- NexusError("Sandbox accepted the key!")
- )
+ call.respondText("Bank accepted signature key\n",
ContentType.Text.Plain, HttpStatusCode.OK)
return@post
}
post("/ebics/subscribers/{id}/sync") {
- // fetch sub's EBICS URL, done
- // prepare message, done
- // send it out, done
- // _parse_ response!
- // respond to client
val id = expectId(call.parameters["id"])
val (url, body, encPrivBlob) = transaction {
val subscriber = EbicsSubscriberEntity.findById(id) ?:
throw SubscriberNotFoundError(HttpStatusCode.NotFound)
@@ -354,9 +356,7 @@ fun main() {
Triple(subscriber.ebicsURL, hpbDoc,
subscriber.encryptionPrivateKey.toByteArray())
}
- val response =
client.postToBank<EbicsKeyManagementResponse>(url, body) ?: throw
UnreachableBankError(
- HttpStatusCode.InternalServerError
- )
+ val response =
client.postToBank<EbicsKeyManagementResponse>(url, body)
if (response.value.body.returnCode.value != "000000") {
throw EbicsError(response.value.body.returnCode.value)
@@ -369,10 +369,27 @@ fun main() {
response.value.body.dataTransfer!!.orderData.value
)
- var dataCompr = CryptoUtil.decryptEbicsE002(er,
CryptoUtil.loadRsaPrivateKey(encPrivBlob))
- var data =
EbicsOrderUtil.decodeOrderDataXml<HPBResponseOrderData>(dataCompr)
+ val dataCompr = CryptoUtil.decryptEbicsE002(er,
CryptoUtil.loadRsaPrivateKey(encPrivBlob))
+ val data =
EbicsOrderUtil.decodeOrderDataXml<HPBResponseOrderData>(dataCompr)
- call.respond(HttpStatusCode.NotImplemented, NexusError("work
in progress"))
+ val bankAuthPubBlob =
CryptoUtil.loadRsaPublicKeyFromComponents(
+
data.authenticationPubKeyInfo.pubKeyValue.rsaKeyValue.modulus,
+
data.authenticationPubKeyInfo.pubKeyValue.rsaKeyValue.exponent
+ )
+
+ val bankEncPubBlob = CryptoUtil.loadRsaPublicKeyFromComponents(
+ data.encryptionPubKeyInfo.pubKeyValue.rsaKeyValue.modulus,
+ data.encryptionPubKeyInfo.pubKeyValue.rsaKeyValue.exponent
+ )
+
+ // put bank's keys into database.
+ transaction {
+ val subscriber = EbicsSubscriberEntity.findById(id)
+ subscriber!!.bankAuthenticationPublicKey =
SerialBlob(bankAuthPubBlob.encoded)
+ subscriber!!.bankEncryptionPublicKey =
SerialBlob(bankEncPubBlob.encoded)
+ }
+
+ call.respondText("Bank keys stored in database\n",
ContentType.Text.Plain, HttpStatusCode.OK)
return@post
}
@@ -448,10 +465,11 @@ fun main() {
throw EbicsError(responseJaxb.value.body.returnCode.value)
}
- call.respond(
- HttpStatusCode.OK,
- NexusError("Sandbox accepted the keys!")
- )
+ call.respondText(
+ "Bank accepted authentication and encryption keys\n",
+ ContentType.Text.Plain,
+ HttpStatusCode.OK)
+
return@post
}
}
diff --git a/sandbox/src/main/python/libeufin-cli
b/sandbox/src/main/python/libeufin-cli
index db261bb..8f27b27 100755
--- a/sandbox/src/main/python/libeufin-cli
+++ b/sandbox/src/main/python/libeufin-cli
@@ -9,211 +9,74 @@ from requests import post, get
from Crypto.PublicKey import RSA
from urllib.parse import urljoin
-CUSTOMERS_PATH = "/tmp/libeufindata/customers"
-RECIPIENT_BANK = "LibEuBank"
-RSA_LENGTH = 2048 # key "length"
-IA_VERSION = "X002"
-ENC_VERSION = "E002"
-ES_VERSION = "A005"
-
@click.group()
@click.option(
- "--base-url", default="http://localhost:5000/",
- help="Base URL of the bank (defaults to http://localhost:5000/)")
+ "--base-url", default="http://localhost:5001/",
+ help="Base URL of the nexus (defaults to http://localhost:5001/)")
@click.pass_context
def cli(ctx, base_url):
ctx.obj = dict(base_url=base_url)
@cli.group()
-def admin():
+def ebics():
pass
-@admin.command(help="Create a new customer (generating name)")
+@ebics.command(help="send INI message")
@click.pass_obj
-def customers(obj):
-
- from faker import Faker
- name = Faker().name()
+@click.option(
+ "--customer-id",
+ help="numerical ID of the customer at the Nexus",
+ required=False,
+ default=1)
+def ini(obj, customer_id):
- url = urljoin(obj["base_url"], "/admin/customers")
- print("Submitting '{}' to {}".format(name, url))
+ url = urljoin(obj["base_url"],
"/ebics/subscribers/{}/sendIni".format(customer_id))
try:
- resp = post(url, json=dict(name=name))
+ resp = post(url)
except Exception:
print("Could not reach the bank")
return
assert(resp.status_code == 200)
- # use the customer id contained in the response to
- # query for your details.
- customer_id = resp.json().get("id")
- assert(customer_id != None)
-
- customer_path = "{}/{}/".format(CUSTOMERS_PATH, customer_id)
- try:
- os.makedirs(customer_path)
- except OSError as e:
- # For now, just overwrite all is found under existing directory.
- assert(e.errno == errno.EEXIST)
+ print(resp.content.decode("utf-8"))
- # Generate keys for new user.
- for keytype in ("eskey", "iakey", "enckey"):
- key = RSA.generate(RSA_LENGTH)
- pem = key.exportKey("PEM").decode("ascii")
- keyfile = open("{}/{}.pem".format(customer_path, keytype), "w")
- keyfile.write(pem)
- keyfile.write("\n")
- keyfile.close()
- print(
- "Customer (id == {}) and private keys ({}) correctly
generated.".format(
- customer_id, customer_path
- )
- )
-@admin.command(help="Ask details about a customer")
+@ebics.command(help="send HIA message")
+@click.pass_obj
@click.option(
"--customer-id",
- help="bank non-EBICS identifier of the customer",
- required=True)
-@click.pass_obj
-def customer_info(obj, customer_id):
-
- url = urljoin(
- obj["base_url"], "/admin/customers/{}".format(customer_id)
- )
-
+ help="numerical ID of the customer at the Nexus",
+ required=False,
+ default=1)
+def hia(obj, customer_id):
+
+ url = urljoin(obj["base_url"],
"/ebics/subscribers/{}/sendHia".format(customer_id))
try:
- resp = get(url)
+ resp = post(url)
except Exception:
- print("Could not reach the bank, aborting")
- return
-
- if resp.status_code != 200:
- print("Failed request, status: {}".format(resp.status_code))
+ print("Could not reach the bank")
return
- print(resp.json())
+ assert(resp.status_code == 200)
+ print(resp.content.decode("utf-8"))
-@admin.command(
- help="Confirm INI and HIA messages via JSON API"
-)
+@ebics.command(help="send HPB message")
+@click.pass_obj
@click.option(
"--customer-id",
- required=True,
- help="id of the customer at the bank (used to pick keyset on disk)"
-)
-@click.pass_obj
-def keyletter(obj, customer_id):
-
- url = urljoin(
- obj["base_url"], "/admin/customers/{}".format(customer_id)
- )
-
- try:
- resp = get(url)
- except Exception:
- print("Could not connect to the bank, aborting")
- return
-
- if resp.status_code != 200:
- print("Couldn't query info about the customer:
{}".format(resp.status_code))
- return
-
-
- user_id = resp.json().get("ebicsInfo", {}).get("userId")
- name = resp.json().get("name")
- assert(user_id)
- assert(name)
-
- # Take timestamp.
- ts = datetime.now()
-
- # Get keys from disk.
- try:
- eskey = RSA.importKey(
- open("{}/{}/eskey.pem".format(
- CUSTOMERS_PATH, customer_id), "r").read()
- )
-
- enckey = RSA.importKey(
- open("{}/{}/enckey.pem".format(
- CUSTOMERS_PATH, customer_id), "r").read()
- )
-
- iakey = RSA.importKey(
- open("{}/{}/iakey.pem".format(
- CUSTOMERS_PATH, customer_id), "r").read()
- )
-
- except FileNotFoundError:
- print("Could not find all the keys; now generating them all on the
fly..")
- eskey = RSA.generate(RSA_LENGTH)
- enckey = RSA.generate(RSA_LENGTH)
- iakey = RSA.generate(RSA_LENGTH)
-
- es_exponent = format(eskey.e, "x")
- es_modulus = format(eskey.n, "x")
-
- ia_exponent = format(iakey.e, "x")
- ia_modulus = format(iakey.n, "x")
-
- enc_exponent = format(enckey.e, "x")
- enc_modulus = format(enckey.n, "x")
-
- # Make the request body.
- body = dict(
-
- ini=dict(
- userId=user_id,
- customerId=customer_id,
- name=name,
- date=ts.strftime("%d.%m.%Y"),
- time=ts.strftime("%H.%M.%S"),
- recipient=RECIPIENT_BANK,
- version=ES_VERSION,
- public_exponent_length=eskey.n.bit_length(),
- public_exponent=es_exponent,
- public_modulus_length=eskey.e.bit_length(),
- public_modulus=es_modulus,
- hash=hashlib.sha256("{} {}".format(es_exponent,
es_modulus).encode()).hexdigest()
- ),
-
- hia=dict(
- userId=user_id,
- customerId=customer_id,
- name=name,
- date=ts.strftime("%d.%m.%Y"),
- time=ts.strftime("%H.%M.%S"),
- recipient=RECIPIENT_BANK,
- ia_version=IA_VERSION,
- ia_public_exponent_length=iakey.e.bit_length(),
- ia_public_exponent=ia_exponent,
- ia_public_modulus_length=iakey.n.bit_length(),
- ia_public_modulus=ia_modulus,
- ia_hash=hashlib.sha256("{} {}".format(ia_exponent,
ia_modulus).encode()).hexdigest(),
- enc_version=ENC_VERSION,
- enc_public_exponent_length=enckey.e.bit_length(),
- enc_public_exponent=enc_exponent,
- enc_public_modulus_length=enckey.n.bit_length(),
- enc_public_modulus=enc_modulus,
- enc_hash=hashlib.sha256("{} {}".format(enc_exponent,
enc_modulus).encode()).hexdigest()
- )
- )
-
- url = urljoin(
- obj["base_url"],
"/admin/customers/{}/ebics/keyletter".format(customer_id)
- )
-
+ help="numerical ID of the customer at the Nexus",
+ required=False,
+ default=1)
+def sync(obj, customer_id):
+
+ url = urljoin(obj["base_url"],
"/ebics/subscribers/{}/sync".format(customer_id))
try:
- resp = post(url, json=body)
+ resp = post(url)
except Exception:
- print("Could not reach the bank, aborting now")
- return
-
- if resp.status_code != 200:
- print("Bank did not accept this letter: {}.".format(resp.status_code))
+ print("Could not reach the bank")
return
- print("Letter accepted by the bank!")
+ assert(resp.status_code == 200)
+ print(resp.content.decode("utf-8"))
cli()
--
To stop receiving notification emails like this one, please contact
address@hidden.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [libeufin] branch master updated: prevent http client from throwing exceptions..,
gnunet <=