gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated (d40f0c4 -> 4dbd3ae)


From: gnunet
Subject: [libeufin] branch master updated (d40f0c4 -> 4dbd3ae)
Date: Tue, 19 May 2020 10:46:14 +0200

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

dold pushed a change to branch master
in repository libeufin.

    from d40f0c4  sandbox db test
     new 77b6f70  remove test case stub
     new 4dbd3ae  CLI for superuser management, abstract PW hashing algo

The 2 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:
 nexus/build.gradle                                 |  6 +++
 nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt    |  5 ++-
 .../src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 24 +++++++-----
 nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt  | 45 +++++++++++++++++++++-
 .../kotlin/tech/libeufin/nexus/MainDeprecated.kt   |  2 +-
 nexus/src/test/kotlin/authentication.kt            | 24 ++----------
 util/src/main/kotlin/CryptoUtil.kt                 | 19 +++++++++
 util/src/test/kotlin/CryptoUtilTest.kt             |  6 +++
 util/src/test/kotlin/XmlUtilTest.kt                |  5 ---
 9 files changed, 96 insertions(+), 40 deletions(-)

diff --git a/nexus/build.gradle b/nexus/build.gradle
index ff58a78..2c6f3b2 100644
--- a/nexus/build.gradle
+++ b/nexus/build.gradle
@@ -68,6 +68,8 @@ dependencies {
     implementation 'org.apache.santuario:xmlsec:2.1.4'
     implementation group: 'org.apache.commons', name: 'commons-compress', 
version: '1.20'
 
+    implementation("com.github.ajalt:clikt:2.7.0")
+
     testImplementation group: 'junit', name: 'junit', version: '4.12'
 }
 
@@ -85,4 +87,8 @@ jar {
     manifest {
         attributes "Main-Class": "tech.libeufin.nexus.MainKt"
     }
+}
+
+run {
+    standardInput = System.in
 }
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
index ec31dce..9c7e225 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -140,6 +140,7 @@ object PreparedPaymentsTable : IdTable<String>() {
     /** never really used, but it makes sure the user always exists  */
     val nexusUser = reference("nexusUser", NexusUsersTable)
 }
+
 class PreparedPaymentEntity(id: EntityID<String>) : Entity<String>(id) {
     companion object : EntityClass<String, 
PreparedPaymentEntity>(PreparedPaymentsTable)
     var paymentId by PreparedPaymentsTable.paymentId
@@ -209,12 +210,12 @@ class EbicsSubscriberEntity(id: EntityID<String>) : 
Entity<String>(id) {
 
 object NexusUsersTable : IdTable<String>() {
     override val id = varchar("id", ID_MAX_LENGTH).entityId().primaryKey()
-    val password = blob("password").nullable()
+    val passwordHash = text("password")
 }
 
 class NexusUserEntity(id: EntityID<String>) : Entity<String>(id) {
     companion object : EntityClass<String, NexusUserEntity>(NexusUsersTable)
-    var password by NexusUsersTable.password
+    var passwordHash by NexusUsersTable.passwordHash
 }
 
 object BankAccountMapsTable : IntIdTable() {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
index cd54e81..ac77efb 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
@@ -11,7 +11,6 @@ import tech.libeufin.util.Amount
 import tech.libeufin.util.CryptoUtil
 import tech.libeufin.util.EbicsClientSubscriberDetails
 import tech.libeufin.util.base64ToBytes
-import javax.sql.rowset.serial.SerialBlob
 import java.util.Random
 import tech.libeufin.util.ebics_h004.EbicsTypes
 import java.security.interfaces.RSAPublicKey
@@ -440,7 +439,7 @@ fun extractNexusUser(param: String?): NexusUserEntity {
  * and returns a pair made of username and hashed (sha256) password.  The 
hashed value
  * will then be compared with the one kept into the database.
  */
-fun extractUserAndHashedPassword(authorizationHeader: String): Pair<String, 
ByteArray> {
+fun extractUserAndHashedPassword(authorizationHeader: String): Pair<String, 
String> {
     logger.debug("Authenticating: $authorizationHeader")
     val (username, password) = try {
         val split = authorizationHeader.split(" ")
@@ -452,7 +451,7 @@ fun extractUserAndHashedPassword(authorizationHeader: 
String): Pair<String, Byte
             "invalid Authorization:-header received"
         )
     }
-    return Pair(username, CryptoUtil.hashStringSHA256(password))
+    return Pair(username, password)
 }
 
 /**
@@ -466,13 +465,20 @@ fun authenticateRequest(authorization: String?): String {
     val headerLine = if (authorization == null) throw NexusError(
         HttpStatusCode.BadRequest, "Authentication:-header line not found"
     ) else authorization
-    val subscriber = transaction {
-        val (user, pass) = extractUserAndHashedPassword(headerLine)
-        NexusUserEntity.find {
-            NexusUsersTable.id eq user and (NexusUsersTable.password eq 
SerialBlob(pass))
+    val nexusUserId = transaction {
+        val (username, password) = extractUserAndHashedPassword(headerLine)
+        val user = NexusUserEntity.find {
+            NexusUsersTable.id eq username
         }.firstOrNull()
-    } ?: throw NexusError(HttpStatusCode.Forbidden, "Wrong password")
-    return subscriber.id.value
+        if (user == null) {
+            throw NexusError(HttpStatusCode.Unauthorized, "Unknown user")
+        }
+        if (!CryptoUtil.checkpw(password, user.passwordHash)) {
+            throw NexusError(HttpStatusCode.Forbidden, "Wrong password")
+        }
+        return@transaction user.id.value
+    }
+    return nexusUserId
 }
 
 fun authenticateAdminRequest(authorization: String?): String {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index 15b4a8b..ec2080e 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -19,6 +19,11 @@
 
 package tech.libeufin.nexus
 
+import com.github.ajalt.clikt.core.CliktCommand
+import com.github.ajalt.clikt.core.subcommands
+import com.github.ajalt.clikt.parameters.arguments.argument
+import com.github.ajalt.clikt.parameters.options.option
+import com.github.ajalt.clikt.parameters.options.prompt
 import com.google.gson.Gson
 import com.google.gson.JsonObject
 import io.ktor.application.ApplicationCallPipeline
@@ -52,6 +57,7 @@ import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import org.slf4j.event.Level
 import tech.libeufin.util.*
+import tech.libeufin.util.CryptoUtil.hashpw
 import tech.libeufin.util.ebics_h004.HTDResponseOrderData
 import java.text.DateFormat
 import java.util.zip.InflaterInputStream
@@ -157,9 +163,44 @@ suspend fun handleEbicsSendMSG(
     return response
 }
 
+class NexusCommand: CliktCommand() {
+    override fun run() = Unit
+}
+
+class Serve: CliktCommand("Run nexus HTTP server") {
+    override fun run() {
+        serverMain()
+    }
+}
+
+class Superuser: CliktCommand("Add superuser or change pw") {
+    val username by argument()
+    val password by option().prompt(requireConfirmation = true, hideInput = 
true)
+    override fun run() {
+        dbCreateTables()
+        transaction {
+            val hashedPw = hashpw(password)
+            val user = NexusUserEntity.findById(username)
+            if (user == null) {
+                NexusUserEntity.new(username) {
+                    this.passwordHash = hashedPw
+                }
+            } else {
+                user.passwordHash = hashedPw
+            }
+        }
+    }
+}
+
+fun main(args: Array<String>) {
+    NexusCommand()
+        .subcommands(Serve(), Superuser())
+        .main(args)
+}
+
 @ExperimentalIoApi
 @KtorExperimentalAPI
-fun main() {
+fun serverMain() {
     dbCreateTables()
     val client = HttpClient() {
         expectSuccess = false // this way, it does not throw exceptions on != 
200 responses.
@@ -249,7 +290,7 @@ fun main() {
                 )
                 transaction {
                     NexusUserEntity.new(body.username) {
-                        password = 
SerialBlob(CryptoUtil.hashStringSHA256(body.password))
+                        passwordHash = hashpw(body.password)
                     }
                 }
                 call.respondText(
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/MainDeprecated.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/MainDeprecated.kt
index d730001..b65acd3 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/MainDeprecated.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/MainDeprecated.kt
@@ -206,7 +206,7 @@ fun main() {
                 val body = call.receive<NexusUserRequest>()
                 transaction {
                     NexusUserEntity.new(id = newUserId) {
-                        password = if (body.password != null) {
+                        passwordHash = if (body.password != null) {
                             
SerialBlob(CryptoUtil.hashStringSHA256(body.password))
                         } else {
                             logger.debug("No password set for $newUserId")
diff --git a/nexus/src/test/kotlin/authentication.kt 
b/nexus/src/test/kotlin/authentication.kt
index 7cbb36d..602f990 100644
--- a/nexus/src/test/kotlin/authentication.kt
+++ b/nexus/src/test/kotlin/authentication.kt
@@ -1,37 +1,19 @@
 package tech.libeufin.nexus
 
-import org.apache.commons.compress.utils.IOUtils
 import org.jetbrains.exposed.sql.Database
 import org.jetbrains.exposed.sql.SchemaUtils
-import org.jetbrains.exposed.sql.and
 import org.jetbrains.exposed.sql.transactions.transaction
 import org.junit.Test
+import junit.framework.TestCase.assertEquals
 import tech.libeufin.util.CryptoUtil
 import javax.sql.rowset.serial.SerialBlob
 
 class AuthenticationTest {
-    @Test
-    fun dbInvolvingTest() {
-        Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", driver = 
"org.h2.Driver")
-        transaction {
-            SchemaUtils.create(NexusUsersTable)
-            NexusUserEntity.new(id = "username") {
-                password = SerialBlob(CryptoUtil.hashStringSHA256("password"))
-            }
-            // base64 of "username:password" == "dXNlcm5hbWU6cGFzc3dvcmQ="
-            val hashedPass= extractUserAndHashedPassword(
-                "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
-            ).second
-            val row = NexusUserEntity.findById("username")
-            assert(row?.password == SerialBlob(hashedPass))
-        }
-    }
-
     @Test
     fun basicAuthHeaderTest() {
-        val hashedPass = extractUserAndHashedPassword(
+        val pass = extractUserAndHashedPassword(
             "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
         ).second
-        
assert(CryptoUtil.hashStringSHA256("password").contentEquals(hashedPass))
+        assertEquals("password", pass);
     }
 }
\ No newline at end of file
diff --git a/util/src/main/kotlin/CryptoUtil.kt 
b/util/src/main/kotlin/CryptoUtil.kt
index fe578a3..f8711c0 100644
--- a/util/src/main/kotlin/CryptoUtil.kt
+++ b/util/src/main/kotlin/CryptoUtil.kt
@@ -300,4 +300,23 @@ object CryptoUtil {
     fun hashStringSHA256(input: String): ByteArray {
         return 
MessageDigest.getInstance("SHA-256").digest(input.toByteArray(Charsets.UTF_8))
     }
+
+    fun hashpw(pw: String): String {
+        val pwh = bytesToBase64(CryptoUtil.hashStringSHA256(pw))
+        return "sha256\$$pwh"
+    }
+
+    fun checkpw(pw: String, storedPwHash: String): Boolean {
+        val idx = storedPwHash.indexOf("\$")
+        if (idx <= 0) {
+            throw Exception("bad password hash")
+        }
+        val algo = storedPwHash.substring(0, idx)
+        if (algo != "sha256") {
+            throw Exception("unsupported hash algo")
+        }
+        val rest = storedPwHash.substring(idx + 1)
+        val pwh = bytesToBase64(CryptoUtil.hashStringSHA256(pw))
+        return pwh == rest
+    }
 }
diff --git a/util/src/test/kotlin/CryptoUtilTest.kt 
b/util/src/test/kotlin/CryptoUtilTest.kt
index da545bf..0bbabc9 100644
--- a/util/src/test/kotlin/CryptoUtilTest.kt
+++ b/util/src/test/kotlin/CryptoUtilTest.kt
@@ -160,5 +160,11 @@ class CryptoUtilTest {
         val expectedEncoding = "C9P6YRG"
         
assert(Base32Crockford.decode(expectedEncoding).toString(Charsets.UTF_8) == 
"blob")
     }
+
+    @Test
+    fun passwordHashing() {
+        val x = CryptoUtil.hashpw("myinsecurepw")
+        assertTrue(CryptoUtil.checkpw("myinsecurepw", x))
+    }
 }
 
diff --git a/util/src/test/kotlin/XmlUtilTest.kt 
b/util/src/test/kotlin/XmlUtilTest.kt
index 80d7f8e..57394b8 100644
--- a/util/src/test/kotlin/XmlUtilTest.kt
+++ b/util/src/test/kotlin/XmlUtilTest.kt
@@ -176,9 +176,4 @@ class XmlUtilTest {
         val key = CryptoUtil.loadRsaPublicKey(keyBytes)
         assertTrue(XMLUtil.verifyEbicsDocument(doc, key))
     }
-
-    @Test
-    fun marshalling() {
-        XMLUtil.convertJaxbToString(Any())
-    }
 }
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

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