Robust Permissions, for the Power User

With Partial Sync, it's probable that your entire user base needs to access one realm. This brings the concept of Object Level Permissions and "Async Server Side Permissioning". This is an advanced power user feature that allows you to inject logical permissions to operations on objects. This is a great way to put in custom, advanced, or 3rd party logic for specific objects.

Async Serverside Permissions

Sometimes we want permissions to exist on the server instead. Similar to the login functionality, Async Server Side Permissions allow you to reject synced changes inflight.

const ros = new ROS({
    ... other config values
    permissions: {
        'realmPath/2:Car': function(incoming, current, done){
            // call done() without any parameters, and the change is accepted 
            // call done(err) with an error parameter, the change is reject
        })
    }
})

The permissions config value takes in an dictionary of regex:ObjectTypeName

  • The regex string is the name of the realmPath (without the host or port)
  • The ObjectName is the name of the Realm Object

What is the incoming object?

The incoming object describes both the userId, the operation, and object as it would look if the application accepted

function(incoming, current, done) {
    console.log(incoming.userId) // the userId that is attempting to make the change
    console.log(incoming.operation) // various information about what the client is attempting to change
    // ^ also contains information about the Operational-Transformation if your interested
    console.log(incoming.objects) // this is what the object would look like if you accepted it
}

Example of using Async Server Side Permissions

const BankApi = require('bank-api')

function(incoming, current, done) {    
    const userId = incoming.userId
    const bankAccount = incoming.object
    const operation = incoming.operation

    const increaseAmount = operation.type === 'INCR'
    const amount = operation.value === '200000'

    bankApi(userId).canIncrease(amount)
        .then((canIncrease) => {
            done() // incoming changes are now accepted
        })
        .catch(err => {
            done(err) // incoming changes are rejected
            // changes will be reverted
        })
}

Client Side Optimistic vs Pessimistic Writes

When you have an Async Serverside Permission, we are bound to the latency of our network. There are instances where you'd like to react to it optimistically vs pessmistically.

Optimistically

If you are attempting to mutate data that will be rejected by the server. You will see a much quieter error handling. This keeps the app much more "responsive". This doesn't mean that error or the correct model reversion isn't correct.

This is best shown in the code below as the notification block is fired twice in the event that the change is rejected. We call this optimistically because the code assumes more likely that the app will succeed with the write operation.

Upon failure, the server will "revert" the change. Depending on your latency you will probably see a quick blip in your UI. This is similar behavior on apps like Facebook or Instagram

let mySyncedRealm = ...

mySyncedRealm.filter(Car.self).addNotificationBlock({ changeEvent in
    print("\(changeEvent.remote?.isServer)") // this will fire TWICE!
    pinrt("\(changeEvent.remote?.error)") // this will contain the specific error from the server
})

//let attempt to write something that will fail on the server side
try! mySyncedRealm.write {
    bankAccount.amount = bankAccount.amount + 200000
}

Pessimistically

With this, we depend on an asynchronous write. This is a good place to ensure that you check if the server has acknowledged the change.

mySyncedRealm.writeRemoteAsync { 
    bankAccount.amount = bankAccount.amount + 200000
} { (err: Error?) in
    guard let error = err else { return }
    print("Uh oh the server likely rejected your change! \(err.localizedDescription)")
}

This is a great place to show a progress indicator. before and after the block is fired. In the pessimistic way, the object is not mutated until the completion block fails or succeeds.

Important Considerations

With great power comes great responsibility! Even though our Robust Serverside Permissioning is powerful, take care to understand that it can become a bottleneck for real-time events if your custom function resolves slowly.

If modification events are being fired extremely frequently and requires a check, a queue of objects checks can build up. Take care to use Robust Permissions on objects that do not aggressively change.

No other clients will sync data that has not passed a permission checkpoint.

results matching ""

    No results matching ""