Object permissions


#1

Hello,
Something with my model doesn’t work, maybe I’ve misunderstood how object permissions works.
Here is my model :

class Place: Object {
    
    @objc dynamic var uuid: String = UUID().uuidString
    @objc dynamic var name: String = ""
    let permissions = List<Permission>()
    let groups = List<Group>()
    
    override static func primaryKey() -> String? {
        return "uuid"
    }
}

class Group: Object {
    
    @objc dynamic var uuid: String = UUID().uuidString
    @objc dynamic var name: String = ""
    let permissions = List<Permission>()

    override static func primaryKey() -> String? {
        return "uuid"
    }
}

Let’s say I have 2 users in one place object

let place = Place()

let permissionUser1 = realm.object(ofType: PermissionUser.self, forPrimaryKey: user1.id)!
let permission1 = place.permissions.findOrCreate(forRole: user1.role!)
permission1.canRead = true
permission1.canUpdate = true

let permissionUser2 = realm.object(ofType: PermissionUser.self, forPrimaryKey: user2.id)!
let permission2 = place.permissions.findOrCreate(forRole: user2.role!)
permission2.canRead = true
permission2.canUpdate = true

At this point, everything works. Users 1 & 2 can only see this place.
Now, let’s say there are two groups in this place and each user is in a group

let group1 = place.groups[0]
let group2 = place.groups[1]

let permissionUser1 = realm.object(ofType: PermissionUser.self, forPrimaryKey: user1.id)!
let permission1 = group1.permissions.findOrCreate(forRole: user1.role!)
permission1.canRead = true
permission1.canUpdate = true

let permissionUser2 = realm.object(ofType: PermissionUser.self, forPrimaryKey: user2.id)!
let permission2 = group2.permissions.findOrCreate(forRole: user2.role!)
permission2.canRead = true
permission2.canUpdate = true

I expect that user1 can only see “group1” and user2 “group2”, just as the place but it doesn’t.
They both can see all the groups.
So, what am I doing wrong and what is the solution?

Thanks for your help.


#2

@benjaminledet ead permissions are transitive

place links to both groups, so any users which have read permission for place can also read both groups


#3

Ok I understand, so in your opinion, how can I achieve what I want to do? It’s useless to have a list of permission in Group?


#4

@benjaminledet If a user has read permissions to a parent object then they also implicitly have read access to all child objects. If this is not the behavior you want then I would suggest reversing the relationship in your schema where a Group refers a specific Place object. Then applying permissions to each group.


Child Object Permissions
#5

I will try that. Thank you for your help, I appreciate.

One last thing,

Would it not be a good idea to have a sort of a hierarchy of permissions like I wanted to achieve?
I mean, the point of realm’s permission system is to restrict access, as the getting started tutorial (part 3) says :

In the previous tutorial we used partial sync to synchronize only projects owned by the current user. However, nothing prevented a malicious user from seeing another user’s projects by subscribing with a query that matches a broader set of Project instances.

To address this issue we will instead use Realm’s permission system to limit access to each Project instance so only the user that created it can read, modify, or delete it.

In my case, I can workaround this with your solution, but there must be cases where you can’t.
So, if you can restrict access for the first object class with object permissions but for subclasses you have to do it with queries, this is not really safe isn’t it?


#6

@benjaminledet Right now we would recommend structuring the data around the permissions system. For instance, you could have as the root object a User - and then have all objects the user has access to as links off that object. Then when you run your partial sync query you would pass your userId as the query string.

You should think of it like a JSON document and you would apply permissions on that document as a whole.

In the future we have discussed having asynchronous links that would allow for the behavior you have described but we have not started it yet.


#7

I understand, thank you.


#8

Do you still recommend structuring your data this way around a root user object? I had a lot of problems using this approach in Realm Java. The main problem is that when all user data is structured around the root user object, you need to download that user the first time the user uses the app on a new device. This will cause realm to download everything recursively and in java, progress notifications are broken, so it can take a very long time. For 3MB worth of data, it would take 10+ seconds with no progress notifications.


#9

@Sipe 3MB of data should not take that long - how fast is your connection? I will say that we are close to launching LIMIT functionality in query-based sync which will allow for pagination of results.

Feel free to open a ticket at support.realm.io if you think there is something is going wrong.


#10

I just download one object, the root user object, which in turn contains all user data. But Realm Java downloads all data recursively, which does take a lot of time. My data structure is nested in a way that there are 3 levels, each containing more data. this case, the first level has about 800 items, the second has about 4800 and the third 30000. The internet connection isn’t the issue. It is slow regardless if I am using the 4G connection on the phone or if I am using the emulator attached to a 50mbit WIFI. This, in combination that I did not get progress notifications (see https://github.com/realm/realm-java/issues/6005). Made the root user approach impossible to use for me, hence the question if it was a recommended approach :wink:

I have now changed the architecture away from this structure and instead of having a root object I am applying permissions directly to all shareable objects instead (in this case the 800 root items that a user has). This is a much more flexible approach when it comes to sharing, and it does seem to have better performance.

However, it has downsides. Since I need to share the same model in local and synced realms, I need to add permissions to the local realm configuration. I got a tip from @mgeerling on how to do this by adding the ObjectPermission module to the local configuration. In addition I had to find out how to add these properties in the local realm migration.

If I run into performance issues again I’ll open an issue :wink: