aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine A <>2024-01-24 11:59:25 +0100
committerAntoine A <>2024-01-24 11:59:25 +0100
commit7edfaa739f019e89e38e078cee08f33bfab0272b (patch)
tree23dc41ec1c0028dedec5987ec08f66cad33b47aa
parent5cc79b332d9595500290523e4c60cc877b0a237f (diff)
downloadlibeufin-7edfaa739f019e89e38e078cee08f33bfab0272b.tar.gz
libeufin-7edfaa739f019e89e38e078cee08f33bfab0272b.tar.bz2
libeufin-7edfaa739f019e89e38e078cee08f33bfab0272b.zip
Improve nexus keys files error logs
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt76
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt219
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt144
-rw-r--r--nexus/src/test/kotlin/CliTest.kt16
-rw-r--r--nexus/src/test/kotlin/Keys.kt19
5 files changed, 247 insertions, 227 deletions
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
index 11fe512c..dc742111 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
@@ -32,51 +32,10 @@ import tech.libeufin.common.*
import tech.libeufin.ebics.*
import tech.libeufin.ebics.ebics_h004.HTDResponseOrderData
import java.time.Instant
-import kotlin.reflect.typeOf
-import java.nio.file.Files
-import java.nio.file.StandardCopyOption
+import java.nio.file.*
import kotlin.io.path.*
/**
- * Writes the JSON content to disk. Used when we create or update
- * keys and other metadata JSON content to disk. WARNING: this overrides
- * silently what's found under the given location!
- *
- * @param obj the class representing the JSON content to store to disk.
- * @param path where to store `obj`
- */
-inline fun <reified T> syncJsonToDisk(obj: T, path: String) {
- val content = try {
- myJson.encodeToString(obj)
- } catch (e: Exception) {
- throw Exception("Could not encode the input '${typeOf<T>()}' to JSON", e)
- }
- try {
- // Write to temp file then rename to enable atomicity when possible
- val path = Path(path).absolute()
- val tmp = Files.createTempFile(path.parent, "tmp_", "_${path.fileName}")
- tmp.writeText(content)
- tmp.moveTo(path, StandardCopyOption.REPLACE_EXISTING);
- } catch (e: Exception) {
- throw Exception("Could not write JSON content at $path", e)
- }
-}
-
-/**
- * Generates new client private keys.
- *
- * @return [ClientPrivateKeysFile]
- */
-fun generateNewKeys(): ClientPrivateKeysFile =
- ClientPrivateKeysFile(
- authentication_private_key = CryptoUtil.generateRsaKeyPair(2048).private,
- encryption_private_key = CryptoUtil.generateRsaKeyPair(2048).private,
- signature_private_key = CryptoUtil.generateRsaKeyPair(2048).private,
- submitted_hia = false,
- submitted_ini = false
- )
-
-/**
* Obtains the client private keys, regardless of them being
* created for the first time, or read from an existing file
* on disk.
@@ -84,20 +43,15 @@ fun generateNewKeys(): ClientPrivateKeysFile =
* @param path path to the file that contains the keys.
* @return current or new client keys
*/
-private fun preparePrivateKeys(path: String): ClientPrivateKeysFile {
+private fun loadOrGenerateClientKeys(path: String): ClientPrivateKeysFile {
// If exists load from disk
- val current = loadPrivateKeysFromDisk(path)
+ val current = loadClientKeys(path)
if (current != null) return current
// Else create new keys
- try {
- val newKeys = generateNewKeys()
- syncJsonToDisk(newKeys, path)
- logger.info("New client keys created at: $path")
- return newKeys
- } catch (e: Exception) {
- throw Exception("Could not create client keys at $path", e)
- // TODO Better log
- }
+ val newKeys = generateNewKeys()
+ persistClientKeys(newKeys, path)
+ logger.info("New client private keys created at '$path'")
+ return newKeys
}
/**
@@ -159,7 +113,7 @@ private fun handleHpbResponse(
val hpbObj = try {
parseEbicsHpbOrder(hpbBytes)
} catch (e: Exception) {
- throw Exception("HPB response content seems invalid: e")
+ throw Exception("HPB response content seems invalid", e)
}
val encPub = try {
CryptoUtil.loadRsaPublicKey(hpbObj.encryptionPubKey.encoded)
@@ -176,11 +130,7 @@ private fun handleHpbResponse(
bank_encryption_public_key = encPub,
accepted = false
)
- try {
- syncJsonToDisk(json, cfg.bankPublicKeysFilename)
- } catch (e: Exception) {
- throw Exception("Failed to persist the bank keys to disk", e)
- }
+ persistBankKeys(json, cfg.bankPublicKeysFilename)
}
/**
@@ -201,7 +151,7 @@ suspend fun doKeysRequestAndUpdateState(
client: HttpClient,
orderType: KeysOrderType
) {
- logger.debug("Doing key request ${orderType.name}")
+ logger.info("Doing key request ${orderType.name}")
val req = when(orderType) {
KeysOrderType.INI -> generateIniMessage(cfg, privs)
KeysOrderType.HIA -> generateHiaMessage(cfg, privs)
@@ -228,7 +178,7 @@ suspend fun doKeysRequestAndUpdateState(
KeysOrderType.HPB -> return handleHpbResponse(cfg, ebics)
}
try {
- syncJsonToDisk(privs, cfg.clientPrivateKeysFilename)
+ persistClientKeys(privs, cfg.clientPrivateKeysFilename)
} catch (e: Exception) {
throw Exception("Could not update the ${orderType.name} state on disk", e)
}
@@ -286,7 +236,7 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") {
override fun run() = cliCmd(logger, common.log) {
val cfg = extractEbicsConfig(common.config)
// Config is sane. Go (maybe) making the private keys.
- val clientKeys = preparePrivateKeys(cfg.clientPrivateKeysFilename)
+ val clientKeys = loadOrGenerateClientKeys(cfg.clientPrivateKeysFilename)
val httpClient = HttpClient()
// Privs exist. Upload their pubs
val keysNotSub = !clientKeys.submitted_ini || !clientKeys.submitted_hia
@@ -330,7 +280,7 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") {
throw Exception("Cannot successfully finish the setup without accepting the bank keys.")
}
try {
- syncJsonToDisk(bankKeysMaybe, cfg.bankPublicKeysFilename)
+ persistBankKeys(bankKeysMaybe, cfg.bankPublicKeysFilename)
} catch (e: Exception) {
throw Exception("Could not set bank keys as accepted on disk.", e)
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt
new file mode 100644
index 00000000..14b88b42
--- /dev/null
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt
@@ -0,0 +1,219 @@
+/*
+ * This file is part of LibEuFin.
+ * Copyright (C) 2023 Stanisci and Dold.
+
+ * LibEuFin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3, or
+ * (at your option) any later version.
+
+ * LibEuFin 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 Affero General
+ * Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public
+ * License along with LibEuFin; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>
+ */
+
+package tech.libeufin.nexus
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.modules.SerializersModule
+import java.security.interfaces.RSAPrivateCrtKey
+import java.security.interfaces.RSAPublicKey
+import tech.libeufin.common.*
+import java.nio.file.*
+import kotlin.io.path.*
+import java.io.File
+import kotlin.reflect.typeOf
+
+val JSON = Json {
+ this.serializersModule = SerializersModule {
+ contextual(RSAPrivateCrtKey::class) { RSAPrivateCrtKeySerializer }
+ contextual(RSAPublicKey::class) { RSAPublicKeySerializer }
+ }
+}
+
+/**
+ * Converts base 32 representation of RSA public keys and vice versa.
+ */
+object RSAPublicKeySerializer : KSerializer<RSAPublicKey> {
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("RSAPublicKey", PrimitiveKind.STRING)
+ override fun serialize(encoder: Encoder, value: RSAPublicKey) {
+ encoder.encodeString(Base32Crockford.encode(value.encoded))
+ }
+
+ // Caller must handle exceptions here.
+ override fun deserialize(decoder: Decoder): RSAPublicKey {
+ val fieldValue = decoder.decodeString()
+ val bytes = Base32Crockford.decode(fieldValue)
+ return CryptoUtil.loadRsaPublicKey(bytes)
+ }
+}
+
+/**
+ * Converts base 32 representation of RSA private keys and vice versa.
+ */
+object RSAPrivateCrtKeySerializer : KSerializer<RSAPrivateCrtKey> {
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("RSAPrivateCrtKey", PrimitiveKind.STRING)
+ override fun serialize(encoder: Encoder, value: RSAPrivateCrtKey) {
+ encoder.encodeString(Base32Crockford.encode(value.encoded))
+ }
+
+ // Caller must handle exceptions here.
+ override fun deserialize(decoder: Decoder): RSAPrivateCrtKey {
+ val fieldValue = decoder.decodeString()
+ val bytes = Base32Crockford.decode(fieldValue)
+ return CryptoUtil.loadRsaPrivateKey(bytes)
+ }
+}
+
+/**
+ * Structure of the JSON file that contains the client
+ * private keys on disk.
+ */
+@Serializable
+data class ClientPrivateKeysFile(
+ @Contextual val signature_private_key: RSAPrivateCrtKey,
+ @Contextual val encryption_private_key: RSAPrivateCrtKey,
+ @Contextual val authentication_private_key: RSAPrivateCrtKey,
+ var submitted_ini: Boolean,
+ var submitted_hia: Boolean
+)
+
+/**
+ * Structure of the JSON file that contains the bank
+ * public keys on disk.
+ */
+@Serializable
+data class BankPublicKeysFile(
+ @Contextual val bank_encryption_public_key: RSAPublicKey,
+ @Contextual val bank_authentication_public_key: RSAPublicKey,
+ var accepted: Boolean
+)
+
+/**
+ * Generates new client private keys.
+ *
+ * @return [ClientPrivateKeysFile]
+ */
+fun generateNewKeys(): ClientPrivateKeysFile =
+ ClientPrivateKeysFile(
+ authentication_private_key = CryptoUtil.generateRsaKeyPair(2048).private,
+ encryption_private_key = CryptoUtil.generateRsaKeyPair(2048).private,
+ signature_private_key = CryptoUtil.generateRsaKeyPair(2048).private,
+ submitted_hia = false,
+ submitted_ini = false
+ )
+
+private inline fun <reified T> persistJsonFile(obj: T, path: String, name: String) {
+ val content = try {
+ JSON.encodeToString(obj)
+ } catch (e: Exception) {
+ throw Exception("Could not encode '${typeOf<T>()}' to JSON", e)
+ }
+ val (path, parent) = try {
+ val path = Path(path)
+ Pair(path, path.parent ?: path.absolute().parent)
+ } catch (e: Exception) {
+ throw Exception("Could not write $name at '$path'", e)
+ }
+ try {
+ // Write to temp file then rename to enable atomicity when possible
+ val tmp = Files.createTempFile(parent, "tmp_", "_${path.fileName}")
+ tmp.writeText(content)
+ tmp.moveTo(path, StandardCopyOption.REPLACE_EXISTING);
+ } catch (e: Exception) {
+ when {
+ !parent.toFile().canWrite() -> throw Exception("Could not write $name at '$path': permission denied on '$parent'")
+ !path.toFile().canWrite() -> throw Exception("Could not write $name at '$path': permission denied")
+ else -> throw Exception("Could not write $name at '$path'", e)
+ }
+ throw Exception("Could not write $name at '$path'", e)
+ }
+}
+
+/**
+ * Persist the bank keys file to disk
+ *
+ * @param location the keys file location
+ */
+fun persistBankKeys(keys: BankPublicKeysFile, location: String) = persistJsonFile(keys, location, "bank public keys")
+
+/**
+ * Persist the client keys file to disk
+ *
+ * @param location the keys file location
+ */
+fun persistClientKeys(keys: ClientPrivateKeysFile, location: String) = persistJsonFile(keys, location, "client private keys")
+
+
+private inline fun <reified T> loadJsonFile(path: String, name: String): T? {
+ val content = try {
+ val path = Path(path)
+ path.readText()
+ } catch (e: Exception) {
+ when {
+ e is NoSuchFileException -> return null
+ e is AccessDeniedException -> throw Exception("Could not read $name at '$path': permission denied")
+ else -> throw Exception("Could not read $name at '$path'", e)
+ }
+ }
+ return try {
+ JSON.decodeFromString(content)
+ } catch (e: Exception) {
+ throw Exception("Could not decode $name at '$path'", e)
+ }
+}
+
+/**
+ * Load the bank keys file from disk.
+ *
+ * @param location the keys file location.
+ * @return the internal JSON representation of the keys file,
+ * or null if the file does not exist
+ */
+fun loadBankKeys(location: String): BankPublicKeysFile? = loadJsonFile(location, "bank public keys")
+
+/**
+ * Load the client keys file from disk.
+ *
+ * @param location the keys file location.
+ * @return the internal JSON representation of the keys file,
+ * or null if the file does not exist
+ */
+fun loadClientKeys(location: String): ClientPrivateKeysFile? = loadJsonFile(location, "client private keys")
+
+/**
+ * Load client and bank keys from disk.
+ * Checks that the keying process has been fully completed.
+ *
+ * Helps to fail before starting to talk EBICS to the bank.
+ *
+ * @param cfg configuration handle.
+ * @return both client and bank keys
+ */
+fun expectFullKeys(
+ cfg: EbicsSetupConfig
+): Pair<ClientPrivateKeysFile, BankPublicKeysFile> {
+ val clientKeys = loadClientKeys(cfg.clientPrivateKeysFilename)
+ if (clientKeys == null) {
+ throw Exception("Missing client private keys file at '${cfg.clientPrivateKeysFilename}', run 'libeufin-nexus ebics-setup' first")
+ } else if (!clientKeys.submitted_ini || !clientKeys.submitted_hia) {
+ throw Exception("Unsubmitted client private keys, run 'libeufin-nexus ebics-setup' first")
+ }
+ val bankKeys = loadBankKeys(cfg.bankPublicKeysFilename)
+ if (bankKeys == null) {
+ throw Exception("Missing bank public keys at '${cfg.bankPublicKeysFilename}', run 'libeufin-nexus ebics-setup' first")
+ } else if (!bankKeys.accepted) {
+ throw Exception("Unaccepted bank public keys, run 'libeufin-nexus ebics-setup' until accepting the bank keys")
+ }
+ return Pair(clientKeys, bankKeys)
+} \ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index 607e39c1..4d27910a 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -29,33 +29,13 @@ import com.github.ajalt.clikt.core.subcommands
import com.github.ajalt.clikt.parameters.options.versionOption
import io.ktor.client.*
import io.ktor.util.*
-import kotlinx.serialization.Contextual
-import kotlinx.serialization.KSerializer
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-import java.io.File
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.descriptors.PrimitiveKind
-import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
-import kotlinx.serialization.descriptors.SerialDescriptor
-import kotlinx.serialization.encoding.Decoder
-import kotlinx.serialization.encoding.Encoder
-import kotlinx.serialization.json.Json
-import kotlinx.serialization.modules.SerializersModule
import tech.libeufin.nexus.ebics.*
import tech.libeufin.common.*
-import java.security.interfaces.RSAPrivateCrtKey
-import java.security.interfaces.RSAPublicKey
-import java.io.FileNotFoundException
val NEXUS_CONFIG_SOURCE = ConfigSource("libeufin", "libeufin-nexus", "libeufin-nexus")
val logger: Logger = LoggerFactory.getLogger("libeufin-nexus")
-val myJson = Json {
- this.serializersModule = SerializersModule {
- contextual(RSAPrivateCrtKey::class) { RSAPrivateCrtKeySerializer }
- contextual(RSAPublicKey::class) { RSAPublicKeySerializer }
- }
-}
/**
* Triple identifying one IBAN bank account.
@@ -210,130 +190,6 @@ class EbicsSetupConfig(val config: TalerConfig) {
}
/**
- * Converts base 32 representation of RSA public keys and vice versa.
- */
-object RSAPublicKeySerializer : KSerializer<RSAPublicKey> {
- override val descriptor: SerialDescriptor =
- PrimitiveSerialDescriptor("RSAPublicKey", PrimitiveKind.STRING)
- override fun serialize(encoder: Encoder, value: RSAPublicKey) {
- encoder.encodeString(Base32Crockford.encode(value.encoded))
- }
-
- // Caller must handle exceptions here.
- override fun deserialize(decoder: Decoder): RSAPublicKey {
- val fieldValue = decoder.decodeString()
- val bytes = Base32Crockford.decode(fieldValue)
- return CryptoUtil.loadRsaPublicKey(bytes)
- }
-}
-
-/**
- * Converts base 32 representation of RSA private keys and vice versa.
- */
-object RSAPrivateCrtKeySerializer : KSerializer<RSAPrivateCrtKey> {
- override val descriptor: SerialDescriptor =
- PrimitiveSerialDescriptor("RSAPrivateCrtKey", PrimitiveKind.STRING)
- override fun serialize(encoder: Encoder, value: RSAPrivateCrtKey) {
- encoder.encodeString(Base32Crockford.encode(value.encoded))
- }
-
- // Caller must handle exceptions here.
- override fun deserialize(decoder: Decoder): RSAPrivateCrtKey {
- val fieldValue = decoder.decodeString()
- val bytes = Base32Crockford.decode(fieldValue)
- return CryptoUtil.loadRsaPrivateKey(bytes)
- }
-}
-
-/**
- * Structure of the JSON file that contains the client
- * private keys on disk.
- */
-@Serializable
-data class ClientPrivateKeysFile(
- @Contextual val signature_private_key: RSAPrivateCrtKey,
- @Contextual val encryption_private_key: RSAPrivateCrtKey,
- @Contextual val authentication_private_key: RSAPrivateCrtKey,
- var submitted_ini: Boolean,
- var submitted_hia: Boolean
-)
-
-/**
- * Structure of the JSON file that contains the bank
- * public keys on disk.
- */
-@Serializable
-data class BankPublicKeysFile(
- @Contextual val bank_encryption_public_key: RSAPublicKey,
- @Contextual val bank_authentication_public_key: RSAPublicKey,
- var accepted: Boolean
-)
-
-/**
- * Load client and bank keys from disk.
- * Checks that the keying process has been fully completed.
- *
- * Helps to fail before starting to talk EBICS to the bank.
- *
- * @param cfg configuration handle.
- * @return both client and bank keys
- */
-fun expectFullKeys(
- cfg: EbicsSetupConfig
-): Pair<ClientPrivateKeysFile, BankPublicKeysFile> {
- val clientKeys = loadPrivateKeysFromDisk(cfg.clientPrivateKeysFilename)
- if (clientKeys == null) {
- throw Exception("Missing client private keys file at '${cfg.clientPrivateKeysFilename}', run 'libeufin-nexus ebics-setup' first")
- } else if (!clientKeys.submitted_ini || !clientKeys.submitted_hia) {
- throw Exception("Unsubmitted client private keys, run 'libeufin-nexus ebics-setup' first")
- }
- val bankKeys = loadBankKeys(cfg.bankPublicKeysFilename)
- if (bankKeys == null) {
- throw Exception("Missing bank public keys at '${cfg.bankPublicKeysFilename}', run 'libeufin-nexus ebics-setup' first")
- } else if (!bankKeys.accepted) {
- throw Exception("Unaccepted bank public keys, run 'libeufin-nexus ebics-setup' until accepting the bank keys")
- }
- return Pair(clientKeys, bankKeys)
-}
-
-private inline fun <reified T> loadJsonFile(path: String, name: String): T? {
- val file = File(path)
- val content = try {
- file.readText()
- } catch (e: Exception) {
- // FileNotFoundException can be thrown if the file exists, but is not accessible...
- when {
- !file.exists() -> return null
- !file.canRead() -> throw Exception("Could not read $name at '$path': permission denied")
- else -> throw Exception("Could not read $name at '$path'", e)
- }
- }
- return try {
- myJson.decodeFromString(content)
- } catch (e: Exception) {
- throw Exception("Could not decode $name at '$path'", e)
- }
-}
-
-/**
- * Load the bank keys file from disk.
- *
- * @param location the keys file location.
- * @return the internal JSON representation of the keys file,
- * or null if the file does not exist
- */
-fun loadBankKeys(location: String): BankPublicKeysFile? = loadJsonFile(location, "bank public keys")
-
-/**
- * Load the client keys file from disk.
- *
- * @param location the keys file location.
- * @return the internal JSON representation of the keys file,
- * or null if the file does not exist
- */
-fun loadPrivateKeysFromDisk(location: String): ClientPrivateKeysFile? = loadJsonFile(location, "client private keys")
-
-/**
* Abstracts the config loading
*
* @param configFile potentially NULL configuration file location.
diff --git a/nexus/src/test/kotlin/CliTest.kt b/nexus/src/test/kotlin/CliTest.kt
index a57088c7..e53db43e 100644
--- a/nexus/src/test/kotlin/CliTest.kt
+++ b/nexus/src/test/kotlin/CliTest.kt
@@ -52,8 +52,9 @@ class CliTest {
val cfg = loadConfig(conf)
val clientKeysPath = Path(cfg.requireString("nexus-ebics", "client_private_keys_file"))
val bankKeysPath = Path(cfg.requireString("nexus-ebics", "bank_public_keys_file"))
- clientKeysPath.parent?.createDirectories()
- bankKeysPath.parent?.createDirectories()
+ clientKeysPath.parent!!.createDirectories()
+ clientKeysPath.parent!!.toFile().setWritable(true)
+ bankKeysPath.parent!!.createDirectories()
// Missing client keys
clientKeysPath.deleteIfExists()
@@ -71,13 +72,13 @@ class CliTest {
nexusCmd.testErr("$cmd -c $conf", "Could not read client private keys at '$clientKeysPath': permission denied")
}
// Unfinished client
- syncJsonToDisk(generateNewKeys(), clientKeysPath.toString())
+ persistClientKeys(generateNewKeys(), clientKeysPath.toString())
for (cmd in cmds) {
nexusCmd.testErr("$cmd -c $conf", "Unsubmitted client private keys, run 'libeufin-nexus ebics-setup' first")
}
// Missing bank keys
- syncJsonToDisk(generateNewKeys().apply {
+ persistClientKeys(generateNewKeys().apply {
submitted_hia = true
submitted_ini = true
}, clientKeysPath.toString())
@@ -96,7 +97,7 @@ class CliTest {
nexusCmd.testErr("$cmd -c $conf", "Could not read bank public keys at '$bankKeysPath': permission denied")
}
// Unfinished bank
- syncJsonToDisk(BankPublicKeysFile(
+ persistBankKeys(BankPublicKeysFile(
bank_authentication_public_key = CryptoUtil.generateRsaKeyPair(2048).public,
bank_encryption_public_key = CryptoUtil.generateRsaKeyPair(2048).public,
accepted = false
@@ -104,5 +105,10 @@ class CliTest {
for (cmd in cmds) {
nexusCmd.testErr("$cmd -c $conf", "Unaccepted bank public keys, run 'libeufin-nexus ebics-setup' until accepting the bank keys")
}
+
+ // Missing permission
+ clientKeysPath.deleteIfExists()
+ clientKeysPath.parent!!.toFile().setWritable(false)
+ nexusCmd.testErr("ebics-setup -c $conf", "Could not write client private keys at '$clientKeysPath': permission denied on '${clientKeysPath.parent}'")
}
} \ No newline at end of file
diff --git a/nexus/src/test/kotlin/Keys.kt b/nexus/src/test/kotlin/Keys.kt
index 41b6b11f..ededc7a5 100644
--- a/nexus/src/test/kotlin/Keys.kt
+++ b/nexus/src/test/kotlin/Keys.kt
@@ -42,7 +42,7 @@ class PublicKeys {
bank_encryption_public_key = CryptoUtil.generateRsaKeyPair(2028).public
)
// storing them on disk.
- syncJsonToDisk(fileContent, "/tmp/nexus-tests-bank-keys.json")
+ persistBankKeys(fileContent, "/tmp/nexus-tests-bank-keys.json")
// loading them and check that values are the same.
val fromDisk = loadBankKeys("/tmp/nexus-tests-bank-keys.json")
assertNotNull(fromDisk)
@@ -64,17 +64,6 @@ class PrivateKeys {
f.delete()
}
- // Testing write failure due to insufficient permissions.
- @Test
- fun createWrongPermissions() {
- f.writeText("won't be overridden")
- f.setReadOnly()
- try {
- syncJsonToDisk(clientKeys, f.path)
- throw Exception("Should have failed")
- } catch (e: Exception) { }
- }
-
/**
* Tests whether loading keys from disk yields the same
* values that were stored to the file.
@@ -82,8 +71,8 @@ class PrivateKeys {
@Test
fun load() {
assertFalse(f.exists())
- syncJsonToDisk(clientKeys, f.path) // Artificially storing this to the file.
- val fromDisk = loadPrivateKeysFromDisk(f.path) // loading it via the tested routine.
+ persistClientKeys(clientKeys, f.path) // Artificially storing this to the file.
+ val fromDisk = loadClientKeys(f.path) // loading it via the tested routine.
assertNotNull(fromDisk)
// Checking the values from disk match the initial object.
assertTrue {
@@ -98,6 +87,6 @@ class PrivateKeys {
// Testing failure on file not found.
@Test
fun loadNotFound() {
- assertNull(loadPrivateKeysFromDisk("/tmp/highly-unlikely-to-be-found.json"))
+ assertNull(loadClientKeys("/tmp/highly-unlikely-to-be-found.json"))
}
} \ No newline at end of file