From 9173dfe12abc49851b26feba882f350825b0ae2b Mon Sep 17 00:00:00 2001 From: Eoin Boylan Date: Wed, 19 Jul 2023 10:49:01 +0100 Subject: [PATCH 01/14] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d138a4f..b2cf77e 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Our Kotlin Multiplatform SDK distributed via [maven](https://search.maven.org/ar ### Gradle DSL ```kotlin -implementation("com.evervault.sdk:evervault-core:1.0.0") +implementation("com.evervault.sdk:evervault-multiplatform:1.0.0") ``` ### Maven @@ -32,7 +32,7 @@ implementation("com.evervault.sdk:evervault-core:1.0.0") ```xml com.evervault.sdk - evervault-core + evervault-multiplatform 1.0.0 ``` From a9b4c8fffb17554a3aa4d47cbff33f16b5361d03 Mon Sep 17 00:00:00 2001 From: Eoin Boylan Date: Wed, 19 Jul 2023 10:52:57 +0100 Subject: [PATCH 02/14] fix pr template --- .github/pull_request_template.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 56d52ec..d6820f1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,7 +5,3 @@ Add links to issues, tech plans etc. # How Describe how you've approached the problem -### If this PR adds a new route: -- [ ] I have added the appropriate Team Audit Log middleware and set it up correctly. -- [ ] I have updated the UI to consume this new type of audit log -See the [Adding an Activity Log Guide](https://www.notion.so/evervault/Adding-a-new-Activity-Log-event-8fa4f2cc72004deba29068c4c506a3b2) for more details From 1bbf4306ef422e03e475b95f0b680788ec991f1e Mon Sep 17 00:00:00 2001 From: Cian Butler <119862796+cian-ev@users.noreply.github.com> Date: Wed, 19 Jul 2023 10:55:36 +0100 Subject: [PATCH 03/14] Create codeql.yml --- .github/workflows/codeql.yml | 82 ++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..8dd8acc --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,82 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "main" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + schedule: + - cron: '39 22 * * 0' + +jobs: + analyze: + name: Analyze + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners + # Consider using larger runners for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" From 5cd8e915af602e88c8cb0139f040945564584b6c Mon Sep 17 00:00:00 2001 From: Eoin Boylan Date: Wed, 19 Jul 2023 11:01:00 +0100 Subject: [PATCH 04/14] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b2cf77e..d138a4f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Our Kotlin Multiplatform SDK distributed via [maven](https://search.maven.org/ar ### Gradle DSL ```kotlin -implementation("com.evervault.sdk:evervault-multiplatform:1.0.0") +implementation("com.evervault.sdk:evervault-core:1.0.0") ``` ### Maven @@ -32,7 +32,7 @@ implementation("com.evervault.sdk:evervault-multiplatform:1.0.0") ```xml com.evervault.sdk - evervault-multiplatform + evervault-core 1.0.0 ``` From d9dcecb777a1b37442de4f39c59443ce4296dfca Mon Sep 17 00:00:00 2001 From: Jake Grogan Date: Wed, 9 Aug 2023 10:38:08 +0100 Subject: [PATCH 05/14] Add runFunction and decrypt functions --- .../kotlin/com/evervault/sdk/Config.kt | 16 ++- .../kotlin/com/evervault/sdk/Evervault.kt | 44 +++++++ .../kotlin/com/evervault/sdk/core/Http.kt | 14 ++- .../com/evervault/sdk/core/HttpRequest.kt | 110 ++++++++++++++++++ .../sdk/core/models/FunctionRunResponse.kt | 15 +++ .../evervault/sdk/core/HttpKeysLoaderTest.kt | 6 +- .../com/evervault/sdk/core/HttpRequestTest.kt | 59 ++++++++++ 7 files changed, 258 insertions(+), 6 deletions(-) create mode 100644 evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt create mode 100644 evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt create mode 100644 evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Config.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Config.kt index 15cf301..a7995a2 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Config.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Config.kt @@ -3,6 +3,8 @@ package com.evervault.sdk import com.evervault.sdk.core.keys.CageKey private const val KEYS_URL = "https://keys.evervault.com" +private const val FUNCTION_RUN_URL = "https://run.evervault.com" +private const val API_URL = "https://api.evervault.com" private val DEBUG_KEY = CageKey( ecdhP256Key = "Al1/Mo85D7t/XvC3I+YYpJvP+OsSyxIbSrhtDhg1SClQ", @@ -23,7 +25,7 @@ internal data class Config( teamId = teamId, appId = appId, encryption = EncryptionConfig(publicKey), - httpConfig = HttpConfig(keysUrl = configUrls.keysUrl) + httpConfig = HttpConfig(keysUrl = configUrls.keysUrl, functionRunUrl = configUrls.functionRunUrl, apiUrl = configUrls.apiUrl) ) } @@ -36,7 +38,15 @@ data class ConfigUrls( /** * The URL for the custom keys endpoint. Default is the Evervault keys URL. */ - var keysUrl: String = KEYS_URL + var keysUrl: String = KEYS_URL, + /** + * The URL for the Function Run API. + */ + var functionRunUrl: String = FUNCTION_RUN_URL, + /** + * The URL for the API. + */ + var apiUrl: String = API_URL, ) internal data class EncryptionConfig( @@ -53,4 +63,4 @@ internal data class EncryptionConfig( data class Header(val iss: String, val version: Int) } -internal data class HttpConfig(var keysUrl: String) +internal data class HttpConfig(var keysUrl: String, var functionRunUrl: String, var apiUrl: String) diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt index af59653..c62384e 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt @@ -95,6 +95,42 @@ class Evervault private constructor() { return client.encrypt(data) } + /** + * Runs an Evervault Function with the supplied token. + * + * @param functionName The name of the Function. + * @param token The Run token used to execute the Function. + * @param payload The payload to send to the Function. + * @return The Function run result. The return type is `Any`, and the caller is responsible for safely casting the result based on the original data type. + * @throws EvervaultException.InitializationError If the encryption process fails. + * + * ## Declaration + * ```kotlin + * suspend fun runFunction(functionName: String, token: String, payload: Any): Any + * ``` + * + * ## Example + * ```kotlin + * val result = Evervault.shared.runFunction("MyPythonFunction", "token1234567890", encryptedData) + * ``` + * + * The `runFunction` function allows you to run an Evervault Function using a Run Token. Run Tokens are single use, time bound tokens for invoking an Evervault Function with a given payload. + * + * Run Tokens will only last for 5 minutes and must be used with the same payload that was used to create the Run Token. + * + * The function returns the result data as `Any`, and the caller is responsible for safely casting the result based on the original data type. For Boolean, Numerics, and Strings, the encrypted data is returned as a String. For Lists and Maps, the encrypted data maintains the same structure but is encrypted. For ByteArray, the encrypted data is returned as encrypted ByteArray. + * + * Note that the encryption process is performed asynchronously using the `suspend` keyword. It's recommended to call this function from within a `suspend` context. + */ + suspend fun runFunction(functionName: String, token: String, payload: Any): Any { + val client = client ?: throw EvervaultException.InitializationError + return client.runFunctionWithToken(functionName, token, payload) + } + + suspend fun decrypt(token: String, data: Any, type: Class): Any { + val client = client ?: throw EvervaultException.InitializationError + return client.decryptWithToken(token, data) + } companion object { /** @@ -146,6 +182,14 @@ internal class Client(private val config: Config, private val http: Http, privat return handlers.encrypt(data) } + + suspend fun runFunctionWithToken(functionName: String, token: String, payload: Any): Any { + return http.runFunctionWithToken(functionName, token, payload) + } + + suspend fun decryptWithToken(token: String, data: Any): Any { + return http.decryptWithToken(token, data) + } } /** diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/Http.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/Http.kt index 48f1f1d..d0536a6 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/Http.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/Http.kt @@ -4,14 +4,24 @@ import com.evervault.sdk.HttpConfig import com.evervault.sdk.core.keys.CageKey internal class Http( - private val keysLoader: HttpKeysLoader + private val keysLoader: HttpKeysLoader, + private val httpRequest: HttpRequest ) { constructor(config: HttpConfig, teamId: String, appId: String, context: String): this( - HttpKeysLoader("${config.keysUrl}/$teamId/apps/$appId?context=$context") + HttpKeysLoader("${config.keysUrl}/$teamId/apps/$appId?context=$context"), + HttpRequest(config) ) suspend fun loadKeys(): CageKey { return keysLoader.loadKeys() } + + suspend fun runFunctionWithToken(functionName: String, token: String, payload: Any): Any { + return httpRequest.runFunctionWithToken(functionName, token, payload) + } + + suspend fun decryptWithToken(token: String, data: Any): Any { + return httpRequest.decryptWithToken(token, data) + } } diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt new file mode 100644 index 0000000..d7a9ead --- /dev/null +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt @@ -0,0 +1,110 @@ +package com.evervault.sdk.core + +import com.evervault.sdk.core.models.FunctionRunResponse +import com.evervault.sdk.HttpConfig +import io.ktor.client.HttpClient +import io.ktor.client.plugins.defaultRequest +import io.ktor.client.request.post +import io.ktor.client.request.header +import io.ktor.client.request.headers +import io.ktor.client.request.setBody +import io.ktor.client.statement.HttpResponse +import io.ktor.client.statement.bodyAsText +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kotlinx.serialization.json.Json + +internal class HttpRequest(private var config: HttpConfig) { + private var activeTask: kotlinx.coroutines.Deferred? = null + + private var httpClient = HttpClient { + defaultRequest { + header(HttpHeaders.ContentType, "application/json") + } + } + + private val json = Json { ignoreUnknownKeys = true } + + suspend fun runFunctionWithToken(functionName: String, token: String, payload: Any): Any { + activeTask?.let { + return it.await() + } + + val task = coroutineScope { + async { + try { + val result = executeRunFunctionWithToken(functionName, token, payload) + activeTask = null + return@async result + } catch (error: Error) { + activeTask = null + throw error + } + } + } + + activeTask = task + + return task.await() + } + + suspend fun decryptWithToken(token: String, data: Any): Any { + activeTask?.let { + val res = it.await() + return res + } + + val task = coroutineScope{ + async { + try { + val result = executeDecryptWithToken(token, data) + activeTask = null + return@async result + } catch (error: Error) { + activeTask = null + throw error + } + } + } + + activeTask = task + + return task.await() + } + + private suspend fun executeRunFunctionWithToken(functionName: String, token: String, payload: Any): FunctionRunResponse { + val response: HttpResponse = httpClient.post("${config.functionRunUrl}/${functionName}") { + setBody(payload) + headers { + append("Authorization", "Bearer ${token}") + } + } + + if (response.status != HttpStatusCode.OK) { + throw Error("Failed to execute function run. Status code: ${response.status}") + } + + val responseBody = response.bodyAsText() + val body = json.decodeFromString(responseBody) + return body + } + + private suspend fun executeDecryptWithToken(token: String, data: Any): T { + val response: HttpResponse = httpClient.post("${config.apiUrl}/decrypt") { + setBody(data) + headers { + append("Authorization", "Bearer ${token}") + } + } + + if (response.status != HttpStatusCode.OK) { + throw Error("Failed to decrypt data. Status code: ${response.status}") + } + + val responseBody = response.bodyAsText() + val body = json.decodeFromString(responseBody) as T + return body + } +} diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt new file mode 100644 index 0000000..4eab9c0 --- /dev/null +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt @@ -0,0 +1,15 @@ +package com.evervault.sdk.core.models + +import kotlinx.serialization.Serializable + +@Serializable +data class FunctionResponseResult( + val message: String +) + +@Serializable +data class FunctionRunResponse( + val result: FunctionResponseResult, + val runId: String, + val appUuid: String +) diff --git a/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpKeysLoaderTest.kt b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpKeysLoaderTest.kt index 2329257..adb5366 100644 --- a/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpKeysLoaderTest.kt +++ b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpKeysLoaderTest.kt @@ -12,7 +12,11 @@ class HttpKeysLoaderTest { @Test fun testLoadKeys() = runBlocking { val http = Http( - config = HttpConfig(keysUrl = ConfigUrls().keysUrl), + config = HttpConfig( + keysUrl = ConfigUrls().keysUrl, + functionRunUrl = ConfigUrls().functionRunUrl, + apiUrl = ConfigUrls().apiUrl + ), teamId = getenv("VITE_EV_TEAM_UUID"), appId = getenv("VITE_EV_APP_UUID"), context = "default" diff --git a/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt new file mode 100644 index 0000000..ad6b209 --- /dev/null +++ b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt @@ -0,0 +1,59 @@ +package com.evervault.sdk.core + +import com.evervault.sdk.core.models.FunctionRunResponse +import com.evervault.sdk.core.models.FunctionResponseResult +import com.evervault.sdk.ConfigUrls +import com.evervault.sdk.HttpConfig +import com.evervault.sdk.test.getenv +import kotlinx.coroutines.runBlocking +import kotlin.test.Test +import kotlin.test.assertEquals +import org.mockito.kotlin.* + +class HttpRequestTest { + @Test + fun testExecuteFunctionWithToken() = runBlocking { + val urls = ConfigUrls() + val mockResponse = FunctionRunResponse( + result = FunctionResponseResult( + message = "mock result" + ), + runId = "function_run_1234567890", + appUuid = "app_1234567890" + ) + val httpMock = mock {} + whenever(httpMock.runFunctionWithToken(any(), any(), any())).thenReturn(mockResponse) + val response = httpMock.runFunctionWithToken("testFuncName", "testToken", "test") + + assertEquals( + response, + mockResponse + ) + } + @Test + fun testExecuteDecryptWithToken() = runBlocking { + data class Foo( + val message: String + ) + + val fooInstance = Foo(message = "test") + val testEncryptedData = "ev:abc123:1234567890:$" + + val urls = ConfigUrls() + val httpMock = mock {} + whenever(httpMock.decryptWithToken(any(), any())).thenReturn(fooInstance) + val response = httpMock.decryptWithToken("testToken", testEncryptedData) + + assertEquals( + response, + Foo ( + message = "test" + ) + ) + + assertEquals( + response::class.java, + fooInstance::class.java + ) + } +} \ No newline at end of file From 941f2048b70d2882c073f09f0fde16356e2ef54c Mon Sep 17 00:00:00 2001 From: Jake Grogan Date: Wed, 9 Aug 2023 10:48:26 +0100 Subject: [PATCH 06/14] Update comments --- .../kotlin/com/evervault/sdk/Evervault.kt | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt index c62384e..a451cdd 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt @@ -127,7 +127,31 @@ class Evervault private constructor() { return client.runFunctionWithToken(functionName, token, payload) } - suspend fun decrypt(token: String, data: Any, type: Class): Any { + /** + * Decrypts data previously encrypted with the `encrypt()` function or through Relay. + * + * @param token The token used to decrypt the data. + * @param data The encrypted data that's to be decrypted. + * @return The decrypted data + * @throws EvervaultException.InitializationError If the encryption process fails. + * + * ## Declaration + * ```kotlin + * suspend fun decrypt(token: String, data: Any): Any + * ``` + * + * ## Example + * ```kotlin + * val decrypted = Evervault.shared.decrypt("token1234567890", encryptedData) + * ``` + * + * The `decrypt()` function allows you to decrypt previously encrypted data using a token and attempt to deserialize it to the parameterized type. The token is a single use, time bound token for decrypting data. + * + * Tokens will only last for 5 minutes and must be used with the same payload that was used to create the token. + * + * The function returns the decrypted data as `Any`, and the caller is responsible for safely casting the result based on the original data type. For Boolean, Numerics, and Strings, the encrypted data is returned as a String. For Lists and Maps, the encrypted data maintains the same structure but is encrypted. For ByteArray, the encrypted data is returned as encrypted ByteArray. + */ + suspend fun decrypt(token: String, data: Any): Any { val client = client ?: throw EvervaultException.InitializationError return client.decryptWithToken(token, data) } From 3f30bd8ce4adf8ec38ce1abd37c091c23aee6f14 Mon Sep 17 00:00:00 2001 From: Jake Grogan Date: Fri, 11 Aug 2023 15:32:45 +0100 Subject: [PATCH 07/14] chore(tokens): Support generic function run result --- .../com/evervault/sdk/core/models/FunctionRunResponse.kt | 8 +------- .../kotlin/com/evervault/sdk/core/HttpRequestTest.kt | 8 ++++---- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt index 4eab9c0..214c839 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt @@ -2,14 +2,8 @@ package com.evervault.sdk.core.models import kotlinx.serialization.Serializable -@Serializable -data class FunctionResponseResult( - val message: String -) - -@Serializable data class FunctionRunResponse( - val result: FunctionResponseResult, + val result: Any, val runId: String, val appUuid: String ) diff --git a/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt index ad6b209..2f67f74 100644 --- a/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt +++ b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt @@ -1,7 +1,7 @@ package com.evervault.sdk.core import com.evervault.sdk.core.models.FunctionRunResponse -import com.evervault.sdk.core.models.FunctionResponseResult +// import com.evervault.sdk.core.models.FunctionResponseResult import com.evervault.sdk.ConfigUrls import com.evervault.sdk.HttpConfig import com.evervault.sdk.test.getenv @@ -15,9 +15,9 @@ class HttpRequestTest { fun testExecuteFunctionWithToken() = runBlocking { val urls = ConfigUrls() val mockResponse = FunctionRunResponse( - result = FunctionResponseResult( - message = "mock result" - ), + result = object { + val message = "test" + }, runId = "function_run_1234567890", appUuid = "app_1234567890" ) From 0f35be27e299883a1af97054a7ff5d488e754fb0 Mon Sep 17 00:00:00 2001 From: Jake Grogan Date: Mon, 14 Aug 2023 11:13:00 +0100 Subject: [PATCH 08/14] chore: update grant --- .../src/commonMain/kotlin/com/evervault/sdk/Evervault.kt | 2 +- .../src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt index a451cdd..5cf9b10 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt @@ -149,7 +149,7 @@ class Evervault private constructor() { * * Tokens will only last for 5 minutes and must be used with the same payload that was used to create the token. * - * The function returns the decrypted data as `Any`, and the caller is responsible for safely casting the result based on the original data type. For Boolean, Numerics, and Strings, the encrypted data is returned as a String. For Lists and Maps, the encrypted data maintains the same structure but is encrypted. For ByteArray, the encrypted data is returned as encrypted ByteArray. + * The function returns the decrypted data as `Any`, and the caller is responsible for safely casting the result based on the original data type. The decrypted data is returned as the parameterized type T. */ suspend fun decrypt(token: String, data: Any): Any { val client = client ?: throw EvervaultException.InitializationError diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt index d7a9ead..b15f2cb 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt @@ -95,7 +95,7 @@ internal class HttpRequest(private var config: HttpConfig) { val response: HttpResponse = httpClient.post("${config.apiUrl}/decrypt") { setBody(data) headers { - append("Authorization", "Bearer ${token}") + append("Authorization", "Token ${token}") } } From 3d01c4163793774de568f45f677bba9e2a551903 Mon Sep 17 00:00:00 2001 From: Jake Grogan Date: Tue, 29 Aug 2023 11:57:16 +0100 Subject: [PATCH 09/14] chore: remove runFunction method and add e2e tests for decrypt function --- .gitignore | 1 + evervault-core/build.gradle.kts | 3 + .../kotlin/com/evervault/sdk/Config.kt | 9 +- .../kotlin/com/evervault/sdk/Evervault.kt | 52 +------ .../kotlin/com/evervault/sdk/core/Http.kt | 8 +- .../com/evervault/sdk/core/HttpRequest.kt | 56 ++----- .../sdk/core/models/FunctionRunResponse.kt | 9 -- .../evervault/sdk/core/HttpKeysLoaderTest.kt | 2 - .../com/evervault/sdk/core/HttpRequestTest.kt | 146 +++++++++++++----- .../sampleapplication/EncryptionView.kt | 4 + .../commonMain/kotlin/EncryptionViewModel.kt | 6 + 11 files changed, 145 insertions(+), 151 deletions(-) delete mode 100644 evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt diff --git a/.gitignore b/.gitignore index 7bb9ebc..f72ae47 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ build/ local.properties ### IntelliJ IDEA ### +.idea/**/* .idea/modules.xml .idea/jarRepositories.xml .idea/compiler.xml diff --git a/evervault-core/build.gradle.kts b/evervault-core/build.gradle.kts index 2d20d43..be940ea 100644 --- a/evervault-core/build.gradle.kts +++ b/evervault-core/build.gradle.kts @@ -34,6 +34,8 @@ kotlin { // JSON implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1") + + implementation("com.google.code.gson:gson:2.8.7") } } @@ -41,6 +43,7 @@ kotlin { dependencies { implementation(kotlin("test")) implementation("org.mockito.kotlin:mockito-kotlin:5.0.0") + implementation("com.google.code.gson:gson:2.8.7") } } diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Config.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Config.kt index a7995a2..b16f9ea 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Config.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Config.kt @@ -3,7 +3,6 @@ package com.evervault.sdk import com.evervault.sdk.core.keys.CageKey private const val KEYS_URL = "https://keys.evervault.com" -private const val FUNCTION_RUN_URL = "https://run.evervault.com" private const val API_URL = "https://api.evervault.com" private val DEBUG_KEY = CageKey( @@ -25,7 +24,7 @@ internal data class Config( teamId = teamId, appId = appId, encryption = EncryptionConfig(publicKey), - httpConfig = HttpConfig(keysUrl = configUrls.keysUrl, functionRunUrl = configUrls.functionRunUrl, apiUrl = configUrls.apiUrl) + httpConfig = HttpConfig(keysUrl = configUrls.keysUrl, apiUrl = configUrls.apiUrl) ) } @@ -39,10 +38,6 @@ data class ConfigUrls( * The URL for the custom keys endpoint. Default is the Evervault keys URL. */ var keysUrl: String = KEYS_URL, - /** - * The URL for the Function Run API. - */ - var functionRunUrl: String = FUNCTION_RUN_URL, /** * The URL for the API. */ @@ -63,4 +58,4 @@ internal data class EncryptionConfig( data class Header(val iss: String, val version: Int) } -internal data class HttpConfig(var keysUrl: String, var functionRunUrl: String, var apiUrl: String) +internal data class HttpConfig(var keysUrl: String, var apiUrl: String) diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt index 5cf9b10..91d1392 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/Evervault.kt @@ -95,44 +95,12 @@ class Evervault private constructor() { return client.encrypt(data) } - /** - * Runs an Evervault Function with the supplied token. - * - * @param functionName The name of the Function. - * @param token The Run token used to execute the Function. - * @param payload The payload to send to the Function. - * @return The Function run result. The return type is `Any`, and the caller is responsible for safely casting the result based on the original data type. - * @throws EvervaultException.InitializationError If the encryption process fails. - * - * ## Declaration - * ```kotlin - * suspend fun runFunction(functionName: String, token: String, payload: Any): Any - * ``` - * - * ## Example - * ```kotlin - * val result = Evervault.shared.runFunction("MyPythonFunction", "token1234567890", encryptedData) - * ``` - * - * The `runFunction` function allows you to run an Evervault Function using a Run Token. Run Tokens are single use, time bound tokens for invoking an Evervault Function with a given payload. - * - * Run Tokens will only last for 5 minutes and must be used with the same payload that was used to create the Run Token. - * - * The function returns the result data as `Any`, and the caller is responsible for safely casting the result based on the original data type. For Boolean, Numerics, and Strings, the encrypted data is returned as a String. For Lists and Maps, the encrypted data maintains the same structure but is encrypted. For ByteArray, the encrypted data is returned as encrypted ByteArray. - * - * Note that the encryption process is performed asynchronously using the `suspend` keyword. It's recommended to call this function from within a `suspend` context. - */ - suspend fun runFunction(functionName: String, token: String, payload: Any): Any { - val client = client ?: throw EvervaultException.InitializationError - return client.runFunctionWithToken(functionName, token, payload) - } - /** * Decrypts data previously encrypted with the `encrypt()` function or through Relay. * * @param token The token used to decrypt the data. - * @param data The encrypted data that's to be decrypted. - * @return The decrypted data + * @param data The encrypted data that's to be decrypted. Must be in the form of a map e.g { "data": encryptedData } + * @return The decrypted data. The data is a `Map` and will need to be cast. See below for an example * @throws EvervaultException.InitializationError If the encryption process fails. * * ## Declaration @@ -142,18 +110,18 @@ class Evervault private constructor() { * * ## Example * ```kotlin - * val decrypted = Evervault.shared.decrypt("token1234567890", encryptedData) + * val decrypted = Evervault.shared.decrypt("token1234567890", encryptedData) as Map * ``` * * The `decrypt()` function allows you to decrypt previously encrypted data using a token and attempt to deserialize it to the parameterized type. The token is a single use, time bound token for decrypting data. * * Tokens will only last for 5 minutes and must be used with the same payload that was used to create the token. * - * The function returns the decrypted data as `Any`, and the caller is responsible for safely casting the result based on the original data type. The decrypted data is returned as the parameterized type T. + * The function returns the decrypted data as `Map`, and the caller is responsible for safely casting the result based on the original data type. */ - suspend fun decrypt(token: String, data: Any): Any { + suspend fun decrypt(token: String, data: Any): Any{ val client = client ?: throw EvervaultException.InitializationError - return client.decryptWithToken(token, data) + return client.decryptWithToken(token, data) } companion object { @@ -207,12 +175,8 @@ internal class Client(private val config: Config, private val http: Http, privat return handlers.encrypt(data) } - suspend fun runFunctionWithToken(functionName: String, token: String, payload: Any): Any { - return http.runFunctionWithToken(functionName, token, payload) - } - - suspend fun decryptWithToken(token: String, data: Any): Any { - return http.decryptWithToken(token, data) + suspend fun decryptWithToken(token: String, data: Any): Any { + return http.decryptWithToken(token, data) } } diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/Http.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/Http.kt index d0536a6..36781be 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/Http.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/Http.kt @@ -17,11 +17,7 @@ internal class Http( return keysLoader.loadKeys() } - suspend fun runFunctionWithToken(functionName: String, token: String, payload: Any): Any { - return httpRequest.runFunctionWithToken(functionName, token, payload) - } - - suspend fun decryptWithToken(token: String, data: Any): Any { - return httpRequest.decryptWithToken(token, data) + suspend fun decryptWithToken(token: String, data: Any): Any { + return httpRequest.decryptWithToken(token, data) } } diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt index b15f2cb..1262a77 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt @@ -1,6 +1,5 @@ package com.evervault.sdk.core -import com.evervault.sdk.core.models.FunctionRunResponse import com.evervault.sdk.HttpConfig import io.ktor.client.HttpClient import io.ktor.client.plugins.defaultRequest @@ -15,6 +14,9 @@ import io.ktor.http.HttpStatusCode import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.serialization.json.Json +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import java.lang.reflect.Type internal class HttpRequest(private var config: HttpConfig) { private var activeTask: kotlinx.coroutines.Deferred? = null @@ -26,31 +28,8 @@ internal class HttpRequest(private var config: HttpConfig) { } private val json = Json { ignoreUnknownKeys = true } - - suspend fun runFunctionWithToken(functionName: String, token: String, payload: Any): Any { - activeTask?.let { - return it.await() - } - - val task = coroutineScope { - async { - try { - val result = executeRunFunctionWithToken(functionName, token, payload) - activeTask = null - return@async result - } catch (error: Error) { - activeTask = null - throw error - } - } - } - - activeTask = task - - return task.await() - } - suspend fun decryptWithToken(token: String, data: Any): Any { + suspend fun decryptWithToken(token: String, data: Any): Any { activeTask?.let { val res = it.await() return res @@ -59,7 +38,7 @@ internal class HttpRequest(private var config: HttpConfig) { val task = coroutineScope{ async { try { - val result = executeDecryptWithToken(token, data) + val result = executeDecryptWithToken(token, data) activeTask = null return@async result } catch (error: Error) { @@ -74,24 +53,8 @@ internal class HttpRequest(private var config: HttpConfig) { return task.await() } - private suspend fun executeRunFunctionWithToken(functionName: String, token: String, payload: Any): FunctionRunResponse { - val response: HttpResponse = httpClient.post("${config.functionRunUrl}/${functionName}") { - setBody(payload) - headers { - append("Authorization", "Bearer ${token}") - } - } - - if (response.status != HttpStatusCode.OK) { - throw Error("Failed to execute function run. Status code: ${response.status}") - } - - val responseBody = response.bodyAsText() - val body = json.decodeFromString(responseBody) - return body - } - - private suspend fun executeDecryptWithToken(token: String, data: Any): T { + private suspend fun executeDecryptWithToken(token: String, payload: Any): Any { + val data = Gson().toJson(payload) val response: HttpResponse = httpClient.post("${config.apiUrl}/decrypt") { setBody(data) headers { @@ -104,7 +67,8 @@ internal class HttpRequest(private var config: HttpConfig) { } val responseBody = response.bodyAsText() - val body = json.decodeFromString(responseBody) as T - return body + val type: Type = object : TypeToken>() {}.type + val map: Map = Gson().fromJson(responseBody, type) + return map } } diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt deleted file mode 100644 index 214c839..0000000 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/models/FunctionRunResponse.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.evervault.sdk.core.models - -import kotlinx.serialization.Serializable - -data class FunctionRunResponse( - val result: Any, - val runId: String, - val appUuid: String -) diff --git a/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpKeysLoaderTest.kt b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpKeysLoaderTest.kt index adb5366..9c7e83c 100644 --- a/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpKeysLoaderTest.kt +++ b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpKeysLoaderTest.kt @@ -14,7 +14,6 @@ class HttpKeysLoaderTest { val http = Http( config = HttpConfig( keysUrl = ConfigUrls().keysUrl, - functionRunUrl = ConfigUrls().functionRunUrl, apiUrl = ConfigUrls().apiUrl ), teamId = getenv("VITE_EV_TEAM_UUID"), @@ -22,7 +21,6 @@ class HttpKeysLoaderTest { context = "default" ) val cageKey = http.loadKeys() - assertEquals( cageKey, CageKey(publicKey = cageKey.ecdhP256KeyUncompressed, isDebugMode = cageKey.isDebugMode) diff --git a/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt index 2f67f74..dde6628 100644 --- a/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt +++ b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt @@ -1,59 +1,131 @@ package com.evervault.sdk.core -import com.evervault.sdk.core.models.FunctionRunResponse // import com.evervault.sdk.core.models.FunctionResponseResult import com.evervault.sdk.ConfigUrls import com.evervault.sdk.HttpConfig import com.evervault.sdk.test.getenv +import io.ktor.client.HttpClient +import io.ktor.client.plugins.defaultRequest +import io.ktor.client.request.header +import io.ktor.client.request.headers +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import io.ktor.client.statement.HttpResponse +import io.ktor.client.statement.bodyAsText +import io.ktor.http.HttpHeaders +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.runBlocking +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString import kotlin.test.Test import kotlin.test.assertEquals -import org.mockito.kotlin.* +import java.util.Base64 +import kotlinx.serialization.json.Json +import com.google.gson.Gson + + +@Serializable +data class TokenData( + val token: String, +) + +@Serializable +data class Name( + var name: String? +) class HttpRequestTest { - @Test - fun testExecuteFunctionWithToken() = runBlocking { - val urls = ConfigUrls() - val mockResponse = FunctionRunResponse( - result = object { - val message = "test" - }, - runId = "function_run_1234567890", - appUuid = "app_1234567890" - ) - val httpMock = mock {} - whenever(httpMock.runFunctionWithToken(any(), any(), any())).thenReturn(mockResponse) - val response = httpMock.runFunctionWithToken("testFuncName", "testToken", "test") - assertEquals( - response, - mockResponse - ) + private var httpClient = HttpClient { + defaultRequest { + header(HttpHeaders.ContentType, "application/json") + } } - @Test - fun testExecuteDecryptWithToken() = runBlocking { - data class Foo( - val message: String - ) - val fooInstance = Foo(message = "test") - val testEncryptedData = "ev:abc123:1234567890:$" + private val json = Json { ignoreUnknownKeys = true } - val urls = ConfigUrls() - val httpMock = mock {} - whenever(httpMock.decryptWithToken(any(), any())).thenReturn(fooInstance) - val response = httpMock.decryptWithToken("testToken", testEncryptedData) + private val functionName = getenv("VITE_EV_FUNCTION_NAME") + private val apiKey = getenv("VITE_EV_API_KEY") + private val appUuid = getenv("VITE_EV_APP_UUID") + private val teamUuid = getenv("VITE_EV_TEAM_UUID") - assertEquals( - response, - Foo ( - message = "test" - ) + private suspend fun encryptData(url: Any, data: Name): Name { + val task = coroutineScope { + async { + try { + val headerValue = "${appUuid}:${apiKey}" + val b64HeaderValue = + Base64.getEncoder().encodeToString(headerValue.toByteArray()); + val body = json.encodeToString(data) + val response: HttpResponse = + httpClient.post("${url}/encrypt") { + setBody(body) + headers{ + append("Authorization", "Basic $b64HeaderValue") + } + } + val responseBody = response.bodyAsText() + return@async json.decodeFromString(responseBody) + } catch (error: Error) { + throw error + } + } + } + + return task.await() + } + + private suspend fun createClientSideToken(url: Any, data: Any): TokenData { + val task = coroutineScope { + async { + try { + val headerValue = + "${appUuid}:${apiKey}" + val b64HeaderValue = + Base64.getEncoder().encodeToString(headerValue.toByteArray()); + val body = Gson().toJson(mapOf("action" to "api:decrypt", "payload" to data)) + val response: HttpResponse = + httpClient.post("${url}/client-side-tokens") { + setBody(body) + headers { + append("Authorization", "Basic $b64HeaderValue") + } + } + + val responseBody = response.bodyAsText() + return@async json.decodeFromString(responseBody) + } catch (error: Error) { + throw error + } + } + } + + return task.await() + } + + @Test + fun testExecuteDecryptWithToken() = runBlocking { + val http = Http( + config = HttpConfig( + keysUrl = ConfigUrls().keysUrl, + apiUrl = ConfigUrls().apiUrl + ), + teamId = teamUuid, + appId = appUuid, + context = "default" ) + val data = Name(name="Bob") + // Encrypt some data + val encrypted = encryptData(ConfigUrls().apiUrl, data) + // Get a run token + val token = createClientSideToken(ConfigUrls().apiUrl, encrypted) + val decrypted = http.decryptWithToken(token.token, encrypted) as Map + assertEquals( - response::class.java, - fooInstance::class.java + decrypted["name"], + "Bob" ) } } \ No newline at end of file diff --git a/examples/android/src/main/java/com/evervault/sampleapplication/EncryptionView.kt b/examples/android/src/main/java/com/evervault/sampleapplication/EncryptionView.kt index 5a1be03..1771a50 100644 --- a/examples/android/src/main/java/com/evervault/sampleapplication/EncryptionView.kt +++ b/examples/android/src/main/java/com/evervault/sampleapplication/EncryptionView.kt @@ -17,14 +17,18 @@ import androidx.compose.ui.unit.dp fun EncryptionView() { var encryptedValue: String? by remember { mutableStateOf(null) } + var decryptedValue: String? by remember { mutableStateOf(null) } LaunchedEffect(Unit) { encryptedValue = EncryptionViewModel().encryptedValue() + decryptedValue = EncryptionViewModel().decryptedValue() } Column(modifier = Modifier.padding(20.dp)) { Text("Encrypted string:") Text(encryptedValue ?: "Loading..") + Text("Decrypted string:") + Text(decryptedValue ?: "Loading..") } } diff --git a/examples/shared/src/commonMain/kotlin/EncryptionViewModel.kt b/examples/shared/src/commonMain/kotlin/EncryptionViewModel.kt index c5a1b2e..6fe8f64 100644 --- a/examples/shared/src/commonMain/kotlin/EncryptionViewModel.kt +++ b/examples/shared/src/commonMain/kotlin/EncryptionViewModel.kt @@ -10,4 +10,10 @@ class EncryptionViewModel { suspend fun encryptedValue(): String { return Evervault.shared.encrypt("Foo") as String } + + suspend fun decryptedValue(): String { + val encrypted = Evervault.shared.encrypt("Foo") as String + val decrypted = Evervault.shared.decrypt("", mapOf("data" to encrypted)) as Map + return decrypted["data"] as String + } } From 46c7042c26583c9ab13ccaecfc50538b88b0c7ab Mon Sep 17 00:00:00 2001 From: Jake Grogan Date: Tue, 29 Aug 2023 12:20:50 +0100 Subject: [PATCH 10/14] chore: test different types --- .../com/evervault/sdk/core/HttpRequestTest.kt | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt index dde6628..7b844fc 100644 --- a/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt +++ b/evervault-core/src/commonTest/kotlin/com/evervault/sdk/core/HttpRequestTest.kt @@ -31,8 +31,21 @@ data class TokenData( ) @Serializable -data class Name( - var name: String? +data class RawData( + var stringData: String?, + var numberData: Int?, + var floatData: Double?, + var booleanData: Boolean?, + var arrayData: ArrayList?, +) + +@Serializable +data class EncryptedTestData( + var stringData: String?, + var numberData: String?, + var floatData: String?, + var booleanData: String?, + var arrayData: ArrayList?, ) class HttpRequestTest { @@ -50,7 +63,7 @@ class HttpRequestTest { private val appUuid = getenv("VITE_EV_APP_UUID") private val teamUuid = getenv("VITE_EV_TEAM_UUID") - private suspend fun encryptData(url: Any, data: Name): Name { + private suspend fun encryptData(url: Any, data: RawData): EncryptedTestData { val task = coroutineScope { async { try { @@ -66,7 +79,7 @@ class HttpRequestTest { } } val responseBody = response.bodyAsText() - return@async json.decodeFromString(responseBody) + return@async json.decodeFromString(responseBody) } catch (error: Error) { throw error } @@ -116,16 +129,41 @@ class HttpRequestTest { context = "default" ) - val data = Name(name="Bob") + val data = RawData( + stringData = "Bob", + numberData = 1, + floatData = 1.5, + booleanData = true, + arrayData = arrayListOf("hello", "world"), + ) // Encrypt some data val encrypted = encryptData(ConfigUrls().apiUrl, data) // Get a run token val token = createClientSideToken(ConfigUrls().apiUrl, encrypted) val decrypted = http.decryptWithToken(token.token, encrypted) as Map - assertEquals( - decrypted["name"], + decrypted["stringData"], "Bob" ) + + assertEquals( + decrypted["numberData"], + 1.0 + ) + + assertEquals( + decrypted["floatData"], + 1.5 + ) + + assertEquals( + decrypted["booleanData"], + true + ) + + assertEquals( + decrypted["arrayData"], + arrayListOf("hello", "world") + ) } } \ No newline at end of file From 7c69feb33c114a7fada96ae0d190f84952d69d99 Mon Sep 17 00:00:00 2001 From: Jake Grogan Date: Tue, 29 Aug 2023 13:40:28 +0100 Subject: [PATCH 11/14] chore: update README --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index d138a4f..47bf427 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,22 @@ val encryptedPassword = Evervault.shared.encrypt("Super Secret Password") The `encrypt` method returns an `Any` type, so you will need to safely cast the result based on the data type you provided. For Boolean, Numerics, and Strings, the encrypted data is returned as a String. For Arrays, Lists and Maps, the encrypted data maintains the same structure but is encrypted (except that Arrays become Lists). For ByteArray, the encrypted data is returned as encrypted ByteArray, which can be useful for encrypting files. +### Decrypting Data + +You can use the `decrypt` method to decrypt data previously encrypted through Evervault. To perform decryptions you will be required to provide a Client Side Token. The token is a time bound token for decrypting data. The token can be generated using our backend SDKs for use in our client-side SDKs. The payload provided to the `decrypt` method must be the same as the payload used to generate the token. + + +Here's an example of decrypting data. + +```kotlin +val encrypted = Evervault.shared.encrypt("John Doe") + +val decrypted = Evervault.shared.decrypt("", mapOf("name" to encrypted)) as Map +println(decrypted["name"]) // Prints "John Doe" +``` + +The `decrypt` function will return `Any`, however this can be cast to `Map`. The data argument must be a map. + ## Sample App The Evervault Kotlin Multiplatform SDK Package includes a sample app, located in the `examples` directory. The sample app consist of a `shared` module, which contains the Evervault Kotlin Multiplatform SDK, and an `android` module, which contains the sample app. From b6fa9a707796343c18c42672334dd30f67a03c4a Mon Sep 17 00:00:00 2001 From: Jake Grogan Date: Tue, 29 Aug 2023 14:08:57 +0100 Subject: [PATCH 12/14] Add user agent header --- .../src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt index 1262a77..60f76cd 100644 --- a/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt +++ b/evervault-core/src/commonMain/kotlin/com/evervault/sdk/core/HttpRequest.kt @@ -24,6 +24,7 @@ internal class HttpRequest(private var config: HttpConfig) { private var httpClient = HttpClient { defaultRequest { header(HttpHeaders.ContentType, "application/json") + header(HttpHeaders.UserAgent, "Evervault/Kotlin") } } From aa38ee83dfeb33cb9d7cffad8306aff051bb2ec2 Mon Sep 17 00:00:00 2001 From: Jake Grogan Date: Tue, 29 Aug 2023 17:39:48 +0100 Subject: [PATCH 13/14] Bump version --- build.gradle.kts | 2 +- evervault-core/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4d9d3f1..d026da5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ buildscript { } } group = "com.evervault.sdk" -version = "1.0" +version = "1.1" plugins { kotlin("jvm") version "1.8.21" apply false id("org.jetbrains.kotlin.android") version "1.8.20" apply false diff --git a/evervault-core/build.gradle.kts b/evervault-core/build.gradle.kts index be940ea..effbddd 100644 --- a/evervault-core/build.gradle.kts +++ b/evervault-core/build.gradle.kts @@ -11,7 +11,7 @@ plugins { val ktorVersion = "2.3.1" group = "com.evervault.sdk" -version = "1.0" +version = "1.1" kotlin { jvm { From be5176a45f595790287671ecd3da1b564c682291 Mon Sep 17 00:00:00 2001 From: Jake Grogan Date: Tue, 29 Aug 2023 17:45:27 +0100 Subject: [PATCH 14/14] add sample app version bump --- examples/android/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/android/build.gradle.kts b/examples/android/build.gradle.kts index d9bd0f1..e30a59d 100644 --- a/examples/android/build.gradle.kts +++ b/examples/android/build.gradle.kts @@ -12,7 +12,7 @@ android { minSdk = 26 targetSdk = 33 versionCode = 1 - versionName = "1.0" + versionName = "1.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables {