Sample realm sync with partition keys for different types (public, private, group)

Is there a good sample realm sync project with partition keys for different types (public, private, group) with multiple collections?

The current realm tutorial discussed a sample with collection user, project, task.But it does not discuss how to set up collections with different partition keys so that users can be associated with certain realms for task and project (public, private, group) . A practical “real” sample would be helpful to design proper realm sync.

@jerry_he we are working on v2 of the tutorial right now and should be released shortly. But to answer your question you will need to open multiple realms and name them different realm references with different sync configurations that have different partitionKeys - for example:

let user = app.currentUser()
let partitionValue = "PUBLIC"
publicRealm = try! Realm(configuration: user.configuration(partitionValue: partitionValue)) 

...

let user = app.currentUser()
let partitionValue = "user=1231"
privateRealm = try! Realm(configuration: user.configuration(partitionValue: partitionValue))

...

let user = app.currentUser()
let partitionValue = "family=851024"
sharedRealm = try! Realm(configuration: user.configuration(partitionValue: partitionValue))

Docs here:
https://docs.mongodb.com/realm/sync/partitioning/

1 Like

Hi Ian,

Thanks for note. The above code is good. How to make sure some/current user can access realm with partitionValue = “family=851024”? We need to do something in rules/filters to make permission. That is the part l am struggling. We can setup any partition value, but in order for someone to access that realm, some permission rules/filers need to be in place. Wonder you have good example?

Also for v2 of the tutorial, is something already in somewhere so that we can have earlier access?

Thanks,
JH

@jerry_he One way would be set custom user data that includes metadata fields about what partitions the user has read or write access to - https://docs.mongodb.com/realm/users/define-custom-user-data/index.html

You can then define the permissions like so in the Sync Permissions UI
https://docs.mongodb.com/realm/sync/rules/#id5

For instance this could be the syntax for read permissions

{
"%%user.custom_data.readPartitions" : "%%partition"
}

@Ian_Ward This was the missing link that I was looking for. Thanks! The key here is that the custom_data is modified by the server (through MongoDB CRUD functions), not the user on the client side, to store application specific data about the user. This should not be confused with the user_data metadata that is set on the User object through a JWT authentication token.

It looks like with android this doesn’t work. I’m getting an error java.lang.IllegalArgumentException: Configurations cannot be different if used to open the same file.

        user = app.currentUser()

        val configStore = SyncConfiguration.Builder(user!!, "store_id=1234")
                .waitForInitialRemoteData()
                .build()
        Realm.getInstanceAsync(configStore, object : Realm.Callback(){
            override fun onSuccess(realm: Realm) {
                this@MainActivity.realmStore = realm
                
            }
            override fun onError(exception: Throwable) {
                super.onError(exception)
            }
        })

val configGlobal = SyncConfiguration.Builder(user!!, "global")
                        .waitForInitialRemoteData()
                        .build()
                Realm.getInstanceAsync(configGlobal, object : Realm.Callback(){
                    override fun onSuccess(realm: Realm) {
                        this@MainActivity.realmGlobal = realm
                    }
                    override fun onError(exception: Throwable) {
                        super.onError(exception)

                    }
                })

So every time a different user wants to share a document, I have to create a new realm? And if those 'share privileges ’ are then changed (e.g. the user that shared a document with me decides not to share it with me anymore), then that triggers a client reset?

@Anthony_CJ
The easiest way to think about it is that all objects in a realm share the same permissions across the entire realm. So a user that can write to document A in realm X, can also write to document B in realm X.

if you want a group of users to share the same permissions for an object or set of objects, then you will need a realm with those permissions set.

In your case - that sounds like you’ll need to make a realm per document. Then keep a list of partitions on the users custom_data and update that to apply/remove permissions to realms.
Make the _partition something like docID=abc-1234-xyz-987.
That way you will never need to update the partition and trigger a reset.
Having lots of realms is common and expected.

Okay that makes sense. So if permissions are changed for a document/realm, e.g. a user is removed, then what happens with the sync for said realm?

You’d not need to change the permissions on the realm per se.

You’d remove the string that represents the realm/partition from the list of partitions that the user can access on their custom_data.

The document/realm remains unchanged. The permissions remain unchanged. The user would effectively lose their access because the partition of the realm does not appear in the list of realms they can access.

