gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated (62d0490 -> 786b70a)


From: gnunet
Subject: [libeufin] branch master updated (62d0490 -> 786b70a)
Date: Tue, 29 Jun 2021 15:59:11 +0200

This is an automated email from the git hooks/post-receive script.

ms pushed a change to branch master
in repository libeufin.

    from 62d0490  Importing dependencies to implement form-based authentication.
     new db3e710  DB: making Ebics subscriber point at bank accounts.
     new cf83585  Commenting out Jinja initialization.
     new 1117d23  Jinja and form-auth experiments
     new 1ec8f2c  drafting user creation
     new c9a3b09  drafting bank config table
     new 5b88146  CLI command to configure the Sandbox
     new 786b70a  helper that returns a bank configuration, given the hostname.

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .idea/libraries-with-intellij-classes.xml          |  65 ++++++++++++
 .../src/main/kotlin/tech/libeufin/sandbox/DB.kt    |  49 ++++++++-
 .../main/kotlin/tech/libeufin/sandbox/Helpers.kt   |  92 +++++++++++++++-
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  | 117 +++++++++++++++++++--
 4 files changed, 308 insertions(+), 15 deletions(-)
 create mode 100644 .idea/libraries-with-intellij-classes.xml

diff --git a/.idea/libraries-with-intellij-classes.xml 
b/.idea/libraries-with-intellij-classes.xml
new file mode 100644
index 0000000..9fa3156
--- /dev/null
+++ b/.idea/libraries-with-intellij-classes.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="libraries-with-intellij-classes">
+    <option name="intellijApiContainingLibraries">
+      <list>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="ideaIU" />
+          <option name="groupId" value="com.jetbrains.intellij.idea" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="ideaIU" />
+          <option name="groupId" value="com.jetbrains" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="ideaIC" />
+          <option name="groupId" value="com.jetbrains.intellij.idea" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="ideaIC" />
+          <option name="groupId" value="com.jetbrains" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="pycharmPY" />
+          <option name="groupId" value="com.jetbrains.intellij.pycharm" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="pycharmPY" />
+          <option name="groupId" value="com.jetbrains" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="pycharmPC" />
+          <option name="groupId" value="com.jetbrains.intellij.pycharm" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="pycharmPC" />
+          <option name="groupId" value="com.jetbrains" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="clion" />
+          <option name="groupId" value="com.jetbrains.intellij.clion" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="clion" />
+          <option name="groupId" value="com.jetbrains" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="riderRD" />
+          <option name="groupId" value="com.jetbrains.intellij.rider" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="riderRD" />
+          <option name="groupId" value="com.jetbrains" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="goland" />
+          <option name="groupId" value="com.jetbrains.intellij.goland" />
+        </LibraryCoordinatesState>
+        <LibraryCoordinatesState>
+          <option name="artifactId" value="goland" />
+          <option name="groupId" value="com.jetbrains" />
+        </LibraryCoordinatesState>
+      </list>
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 1cc38fd..99bed21 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -22,10 +22,13 @@ package tech.libeufin.sandbox
 import org.jetbrains.exposed.dao.Entity
 import org.jetbrains.exposed.dao.EntityClass
 import org.jetbrains.exposed.dao.IntEntity
+import org.jetbrains.exposed.dao.LongEntity
 import org.jetbrains.exposed.dao.IntEntityClass
+import org.jetbrains.exposed.dao.LongEntityClass
 import org.jetbrains.exposed.dao.id.EntityID
 import org.jetbrains.exposed.dao.id.IdTable
 import org.jetbrains.exposed.dao.id.IntIdTable
+import org.jetbrains.exposed.dao.id.LongIdTable
 import org.jetbrains.exposed.sql.*
 import org.jetbrains.exposed.sql.transactions.TransactionManager
 import org.jetbrains.exposed.sql.transactions.transaction
@@ -84,6 +87,39 @@ enum class KeyState {
     RELEASED
 }
 
