Skip to content

Commit

Permalink
DOCSP-34240: Document metadata encryption in Kotlin SDK (#3074)
Browse files Browse the repository at this point in the history
## Pull Request Info

Update existing [Encrypt a
Realm](https://www.mongodb.com/docs/realm/sdk/kotlin/realm-database/realm-files/encrypt-a-realm/)
and [Connect to App
Services](https://www.mongodb.com/docs/realm/sdk/kotlin/app-services/connect-to-app-services-backend/)
pages with info on App metadata encryption.

### Jira

- https://jira.mongodb.org/browse/DOCSP-34240

### Staged Changes

- [Encrypt a Realm - Kotlin
SDK](https://preview-mongodbcbullinger.gatsbyjs.io/realm/docsp-34240-encrypt-app-metadata/sdk/kotlin/realm-database/realm-files/encrypt-a-realm/#std-label-kotlin-encryption-and-device-sync)
- [Connect to Atlas App Services - Kotlin
SDK](https://preview-mongodbcbullinger.gatsbyjs.io/realm/docsp-34240-encrypt-app-metadata/sdk/kotlin/app-services/connect-to-app-services-backend/#encrypt-app-metadata)

### Reminder Checklist

If your PR modifies the docs, you might need to also update some
corresponding
pages. Check if completed or N/A.

- [x] Create Jira ticket for corresponding docs-app-services update(s),
if any
- [x] Checked/updated Admin API
- [x] Checked/updated CLI reference

### Review Guidelines


[REVIEWING.md](https://github.com/mongodb/docs-realm/blob/master/REVIEWING.md)
  • Loading branch information
cbullinger authored Nov 13, 2023
1 parent 870c6ff commit 2d6438a
Show file tree
Hide file tree
Showing 14 changed files with 239 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,45 @@ class AppClientTest: RealmTest() {
}

@Test
fun setCustomHttpHeadersTest() {
val myEncryptionKey = getEncryptionKey()
val config1 =
// :snippet-start: configure-app-client
fun configureAppClient() {
val config =
// :snippet-start: configure-app-client
// Creates an App with custom configuration values
AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID
// Specify your custom configuration values
.appName("my-app-name")
.appVersion("1.0.0")
.baseUrl("http://localhost:9090")
.build()
// :snippet-end:
assertEquals(config.appName, "my-app-name")
assertEquals(config.baseUrl, "http://localhost:9090")
assertEquals(config.appVersion, "1.0.0")
}

@Test
fun encryptAppMetadata() {
val myEncryptionKey = getEncryptionKey()
val config =
// :snippet-start: encrypted-app-client
AppConfiguration.Builder(YOUR_APP_ID)
// Specify the encryption key
.encryptionKey(myEncryptionKey)
.build()
// :snippet-end:
assertTrue(config.encryptionKey.contentEquals(myEncryptionKey))
}

@Test
fun setCustomHttpHeadersTest() {
val config1 = AppConfiguration.Builder(YOUR_APP_ID)
.appName("my-app-name")
.build()
val config2 =
// :snippet-start: set-custom-http-headers
AppConfiguration.Builder(YOUR_APP_ID)
.authorizationHeaderName("MyApp-Authorization")
.customRequestHeaders {
put("X-MyApp-Version", "1.0.0")
}
.customRequestHeaders { put("X-MyApp-Version", "1.0.0") }
.build()
// :snippet-end:
assertEquals(config1.authorizationHeaderName, "Authorization")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ package com.mongodb.realm.realmkmmapp

import io.realm.kotlin.Realm
import io.realm.kotlin.RealmConfiguration
import io.realm.kotlin.ext.query
import io.realm.kotlin.internal.platform.runBlocking
import io.realm.kotlin.mongodb.App
import io.realm.kotlin.mongodb.Credentials
import io.realm.kotlin.mongodb.sync.SyncConfiguration
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class EncryptARealmTest : RealmTest() {
@Test
Expand All @@ -15,39 +21,62 @@ class EncryptARealmTest : RealmTest() {
// generate a new 64-byte encryption key
val key = ByteArray(64)
if (seed != null) {
// If there is a seed provided, create a random number with that seed and fill the byte array with random bytes
// If there is a seed provided, create a random number with that seed
// and fill the byte array with random bytes
Random(seed).nextBytes(key)
} else {
// fill the byte array with random bytes
Random.nextBytes(key)
}
return key
}
// :remove-start:
assertTrue(getRandomKey().size == 64)
val generatedKey = getRandomKey()
// :remove-end:

runBlocking {
// Create the configuration
val config = RealmConfiguration.Builder(
setOf(Frog::class))
val config = RealmConfiguration.Builder(setOf(Frog::class))
// :remove-start:
.deleteRealmIfMigrationNeeded()
.directory("tmp/encrypted")
// :remove-end:
// specify the encryptionKey
.encryptionKey(getRandomKey())
// Specify the encryption key
.encryptionKey(generatedKey)
.build()
// :remove-start:
Realm.deleteRealm(config)
// :remove-end:
// Open a realm with the encryption key.
// Open the realm with the configuration
val realm = Realm.open(config)
Log.v("Successfully opened realm:" +
realm.configuration.name
)

Log.v("Successfully opened encrypted realm: ${realm.configuration.name}")
// :remove-start:
assertEquals(generatedKey, realm.configuration.encryptionKey)
realm.close()
Realm.deleteRealm(config)
// :remove-end:
}
// :snippet-end:
}

@Test
fun encryptSyncedRealm() {
val generatedKey = getEncryptionKey()
val app = App.create(yourFlexAppId)
runBlocking {
val user = app.login(Credentials.anonymous())
// :snippet-start: encrypt-synced-realm
val syncConfig = SyncConfiguration.Builder(user, setOf(Frog::class))
.initialSubscriptions { realm ->
add(realm.query<Frog>())
}
// Specify the encryption key
.encryptionKey(generatedKey)
.build()
val realm = Realm.open(syncConfig)
Log.v("Successfully opened encrypted realm: ${realm.configuration.name}")
// :snippet-end:
assertEquals(generatedKey, realm.configuration.encryptionKey)
realm.close()
user.delete()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Creates an App with custom configuration values
AppConfiguration.Builder(YOUR_APP_ID) // Replace with your App ID
// Specify your custom configuration values
.appName("my-app-name")
.encryptionKey(myEncryptionKey)
.appVersion("1.0.0")
.baseUrl("http://localhost:9090")
.build()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
AppConfiguration.Builder(YOUR_APP_ID)
// Specify the encryption key
.encryptionKey(myEncryptionKey)
.build()
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
AppConfiguration.Builder(YOUR_APP_ID)
.authorizationHeaderName("MyApp-Authorization")
.customRequestHeaders {
put("X-MyApp-Version", "1.0.0")
}
.customRequestHeaders { put("X-MyApp-Version", "1.0.0") }
.build()
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ fun getRandomKey(seed: Long? = null): ByteArray {
// generate a new 64-byte encryption key
val key = ByteArray(64)
if (seed != null) {
// If there is a seed provided, create a random number with that seed and fill the byte array with random bytes
// If there is a seed provided, create a random number with that seed
// and fill the byte array with random bytes
Random(seed).nextBytes(key)
} else {
// fill the byte array with random bytes
Expand All @@ -14,15 +15,11 @@ fun getRandomKey(seed: Long? = null): ByteArray {

runBlocking {
// Create the configuration
val config = RealmConfiguration.Builder(
setOf(Frog::class))
// specify the encryptionKey
.encryptionKey(getRandomKey())
val config = RealmConfiguration.Builder(setOf(Frog::class))
// Specify the encryption key
.encryptionKey(generatedKey)
.build()
// Open a realm with the encryption key.
// Open the realm with the configuration
val realm = Realm.open(config)
Log.v("Successfully opened realm:" +
realm.configuration.name
)

Log.v("Successfully opened encrypted realm: ${realm.configuration.name}")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
val syncConfig = SyncConfiguration.Builder(user, setOf(Frog::class))
.initialSubscriptions { realm ->
add(realm.query<Frog>())
}
// Specify the encryption key
.encryptionKey(generatedKey)
.build()
val realm = Realm.open(syncConfig)
Log.v("Successfully opened encrypted realm: ${realm.configuration.name}")
8 changes: 8 additions & 0 deletions source/sdk/dotnet/realm-files/encrypt-a-realm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ integrity using a :wikipedia:`hash-based message authentication code

.. include:: /includes/encrypt-use-strong-cryptographic-hash.rst

.. note:: Cannot Encrypt an Existing Unencrypted Realm

You must encrypt a realm the first time you open it.
If you try to open an existing unencrypted realm using a configuration
that contains an encryption key, Realm throws an error.

.. update with writeCopy API when available

Considerations
--------------

Expand Down
11 changes: 11 additions & 0 deletions source/sdk/flutter/realm-database/realm-files/encrypt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ integrity using a :wikipedia:`hash-based message authentication code

.. include:: /includes/encrypt-use-strong-cryptographic-hash.rst

.. note:: Encrypt a Realm on Open or Copy Unencrypted Realm

You must encrypt a realm the first time you open it.
If you try to open an existing unencrypted realm using a configuration
that contains an encryption key, Realm throws an error.

Alternatively, you can copy the unencrypted realm data to a new
encrypted realm using the
:flutter-sdk:`Realm.writeCopy() <realm/Realm/writeCopy.html>` method.
Refer to :ref:`<flutter-copy-data-into-new-realm>` for more information.

Considerations
--------------

Expand Down
27 changes: 23 additions & 4 deletions source/sdk/kotlin/app-services/connect-to-app-services-backend.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,39 @@ Configure the App Client

You can add optional arguments to the ``AppConfiguration`` for more
granular control of your app connection details, such as custom
timeouts for connections and keys for local encryption.
request headers and keys for local encryption.

To control the additional configuration options, use the
`AppConfiguration.Builder
<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/-builder/index.html>`__ and call the ``.build()`` method to pass a configuration
object:
<{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/-builder/index.html>`__
and call the ``.build()`` method to pass a configuration object:

.. literalinclude:: /examples/generated/kotlin/AppClientTest.snippet.configure-app-client.kt
:language: kotlin

.. include:: /includes/multiple-app-client-details-and-app-config-cache.rst

.. _kotlin-encrypt-app-metadata:

Encrypt App Metadata
~~~~~~~~~~~~~~~~~~~~

When you connect to App Services, Realm creates additional metadata files on
a device. For more information about these metadata files, refer to
:ref:`<kotlin-realm-database-internals>`.

You can encrypt the metadata that App Services stores on client devices,
similar to how you :ref:`<kotlin-encrypt-a-synced-realm>`.

To encrypt App metadata, pass your encryption key to the
`encryptionKey <{+kotlin-sync-prefix+}io.realm.kotlin.mongodb/-app-configuration/encryption-key.html>`__
property when you initialize the App:

.. literalinclude:: /examples/generated/kotlin/AppClientTest.snippet.encrypted-app-client.kt
:language: kotlin

Set Custom HTTP Headers
-----------------------
~~~~~~~~~~~~~~~~~~~~~~~

.. versionadded:: 1.11.0

Expand Down
Loading

0 comments on commit 2d6438a

Please sign in to comment.