gnunet-svn
[Top][All Lists]
Advanced

[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.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]