diff --git a/src/dorkbox/network/Configuration.kt b/src/dorkbox/network/Configuration.kt index 13aa5090..cf63b84b 100644 --- a/src/dorkbox/network/Configuration.kt +++ b/src/dorkbox/network/Configuration.kt @@ -24,10 +24,8 @@ import dorkbox.network.aeron.CoroutineSleepingMillisIdleStrategy import dorkbox.network.exceptions.ClientException import dorkbox.network.exceptions.ServerException import dorkbox.network.serialization.Serialization -import dorkbox.network.storage.PropertyStore import dorkbox.network.storage.SettingsStore import dorkbox.os.OS -import dorkbox.util.storage.StorageBuilder import dorkbox.util.storage.StorageSystem import io.aeron.driver.Configuration import io.aeron.driver.MediaDriver @@ -226,18 +224,11 @@ open class Configuration { } /** - * Allows the end user to change how endpoint settings are stored. For example, a custom database instead of the default. + * Allows the end user to change how endpoint settings are stored. + * + * For example, a custom database instead of the default, in-memory storage. Another built-in option is StorageSystem.Disk() */ - var settingsStore: SettingsStore = PropertyStore() - set(value) { - require(context == null) { errorMessage } - field = value - } - - /** - * Specify the type of storage used for the endpoint settings , the options are Disk and Memory - */ - var settingsStorageSystem: StorageBuilder = StorageSystem.Memory() + var settingsStore: SettingsStore = SettingsStore(StorageSystem.Memory()) set(value) { require(context == null) { errorMessage } field = value diff --git a/src/dorkbox/network/connection/EndPoint.kt b/src/dorkbox/network/connection/EndPoint.kt index e7679125..e506b8e2 100644 --- a/src/dorkbox/network/connection/EndPoint.kt +++ b/src/dorkbox/network/connection/EndPoint.kt @@ -34,6 +34,7 @@ import dorkbox.network.serialization.Serialization import dorkbox.network.storage.SettingsStore import dorkbox.util.NamedThreadFactory import dorkbox.util.exceptions.SecurityException +import dorkbox.util.storage.StorageSystem import io.aeron.Aeron import io.aeron.Publication import io.aeron.driver.MediaDriver @@ -124,7 +125,13 @@ internal constructor(val type: Class<*>, internal val config: Configuration) : A // we have to be able to specify WHAT property store we want to use, since it can change! settingsStore = config.settingsStore - settingsStore.init(serialization, config.settingsStorageSystem.build()) + settingsStore.init() + + when (val builder = settingsStore.builder) { + is StorageSystem.DiskBuilder -> logger.info("Disk storage system initialized at: '${builder.file}'") + is StorageSystem.MemoryBuilder -> logger.info("Memory storage system initialized") + else -> logger.info("${builder::class.java.simpleName} storage system initialized") + } crypto = CryptoManagement(logger, settingsStore, type, config) diff --git a/src/dorkbox/network/serialization/Serialization.kt b/src/dorkbox/network/serialization/Serialization.kt index 3c2fa230..dbbb9d3d 100644 --- a/src/dorkbox/network/serialization/Serialization.kt +++ b/src/dorkbox/network/serialization/Serialization.kt @@ -408,7 +408,7 @@ open class Serialization(private val references: Boolean = true, private val fac return false } - settingsStore.getSerializationTypes().forEach { + settingsStore.getSerializationRegistrations().forEach { classesToRegister.add(ClassRegistration3(it)) } diff --git a/src/dorkbox/network/storage/NullSettingsStore.kt b/src/dorkbox/network/storage/NullSettingsStore.kt deleted file mode 100644 index 6b2b9092..00000000 --- a/src/dorkbox/network/storage/NullSettingsStore.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2020 dorkbox, llc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dorkbox.network.storage - -import dorkbox.network.serialization.Serialization -import dorkbox.util.exceptions.SecurityException -import dorkbox.util.storage.Storage -import java.net.InetAddress -import java.security.SecureRandom - -class NullSettingsStore : SettingsStore() { - private var serverSalt: ByteArray? = null - - override fun init(serializationManager: Serialization, storage: Storage) {} - - @Throws(SecurityException::class) - override fun getPrivateKey(): ByteArray { - TODO("not impl") - } - - @Throws(SecurityException::class) - override fun savePrivateKey(serverPrivateKey: ByteArray) { - } - - @Throws(SecurityException::class) - override fun getPublicKey(): ByteArray { - TODO("not impl") - } - - @Throws(SecurityException::class) - override fun savePublicKey(serverPublicKey: ByteArray) { - } - - override fun getSalt(): ByteArray { - if (serverSalt == null) { - val secureRandom = SecureRandom() - serverSalt = ByteArray(32) - secureRandom.nextBytes(serverSalt) - } - - return serverSalt!! - } - - @Throws(SecurityException::class) - override fun getRegisteredServerKey(hostAddress: InetAddress): ByteArray { - TODO("not impl") - } - - @Throws(SecurityException::class) - override fun addRegisteredServerKey(hostAddress: InetAddress, publicKey: ByteArray) { - TODO("not impl") - } - - @Throws(SecurityException::class) - override fun removeRegisteredServerKey(hostAddress: InetAddress): Boolean { - return true - } - - override fun close() {} -} diff --git a/src/dorkbox/network/storage/PropertyStore.kt b/src/dorkbox/network/storage/PropertyStore.kt deleted file mode 100644 index b8df9ff0..00000000 --- a/src/dorkbox/network/storage/PropertyStore.kt +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2020 dorkbox, llc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dorkbox.network.storage - -import dorkbox.netUtil.IPv4 -import dorkbox.netUtil.IPv6 -import dorkbox.network.connection.CryptoManagement -import dorkbox.network.serialization.Serialization -import dorkbox.util.storage.Storage -import org.agrona.collections.Object2NullableObjectHashMap -import java.net.InetAddress -import java.security.SecureRandom - -/** - * The property store is the DEFAULT type of store for the network stack. - */ -class PropertyStore : SettingsStore() { - private lateinit var storage: Storage - private lateinit var servers: Object2NullableObjectHashMap - - /** - * Address 0.0.0.0 or ::0 may be used as a source address for this host on this network. - * - * Because we assigned BOTH to the same thing, it doesn't matter which one we use - */ - private val ipv4Host = IPv4.WILDCARD - private val ipv6Host = IPv6.WILDCARD - - /** - * Method of preference for creating/getting this connection store. - * - * @param serializationManager this is the serialization used for saving objects into the storage database - */ - override fun init(serializationManager: Serialization, storage: Storage) { - this.storage = storage - servers = this.storage.get(DB_Server.STORAGE_KEY, Object2NullableObjectHashMap()) - - // this will always be null and is here to help people that copy/paste code - var localServer = servers[ipv4Host] - if (localServer == null) { - localServer = DB_Server() - servers[ipv4Host] = localServer - - // have to always specify what we are saving - this.storage.put(DB_Server.STORAGE_KEY, servers) - } - - if (servers[ipv6Host] == null) { - servers[ipv6Host] = localServer - - // have to always specify what we are saving - this.storage.put(DB_Server.STORAGE_KEY, servers) - } - } - - /** - * Simple, property based method to getting the private key of the server - */ - @Synchronized - override fun getPrivateKey(): ByteArray? { - checkAccess(CryptoManagement::class.java) - return servers[ipv4Host]!!.privateKey - } - - /** - * Simple, property based method for saving the private key of the server - */ - @Synchronized - override fun savePrivateKey(serverPrivateKey: ByteArray) { - checkAccess(CryptoManagement::class.java) - servers[ipv4Host]!!.privateKey = serverPrivateKey - - // have to always specify what we are saving - storage.put(DB_Server.STORAGE_KEY, servers) - } - - /** - * Simple, property based method to getting the public key of the server - */ - @Synchronized - override fun getPublicKey(): ByteArray? { - return servers[ipv4Host]!!.publicKey - } - - /** - * Simple, property based method for saving the public key of the server - */ - @Synchronized - override fun savePublicKey(serverPublicKey: ByteArray) { - checkAccess(CryptoManagement::class.java) - servers[ipv4Host]!!.publicKey = serverPublicKey - - // have to always specify what we are saving - storage.put(DB_Server.STORAGE_KEY, servers) - } - - /** - * Simple, property based method to getting the server salt - */ - @Synchronized - override fun getSalt(): ByteArray { - val localServer = servers[ipv4Host] - var salt = localServer!!.salt - - // we don't care who gets the server salt - if (salt == null) { - val secureRandom = SecureRandom() - - // server salt is used to salt usernames and other various connection handshake parameters - val bytes = ByteArray(256) - secureRandom.nextBytes(bytes) - salt = bytes - localServer.salt = bytes - - // have to always specify what we are saving - storage.put(DB_Server.STORAGE_KEY, servers) - } - - return salt - } - - /** - * Simple, property based method to getting a connected computer by host IP address - */ - @Synchronized - override fun getRegisteredServerKey(hostAddress: InetAddress): ByteArray? { - return servers[hostAddress]?.publicKey - } - - /** - * Saves a connected computer by host IP address and public key - */ - @Synchronized - override fun addRegisteredServerKey(hostAddress: InetAddress, publicKey: ByteArray) { - // checkAccess(RegistrationWrapper.class); - var db_server = servers[hostAddress] - if (db_server == null) { - db_server = DB_Server() - } - - db_server.publicKey = publicKey - servers[hostAddress] = db_server - - // have to always specify what we are saving - storage.put(DB_Server.STORAGE_KEY, servers) - } - - /** - * Deletes a registered computer by host IP address - */ - @Synchronized - override fun removeRegisteredServerKey(hostAddress: InetAddress): Boolean { - // checkAccess(RegistrationWrapper.class); - val db_server = servers.remove(hostAddress) - - // have to always specify what we are saving - storage.put(DB_Server.STORAGE_KEY, servers) - - return db_server != null - } - - override fun close() { - storage.close() - } -} diff --git a/src/dorkbox/network/storage/SettingsStore.kt b/src/dorkbox/network/storage/SettingsStore.kt index 54020e7a..8bb529ab 100644 --- a/src/dorkbox/network/storage/SettingsStore.kt +++ b/src/dorkbox/network/storage/SettingsStore.kt @@ -15,89 +15,189 @@ */ package dorkbox.network.storage -import dorkbox.network.serialization.Serialization +import dorkbox.netUtil.IPv4 +import dorkbox.netUtil.IPv6 +import dorkbox.network.connection.CryptoManagement import dorkbox.util.bytes.ByteArrayWrapper import dorkbox.util.exceptions.SecurityException import dorkbox.util.storage.Storage +import dorkbox.util.storage.StorageBuilder +import org.agrona.collections.Object2NullableObjectHashMap import org.slf4j.LoggerFactory import java.net.InetAddress -import java.util.* +import java.security.SecureRandom /** * This class provides a way for the network stack to use the server's database, instead of a property file (which it uses when stand-alone) * - * * A static "create" method, with any number of parameters, is required to create this class (which is done via reflection) */ -abstract class SettingsStore : AutoCloseable { - /** - * Initialize the settingsStore with the provided serialization manager. - */ - abstract fun init(serializationManager: Serialization, storage: Storage) +open class SettingsStore(internal val builder: StorageBuilder) : AutoCloseable { + private lateinit var storage: Storage + private lateinit var servers: Object2NullableObjectHashMap - fun getSerializationTypes(): List> { - // make sure our custom types are registered - // only register if not ALREADY initialized, since we can initialize in the server and in the client. This creates problems if - // running inside the same JVM - return listOf(HashMap::class.java, - ByteArrayWrapper::class.java, - DB_Server::class.java - ) + /** + * Address 0.0.0.0 or ::0 may be used as a source address for this host on this network. + * + * Because we assigned BOTH to the same thing, it doesn't matter which one we use + */ + private val ipv4Host = IPv4.WILDCARD + private val ipv6Host = IPv6.WILDCARD + + /** + * Initialize using the provided serialization manager. + */ + fun init() { + this.storage = builder.build() + + servers = this.storage.get(DB_Server.STORAGE_KEY, Object2NullableObjectHashMap()) + + // this will always be null and is here to help people that copy/paste code + var localServer = servers[ipv4Host] + if (localServer == null) { + localServer = DB_Server() + servers[ipv4Host] = localServer + + // have to always specify what we are saving + this.storage.put(DB_Server.STORAGE_KEY, servers) + } + + if (servers[ipv6Host] == null) { + servers[ipv6Host] = localServer + + // have to always specify what we are saving + this.storage.put(DB_Server.STORAGE_KEY, servers) + } } /** - * Simple, property based method for saving the private key of the server + * Used to register the different serialization registrations for this store */ - @Throws(SecurityException::class) - abstract fun getPrivateKey(): ByteArray? + fun getSerializationRegistrations(): List> { + // make sure our custom types are registered + // only register if not ALREADY initialized, since we can initialize in the server and in the client. This creates problems if + // running inside the same JVM + return listOf(Object2NullableObjectHashMap::class.java, + ByteArrayWrapper::class.java, + DB_Server::class.java + ) + } /** - * Simple, property based method for saving the private key of the server + * @return the private key of the server */ - @Throws(SecurityException::class) - abstract fun savePrivateKey(serverPrivateKey: ByteArray) + @Synchronized + fun getPrivateKey(): ByteArray? { + checkAccess(CryptoManagement::class.java) + return servers[ipv4Host]!!.privateKey + } /** - * Simple, property based method to getting the public key of the server + * Saves the private key of the server */ - @Throws(SecurityException::class) - abstract fun getPublicKey(): ByteArray? + @Synchronized + fun savePrivateKey(serverPrivateKey: ByteArray) { + checkAccess(CryptoManagement::class.java) + servers[ipv4Host]!!.privateKey = serverPrivateKey + + // have to always specify what we are saving + storage.put(DB_Server.STORAGE_KEY, servers) + } /** - * Simple, property based method for saving the public key of the server + * @return the public key of the server */ - @Throws(SecurityException::class) - abstract fun savePublicKey(serverPublicKey: ByteArray) + @Synchronized + fun getPublicKey(): ByteArray? { + return servers[ipv4Host]!!.publicKey + } + + /** + * Saves the public key of the server + */ + @Synchronized + fun savePublicKey(serverPublicKey: ByteArray) { + checkAccess(CryptoManagement::class.java) + servers[ipv4Host]!!.publicKey = serverPublicKey + + // have to always specify what we are saving + storage.put(DB_Server.STORAGE_KEY, servers) + } /** * @return the server salt */ - abstract fun getSalt(): ByteArray + @Synchronized + fun getSalt(): ByteArray { + val localServer = servers[ipv4Host] + var salt = localServer!!.salt + + // we don't care who gets the server salt + if (salt == null) { + val secureRandom = SecureRandom() + + // server salt is used to salt usernames and other various connection handshake parameters + val bytes = ByteArray(256) + secureRandom.nextBytes(bytes) + salt = bytes + localServer.salt = bytes + + // have to always specify what we are saving + storage.put(DB_Server.STORAGE_KEY, servers) + } + + return salt + } /** * Gets a previously registered computer by host IP address */ - @Throws(SecurityException::class) - abstract fun getRegisteredServerKey(hostAddress: InetAddress): ByteArray? + @Synchronized + fun getRegisteredServerKey(hostAddress: InetAddress): ByteArray? { + return servers[hostAddress]?.publicKey + } /** - * Saves a registered computer by host IP address and public key + * Saves a connected computer by host IP address and public key */ - @Throws(SecurityException::class) - abstract fun addRegisteredServerKey(hostAddress: InetAddress, publicKey: ByteArray) + @Synchronized + fun addRegisteredServerKey(hostAddress: InetAddress, publicKey: ByteArray) { + // checkAccess(RegistrationWrapper.class); + var db_server = servers[hostAddress] + if (db_server == null) { + db_server = DB_Server() + } + + db_server.publicKey = publicKey + servers[hostAddress] = db_server + + // have to always specify what we are saving + storage.put(DB_Server.STORAGE_KEY, servers) + } /** * Deletes a registered computer by host IP address * * @return true if successful, false if there were problems (or it didn't exist) */ - @Throws(SecurityException::class) - abstract fun removeRegisteredServerKey(hostAddress: InetAddress): Boolean + @Synchronized + fun removeRegisteredServerKey(hostAddress: InetAddress): Boolean { + // checkAccess(RegistrationWrapper.class); + val db_server = servers.remove(hostAddress) + + // have to always specify what we are saving + storage.put(DB_Server.STORAGE_KEY, servers) + + return db_server != null + } + /** * Take the proper steps to close the storage system. */ - abstract override fun close() + override fun close() { + storage.close() + } /**