From 9f7d34625bceafbab42994c5a332871719b178f9 Mon Sep 17 00:00:00 2001 From: Matteo Battaglio Date: Mon, 29 Jul 2024 17:07:38 +0200 Subject: [PATCH] feat: add apiKey support and related tests --- app/src/main/assets/secrets.json.sample | 3 ++- .../io/sqlitecloud/sampleapp/MainActivity.kt | 5 +++-- .../java/io/sqlitecloud/sampleapp/Secrets.kt | 1 + .../java/io/sqlitecloud/ConnectionTest.kt | 22 +++++++++++++++++++ .../java/io/sqlitecloud/Secrets.kt | 1 + .../java/io/sqlitecloud/TestContext.kt | 16 ++++++++++++++ sqlitecloud/src/main/cpp/sqcloud.c | 2 ++ sqlitecloud/src/main/cpp/sqlitecloud.cpp | 6 ++--- .../main/java/io/sqlitecloud/SQLiteCloud.kt | 2 +- .../java/io/sqlitecloud/SQLiteCloudBridge.kt | 14 ++++++------ .../java/io/sqlitecloud/SQLiteCloudConfig.kt | 19 +++++++++++----- .../java/io/sqlitecloud/SQLiteCloudTest.kt | 13 +++++++++++ 12 files changed, 85 insertions(+), 19 deletions(-) diff --git a/app/src/main/assets/secrets.json.sample b/app/src/main/assets/secrets.json.sample index 7ecc750..c673421 100644 --- a/app/src/main/assets/secrets.json.sample +++ b/app/src/main/assets/secrets.json.sample @@ -1,5 +1,6 @@ { "hostname": "", "username": "", - "password": "" + "password": "", + "apiKey": "" } diff --git a/app/src/main/java/io/sqlitecloud/sampleapp/MainActivity.kt b/app/src/main/java/io/sqlitecloud/sampleapp/MainActivity.kt index 71d902b..0290c49 100644 --- a/app/src/main/java/io/sqlitecloud/sampleapp/MainActivity.kt +++ b/app/src/main/java/io/sqlitecloud/sampleapp/MainActivity.kt @@ -45,8 +45,9 @@ class MainActivity : ComponentActivity() { appContext = applicationContext, config = SQLiteCloudConfig( hostname = secrets?.hostname ?: "", - username = secrets?.username ?: "", - password = secrets?.password ?: "", + username = secrets?.username, + password = secrets?.password, + apiKey = secrets?.apiKey ), ) diff --git a/app/src/main/java/io/sqlitecloud/sampleapp/Secrets.kt b/app/src/main/java/io/sqlitecloud/sampleapp/Secrets.kt index 2e2c975..0260db6 100644 --- a/app/src/main/java/io/sqlitecloud/sampleapp/Secrets.kt +++ b/app/src/main/java/io/sqlitecloud/sampleapp/Secrets.kt @@ -7,4 +7,5 @@ data class Secrets( val hostname: String, val username: String, val password: String, + val apiKey: String, ) diff --git a/sqlitecloud/src/androidTest/java/io/sqlitecloud/ConnectionTest.kt b/sqlitecloud/src/androidTest/java/io/sqlitecloud/ConnectionTest.kt index 11c5da0..5e6d929 100644 --- a/sqlitecloud/src/androidTest/java/io/sqlitecloud/ConnectionTest.kt +++ b/sqlitecloud/src/androidTest/java/io/sqlitecloud/ConnectionTest.kt @@ -15,6 +15,7 @@ import org.junit.runner.RunWith class ConnectionTest { companion object { private val sql: SQLiteCloud = TestContext.sqliteCloud() + private val sqlApiKey: SQLiteCloud = TestContext.sqliteCloudApiKey() } @Test @@ -25,6 +26,14 @@ class ConnectionTest { assertFalse(sql.isConnected) } + @Test + fun connectWithValidApiKeyCredentialsSucceeds() = runBlocking { + sqlApiKey.connect() + assertTrue(sqlApiKey.isConnected) + sqlApiKey.disconnect() + assertFalse(sqlApiKey.isConnected) + } + @Test fun connectWithInvalidCredentialsThrowsSQLiteCloudError() { val invalidSql = SQLiteCloud( @@ -38,6 +47,19 @@ class ConnectionTest { } } + @Test + fun connectWithInvalidCredentialsApiKeyThrowsSQLiteCloudError() { + val invalidSql = SQLiteCloud( + appContext = TestContext.context, + config = sqlApiKey.config.copy(apiKey = "INVALID APIKEY"), + ) + assertThrows(SQLiteCloudError::class.java) { + runBlocking { + invalidSql.connect() + } + } + } + @Test fun disconnectWhenNotConnectedThrowsSQLiteCloudError() { assertThrows(SQLiteCloudError::class.java) { diff --git a/sqlitecloud/src/androidTest/java/io/sqlitecloud/Secrets.kt b/sqlitecloud/src/androidTest/java/io/sqlitecloud/Secrets.kt index 9a54684..52ea7bf 100644 --- a/sqlitecloud/src/androidTest/java/io/sqlitecloud/Secrets.kt +++ b/sqlitecloud/src/androidTest/java/io/sqlitecloud/Secrets.kt @@ -7,4 +7,5 @@ data class Secrets( val hostname: String, val username: String, val password: String, + val apiKey: String ) diff --git a/sqlitecloud/src/androidTest/java/io/sqlitecloud/TestContext.kt b/sqlitecloud/src/androidTest/java/io/sqlitecloud/TestContext.kt index 9770af9..b665f1d 100644 --- a/sqlitecloud/src/androidTest/java/io/sqlitecloud/TestContext.kt +++ b/sqlitecloud/src/androidTest/java/io/sqlitecloud/TestContext.kt @@ -41,6 +41,22 @@ object TestContext { hostname = hostname, username = username, password = password, + apiKey = null, + rootCertificate = rootCertificatePath, + ) + ) + + fun sqliteCloudApiKey( + hostname: String = secrets?.hostname ?: "", + apiKey: String = secrets?.apiKey ?: "", + rootCertificatePath: String = certFile.path, + ) = SQLiteCloud( + appContext = context, + config = SQLiteCloudConfig( + hostname = hostname, + username = null, + password = null, + apiKey = apiKey, rootCertificate = rootCertificatePath, ) ) diff --git a/sqlitecloud/src/main/cpp/sqcloud.c b/sqlitecloud/src/main/cpp/sqcloud.c index 0728ac8..63dfa9f 100644 --- a/sqlitecloud/src/main/cpp/sqcloud.c +++ b/sqlitecloud/src/main/cpp/sqcloud.c @@ -52,6 +52,7 @@ struct tls; struct tls_config; struct tls *tls_client(void); struct tls_config *tls_config_new(void); +void tls_config_free(struct tls_config *config); int tls_init(void); int tls_configure(struct tls *_ctx, struct tls_config *_config); int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername); @@ -531,6 +532,7 @@ static bool internal_setup_tls (SQCloudConnection *connection, SQCloudConfig *co // apply configuration to context rc = tls_configure(tls_context, tls_conf); + tls_config_free(tls_conf); if (rc < 0) { return internal_set_error(connection, INTERNAL_ERRCODE_TLS, "Error in tls_configure: %s.", tls_error(tls_context)); } diff --git a/sqlitecloud/src/main/cpp/sqlitecloud.cpp b/sqlitecloud/src/main/cpp/sqlitecloud.cpp index 7ec2fc2..69271fb 100644 --- a/sqlitecloud/src/main/cpp/sqlitecloud.cpp +++ b/sqlitecloud/src/main/cpp/sqlitecloud.cpp @@ -54,10 +54,10 @@ Java_io_sqlitecloud_SQLiteCloudBridge_doConnect( jstring username, jstring password, jstring database, + jstring apiKey, jint timeout, jint family, jboolean compression, - jboolean sqlite_mode, jboolean zero_text, jboolean password_hashed, jboolean nonlinearizable, @@ -77,13 +77,13 @@ Java_io_sqlitecloud_SQLiteCloudBridge_doConnect( .username = cString(env, username), .password = cString(env, password), .database = database ? cString(env, database) : nullptr, + .api_key = cString(env, apiKey), .timeout = timeout, .family = family, .compression = static_cast(compression), - .sqlite_mode = static_cast(sqlite_mode), .zero_text = static_cast(zero_text), .password_hashed = static_cast(password_hashed), - .nonlinearizable = static_cast(nonlinearizable), + .non_linearizable = static_cast(nonlinearizable), .db_memory = static_cast(db_memory), .no_blob = static_cast(no_blob), .db_create = static_cast(db_create), diff --git a/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloud.kt b/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloud.kt index cfe2e9c..3b3ed76 100644 --- a/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloud.kt +++ b/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloud.kt @@ -154,10 +154,10 @@ class SQLiteCloud( username = config.username, password = config.password, database = config.dbname, + apiKey = config.apiKey, timeout = config.timeout, family = config.family.value, compression = config.compression, - sqliteMode = config.sqliteMode, zeroText = config.zerotext, passwordHashed = config.passwordHashed, nonlinearizable = config.nonlinearizable, diff --git a/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloudBridge.kt b/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloudBridge.kt index 0b22f18..a70a249 100644 --- a/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloudBridge.kt +++ b/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloudBridge.kt @@ -42,13 +42,13 @@ internal class SQLiteCloudBridge(val logger: SQLiteCloudLogger?) { private external fun doConnect( hostname: String, port: Int, - username: String, - password: String, + username: String?, + password: String?, database: String?, + apiKey: String?, timeout: Int, family: Int, compression: Boolean, - sqliteMode: Boolean, zeroText: Boolean, passwordHashed: Boolean, nonlinearizable: Boolean, @@ -67,13 +67,13 @@ internal class SQLiteCloudBridge(val logger: SQLiteCloudLogger?) { fun connect( hostname: String, port: Int, - username: String, - password: String, + username: String?, + password: String?, database: String?, + apiKey: String?, timeout: Int, family: Int, compression: Boolean, - sqliteMode: Boolean, zeroText: Boolean, passwordHashed: Boolean, nonlinearizable: Boolean, @@ -94,10 +94,10 @@ internal class SQLiteCloudBridge(val logger: SQLiteCloudLogger?) { username = username, password = password, database = database, + apiKey = apiKey, timeout = timeout, family = family, compression = compression, - sqliteMode = sqliteMode, zeroText = zeroText, passwordHashed = passwordHashed, nonlinearizable = nonlinearizable, diff --git a/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloudConfig.kt b/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloudConfig.kt index d97408f..376d8ba 100644 --- a/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloudConfig.kt +++ b/sqlitecloud/src/main/java/io/sqlitecloud/SQLiteCloudConfig.kt @@ -5,8 +5,9 @@ import kotlin.io.path.Path data class SQLiteCloudConfig( val hostname: String, - val username: String, - val password: String, + val username: String?, + val password: String?, + val apiKey: String?, val port: Int = defaultPort, val family: Family = Family.IPv4, val passwordHashed: Boolean = false, @@ -29,7 +30,13 @@ data class SQLiteCloudConfig( val clientCertificateKey: String? = null, ) { val connectionString: String - get() = "sqlitecloud://$username:****@$hostname:$port/${dbname ?: ""}" + get() { + if (apiKey != null) { + return "sqlitecloud://$hostname:$port/${dbname ?: ""}?apikey=$apiKey" + } else { + return "sqlitecloud://${username ?: ""}:****@$hostname:$port/${dbname ?: ""}" + } + } companion object { const val defaultPort = 8860 @@ -62,6 +69,7 @@ data class SQLiteCloudConfig( key to value } ?: emptyMap() + val apiKey = queryItems["apikey"] val family = queryItems["family"] val passwordHashed = queryItems["passwordHashed"] val nonlinearizable = queryItems["nonlinearizable"] @@ -82,8 +90,9 @@ data class SQLiteCloudConfig( return SQLiteCloudConfig( hostname = connectionUri.host ?: "", - username = userInfo?.get(0) ?: "", - password = userInfo?.get(1) ?: "", + username = userInfo?.get(0), + password = userInfo?.get(1), + apiKey = apiKey, port = port, dbname = dbname, family = family?.toIntOrNull() diff --git a/sqlitecloud/src/test/java/io/sqlitecloud/SQLiteCloudTest.kt b/sqlitecloud/src/test/java/io/sqlitecloud/SQLiteCloudTest.kt index e397a15..59e76fd 100644 --- a/sqlitecloud/src/test/java/io/sqlitecloud/SQLiteCloudTest.kt +++ b/sqlitecloud/src/test/java/io/sqlitecloud/SQLiteCloudTest.kt @@ -17,4 +17,17 @@ class SQLiteCloudTest { assertEquals("dbname", config.dbname) assertEquals("path", config.rootCertificate) } + + @Test + fun creationWithApiKeyFromStringIsCorrect() { + val connectionString = "sqlitecloud://hostname.com:1234/dbname?root_certificate=path&apikey=apikey" + + val config = SQLiteCloudConfig.fromString(connectionString) + + assertEquals("apiKey", config.apiKey) + assertEquals("hostname.com", config.hostname) + assertEquals(1234, config.port) + assertEquals("dbname", config.dbname) + assertEquals("path", config.rootCertificate) + } }