Using the approach outlined above, the idea is that when a realm sync is attempted by a user, the custom_data property containing a list of partitions they are allowed to access is interrogated - and access is granted/denied on that basis.

You would need to manage the access to this document manually by adding and removing the partition string from any users custom_data

1 Like

Thanks @Benjamin_Storrier. So if I end up having a realm for each document, then whenever users add e.g. user_123 to the shared users for that document, and a new realm is created, does it automatically then sync with user_123 because of the partition key?

@Benjamin_Storrier Your idea of using custom data to store the list of realms (or partitions) that a user can read and/or write to is brilliant. This is a very elegant solution that does not trigger Realm resets. I assume that a server side function would maintain this custom data, so that a user could not spoof the system by writing into his/her own custom data to grant themselves permission to something. In one stroke, you sort of solved the whole permission issue that I have been struggling with for the past two months. Thank you!

Haha @Richard_Krueger - awesome - I thought that was where @Ian_Ward was heading all along - so there you go :slight_smile:

Here is what I had planned on using as the read permissions expression.
I’d use the same structure for write permissions.

I haven’t tested it yet because I’m still in dev mode and currently getting getting a big red error in the sync panel that does not allow me to terminate sync which is rather frustrating.

{
  "$or": [
      "%%partition": "%%user.id",
      "%%partition": {
          "%in": "%%user.custom_data.readPermissions"
      }
      ]
}

Do you think this approach would work?

B

2 Likes

@Anthony_CJ
You would need to handle the addition and deletion of these values on the user data yourself.
Preferably on a server somewhere.
You don’t want users having write access to these values or they can spoof them to gain access to things they shouldn’t.

@Benjamin_Storrier I assume via Triggers would be a way to achieve this yeah? But TBH I don’t understand why the user can’t do this. If the user has permission to update their document (in this use case, add or remove other users to the list of who has access to that document), why couldn’t it be done that way?

And back to my other question, regardless of how the update is made, does updating who has access to that Realm (either adding access or removing access), automatically sync that realm to the added users and remove access for the users who no longer have access?

Hi @Anthony_CJ

I assume triggers would be a way to do this. But I am not using them so I can’t offer help there.
I can’t speak as to the architectural reasons for why it is the way it is sorry.

The users who have access to the new realm would have to open the realm and then it would sync. The updates to custom_data merely grant the permissions.

As a note for those who seek to control permissions using custom_data, here are a few tips.

assuming your custom_data collection is structured like this, we’ll call it ‘RealmPermissions’:

{
  "title": "RealmPermissions",
  "bsonType": "object",
  "required": [
    "_id",
    "_partition",
    "userId"
  ],
  "properties": {
    "_id": {
      "bsonType": "string"
    },
    "_partition": {
      "bsonType": "string"
    },
    "userId": { 
      "bsonType": "string"
    },  // links the user to custom_data
    "readPermissions": {
      "bsonType": "array",
      "items": {
        "bsonType": "string"
      }  // contains partition values you want a user to read from
    },
    "writePermissions": {
      "bsonType": "array",
      "items": {
        "bsonType": "string"
      } // contains partition values you want a user to be able to write to
    }
  }
}

Once you have a user and their custom data linked, you can use a permission config like this:

Reading:

{
    "%%partition": {
        "%in": "%%user.custom_data.readPermissions"
    }
}

Writing:

{
    "%%partition": {
        "%in": "%%user.custom_data.writePermissions"
    }
}

Then you can add and remove permissions from these arrays on the RealmPermissions for fine grained control over a user’s access.

A little gotcha is that custom_data easily becomes stale and a newly added permission won’t necessarily be in the custom data when a user goes to access a realm if it has been recently created.
So, to solve this, I have prefaced all Realm.open(...) calls with calls to Realm.User.refreshCustomData()

So far so good.

Happy realming.

B

@Benjamin_Storrier I think that write access to the custom data is a server side privilege only, so that should in theory prevent spoofing.

@Richard_Krueger
You could actually make the collection that houses the custom_data a realm and allow certain users to modify that realm. But that’s potentially a house of cards :slight_smile:

@Benjamin_Storrier my sense is that would be security risk. My preference would be to have the custom data only accessible by server functions. The minute you open the door to god like powers at the client side, you are opening pandoras box so to speak.

1 Like