+object SandboxConfigsTable : LongIdTable() {
+    val currency = text("currency")
+    val allowRegistrations = bool("allowRegistrations")
+    val bankDebtLimit = integer("bankDebtLimit")
+    val usersDebtLimit = integer("usersDebtLimit")
+    val hostname = text("hostname")
+}
+
+class SandboxConfigEntity(id: EntityID<Long>) : LongEntity(id) {
+    companion object : 
LongEntityClass<SandboxConfigEntity>(SandboxConfigsTable)
+    var currency by SandboxConfigsTable.currency
+    var allowRegistrations by SandboxConfigsTable.allowRegistrations
+    var bankDebtLimit by SandboxConfigsTable.bankDebtLimit
+    var usersDebtLimit by SandboxConfigsTable.usersDebtLimit
+    var name by SandboxConfigsTable.hostname
+}
+
+object SandboxUsersTable : LongIdTable() {
+    val username = text("username")
+    val passwordHash = text("password")
+    val superuser = bool("superuser") // admin
+    val bankAccount = reference("bankAccout", BankAccountsTable)
+}
+
+class SandboxUserEntity(id: EntityID<Long>) : LongEntity(id) {
+    companion object : LongEntityClass<SandboxUserEntity>(SandboxUsersTable)
+    var username by SandboxUsersTable.username
+    var passwordHash by SandboxUsersTable.passwordHash
+    var superuser by SandboxUsersTable.superuser
+    var bankAccount by BankAccountEntity referencedOn 
SandboxUsersTable.bankAccount
+}
+
+
 /**
  * This table stores RSA public keys of subscribers.
  */
@@ -94,7 +130,6 @@ object EbicsSubscriberPublicKeysTable : IntIdTable() {
 
 class EbicsSubscriberPublicKeyEntity(id: EntityID<Int>) : IntEntity(id) {
     companion object : 
IntEntityClass<EbicsSubscriberPublicKeyEntity>(EbicsSubscriberPublicKeysTable)
-
     var rsaPublicKey by EbicsSubscriberPublicKeysTable.rsaPublicKey
     var state by EbicsSubscriberPublicKeysTable.state
 }
@@ -133,6 +168,9 @@ object EbicsSubscribersTable : IntIdTable() {
     val authenticationKey = reference("authorizationKey", 
EbicsSubscriberPublicKeysTable).nullable()
     val nextOrderID = integer("nextOrderID")
     val state = enumeration("state", SubscriberState::class)
+    // setting as nullable to integrate this change more seamlessly into the 
current
+    // implementation.  Can be removed eventually.
+    val bankAccount = reference("bankAccount", BankAccountsTable).nullable()
 }
 
 class EbicsSubscriberEntity(id: EntityID<Int>) : IntEntity(id) {
@@ -147,6 +185,7 @@ class EbicsSubscriberEntity(id: EntityID<Int>) : 
IntEntity(id) {
     var authenticationKey by EbicsSubscriberPublicKeyEntity 
optionalReferencedOn EbicsSubscribersTable.authenticationKey
     var nextOrderID by EbicsSubscribersTable.nextOrderID
     var state by EbicsSubscribersTable.state
+    var bankAccount by BankAccountEntity optionalReferencedOn 
EbicsSubscribersTable.bankAccount
 }
 
 /**
@@ -285,7 +324,6 @@ object BankAccountsTable : IntIdTable() {
     val bic = text("bic")
     val name = text("name")
     val label = text("label").uniqueIndex("accountLabelIndex")
-    val subscriber = reference("subscriber", EbicsSubscribersTable)
     val currency = text("currency")
 }
 
@@ -296,7 +334,6 @@ class BankAccountEntity(id: EntityID<Int>) : IntEntity(id) {
     var bic by BankAccountsTable.bic
     var name by BankAccountsTable.name
     var label by BankAccountsTable.label
-    var subscriber by EbicsSubscriberEntity referencedOn 
BankAccountsTable.subscriber
     var currency by BankAccountsTable.currency
 }
 
@@ -327,7 +364,9 @@ fun dbDropTables(dbConnectionString: String) {
             BankAccountTransactionsTable,
             BankAccountsTable,
             BankAccountReportsTable,
-            BankAccountStatementsTable
+            BankAccountStatementsTable,
+            SandboxConfigsTable,
+            SandboxUsersTable
         )
     }
 }
@@ -337,6 +376,8 @@ fun dbCreateTables(dbConnectionString: String) {
     TransactionManager.manager.defaultIsolationLevel = 
Connection.TRANSACTION_SERIALIZABLE
     transaction {
         SchemaUtils.create(
+            SandboxConfigsTable,
+            SandboxUsersTable,
             EbicsSubscribersTable,
             EbicsHostsTable,
             EbicsDownloadTransactionsTable,
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
index bc9c908..7f7d7aa 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
@@ -19,6 +19,9 @@
 
 package tech.libeufin.sandbox
 
+import com.google.common.io.Resources
+import com.hubspot.jinjava.Jinjava
+import com.hubspot.jinjava.lib.fn.ELFunctionDefinition
 import io.ktor.http.HttpStatusCode
 import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
 import org.jetbrains.exposed.sql.and
@@ -66,12 +69,33 @@ fun getBankAccountFromLabel(label: String): 
BankAccountEntity {
 
 fun getBankAccountFromSubscriber(subscriber: EbicsSubscriberEntity): 
BankAccountEntity {
     return transaction {
-        BankAccountEntity.find(BankAccountsTable.subscriber eq subscriber.id)
-    }.firstOrNull() ?: throw SandboxError(
-        HttpStatusCode.NotFound,
-        "Subscriber doesn't have any bank account"
+        subscriber.bankAccount ?: throw SandboxError(
+            HttpStatusCode.NotFound,
+            "Subscriber doesn't have any bank account"
+        )
+    }
+}
+
+/**
+ * Fetch a configuration for Sandbox, corresponding to the host that runs the 
service.
+ */
+fun getSandboxConfig(hostname: String?): SandboxConfigEntity {
+    var ret: SandboxConfigEntity? = transaction {
+        if (hostname == null) {
+            SandboxConfigEntity.all().firstOrNull()
+        } else {
+            SandboxConfigEntity.find {
+                SandboxConfigsTable.hostname eq hostname
+            }.firstOrNull()
+        }
+    }
+    if (ret == null) throw SandboxError(
+        HttpStatusCode.InternalServerError,
+        "Serving from a non configured host"
     )
+    return ret
 }
+
 fun getEbicsSubscriberFromDetails(userID: String, partnerID: String, hostID: 
String): EbicsSubscriberEntity {
     return transaction {
         EbicsSubscriberEntity.find {
@@ -82,4 +106,62 @@ fun getEbicsSubscriberFromDetails(userID: String, 
partnerID: String, hostID: Str
             "Ebics subscriber not found"
         )
     }
-}
\ No newline at end of file
+}
+
+/**
+ * FIXME: commenting out until a solution for i18n is found.
+ *
+private fun initJinjava(): Jinjava {
+    class JinjaFunctions {
+        // Used by templates to retrieve configuration values.
+        fun settings_value(name: String): String {
+            return "foo"
+        }
+        fun gettext(translatable: String): String {
+            // temporary, just to make the compiler happy.
+            return translatable
+        }
+        fun url(name: String): String {
+            val map = mapOf<String, String>(
+                "login" to "todo",
+                "profile" to "todo",
+                "register" to "todo",
+                "public-accounts" to "todo"
+            )
+            return map[name] ?: throw 
SandboxError(HttpStatusCode.InternalServerError, "URL name unknown")
+        }
+    }
+    val jinjava = Jinjava()
+    val settingsValueFunc = ELFunctionDefinition(
+        "tech.libeufin.sandbox", "settings_value",
+        JinjaFunctions::class.java, "settings_value", String::class.java
+    )
+    val gettextFuncAlias = ELFunctionDefinition(
+        "tech.libeufin.sandbox", "_",
+        JinjaFunctions::class.java, "gettext", String::class.java
+    )
+    val gettextFunc = ELFunctionDefinition(
+        "", "gettext",
+        JinjaFunctions::class.java, "gettext", String::class.java
+    )
+    val urlFunc = ELFunctionDefinition(
+        "tech.libeufin.sandbox", "url",
+        JinjaFunctions::class.java, "url", String::class.java
+    )
+
+    jinjava.globalContext.registerFunction(settingsValueFunc)
+    jinjava.globalContext.registerFunction(gettextFunc)
+    jinjava.globalContext.registerFunction(gettextFuncAlias)
+    jinjava.globalContext.registerFunction(urlFunc)
+
+    return jinjava
+}
+
+val jinjava = initJinjava()
+
+fun renderTemplate(templateName: String, context: Map<String, String>): String 
{
+    val template = Resources.toString(Resources.getResource(
+        "templates/$templateName"), Charsets.UTF_8
+    )
+    return jinjava.render(template, context)
+} **/
\ No newline at end of file
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 3d96517..2f41e58 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -62,6 +62,7 @@ import com.github.ajalt.clikt.core.context
 import com.github.ajalt.clikt.core.subcommands
 import com.github.ajalt.clikt.output.CliktHelpFormatter
 import com.github.ajalt.clikt.parameters.options.default
+import com.github.ajalt.clikt.parameters.options.flag
 import com.github.ajalt.clikt.parameters.options.option
 import com.github.ajalt.clikt.parameters.options.versionOption
 import com.github.ajalt.clikt.parameters.types.int
@@ -86,6 +87,7 @@ import 
tech.libeufin.sandbox.BankAccountTransactionsTable.debtorIban
 import tech.libeufin.sandbox.BankAccountTransactionsTable.debtorName
 import tech.libeufin.sandbox.BankAccountTransactionsTable.direction
 import tech.libeufin.sandbox.BankAccountTransactionsTable.pmtInfId
+import tech.libeufin.sandbox.SandboxConfigEntity
 import tech.libeufin.util.*
 import tech.libeufin.util.ebics_h004.EbicsResponse
 import tech.libeufin.util.ebics_h004.EbicsTypes
@@ -102,6 +104,34 @@ data class SandboxError(val statusCode: HttpStatusCode, 
val reason: String) : Ex
 data class SandboxErrorJson(val error: SandboxErrorDetailJson)
 data class SandboxErrorDetailJson(val type: String, val description: String)
 
+class Config : CliktCommand("Insert one configuration into the database") {
+    init {
+        context {
+            helpFormatter = CliktHelpFormatter(showDefaultValues = true)
+        }
+    }
+
+    private val currencyOption by option().default("EUR")
+    private val bankDebtLimitOption by option().int().default(1000000)
+    private val usersDebtLimitOption by option().int().default(1000)
+    private val allowRegistrationsOption by option().flag(default = true)
+
+    override fun run() {
+        val dbConnString = getDbConnFromEnv(SANDBOX_DB_ENV_VAR_NAME)
+        execThrowableOrTerminate {
+            dbCreateTables(dbConnString)
+            transaction {
+                SandboxConfigEntity.new {
+                    currency = currencyOption
+                    bankDebtLimit = bankDebtLimitOption
+                    usersDebtLimit = usersDebtLimitOption
+                    allowRegistrations = allowRegistrationsOption
+                }
+            }
+        }
+    }
+}
+
 class ResetTables : CliktCommand("Drop all the tables from the database") {
     init {
         context {
@@ -201,7 +231,7 @@ class SandboxCommand : CliktCommand(invokeWithoutSubcommand 
= true, printHelpOnE
 }
 
 fun main(args: Array<String>) {
-    SandboxCommand().subcommands(Serve(), ResetTables()).main(args)
+    SandboxCommand().subcommands(Serve(), ResetTables(), Config()).main(args)
 }
 
 suspend inline fun <reified T : Any> ApplicationCall.receiveJson(): T {
@@ -227,8 +257,16 @@ fun serverMain(dbName: String, port: Int) {
         }
         install(Authentication) {
             // Web-based authentication for Bank customers.
-            form {
-
+            form("auth-form") {
+                userParamName = "username"
+                passwordParamName = "password"
+                validate { credentials ->
+                    if (credentials.name == "test") {
+                        UserIdPrincipal(credentials.name)
+                    } else {
+                        null
+                    }
+                }
             }
         }
         install(ContentNegotiation) {
@@ -302,6 +340,54 @@ fun serverMain(dbName: String, port: Int) {
             }
         }
         routing {
+            /*
+
+              FIXME: commenting out until a solution for i18n is found.
+
+            get("/bank") {
+                val ret = renderTemplate(
+                    "login.html",
+                    mapOf("csrf_token" to "todo", )
+                )
+                call.respondText(ret)
+                return@get
+            } */
+
+            post("/register") {
+                // how to read form-POSTed values?
+                val username = "fixme"
+                val password = "fixme"
+                val superuser = false
+
+                transaction {
+                    // check if username is taken.
+                    var maybeUser = SandboxUserEntity.find {
+                        SandboxUsersTable.username eq username
+                    }.firstOrNull()
+                    // Will be converted to a HTML response.
+                    if (maybeUser != null) throw SandboxError(
+                        HttpStatusCode.Conflict, "Username not available"
+                    )
+
+                    // username is valid.  Register the user + new bank 
account.
+                    SandboxUserEntity.new {
+                        this.username = username
+                        passwordHash = CryptoUtil.hashpw(password)
+                        this.superuser = superuser
+                        bankAccount = BankAccountEntity.new {
+                            iban = "fixme"
+                            bic = "fixme"
+                            name = "fixme"
+                            label = "fixme"
+                            currency = "fixme"
+                        }
+                    }
+                }
+
+                call.respondText("User $username created")
+                return@post
+            }
+
             get("/jinja-test") {
                 val template = Resources.toString(
                     Resources.getResource("templates/hello.html"),
@@ -313,6 +399,15 @@ fun serverMain(dbName: String, port: Int) {
                 return@get
             }
 
+            authenticate("auth-form") {
+                get("/profile") {
+                    val userSession = call.principal<UserIdPrincipal>()
+                    println("Welcoming ${userSession?.name}")
+                    call.respond(object {})
+                    return@get
+                }
+            }
+
             static("/static") {
                 /**
                  * Here Sandbox will serve the CSS files.
@@ -400,13 +495,12 @@ fun serverMain(dbName: String, port: Int) {
             post("/admin/ebics/bank-accounts") {
                 val body = call.receiveJson<BankAccountRequest>()
                 transaction {
-                    val subscriber = getEbicsSubscriberFromDetails(
+                    var subscriber = getEbicsSubscriberFromDetails(
                         body.subscriber.userID,
                         body.subscriber.partnerID,
                         body.subscriber.hostID
                     )
-                    BankAccountEntity.new {
-                        this.subscriber = subscriber
+                    subscriber.bankAccount = BankAccountEntity.new {
                         iban = body.iban
                         bic = body.bic
                         name = body.name
@@ -600,6 +694,17 @@ fun serverMain(dbName: String, port: Int) {
             }
         }
     }
+    val configs = transaction {
+        SandboxConfigEntity.all().firstOrNull()
+    }
+    if (configs == null) {
+        logger.error("""
+            Sandbox cannot run without at least one configuration.
+            See "libeufin-sandbox config --help"
+        """.trimIndent()
+        )
+        exitProcess(1)
+    }
     logger.info("LibEuFin Sandbox running on port $port")
     try {
         server.start(wait = true)

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

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