Query-based Sync is not recommended. For applications using Realm Sync, we recommend Full Sync. Learn more about our plans for the future of Realm Sync here.
The Query-based sync permission system is based on role-based access control lists and as such, permissions are assigned to roles, not users directly. This means that a user's current privileges are the sum of all roles they belong to.
The permission system recognizes three levels of permissions: Realm, Class, and Object-level. The hierarchy works like this: If a user does not have higher-level Read
access, they cannot see anything on the lower levels. For example, if a user lacks Read
access at the Realm-level, they cannot see any data in the Realm, even if they have Read
at the class or object-level. However, just because they have higher-level Read
access does not mean they can see everything on the lower level -- just that they can see anything at all. In this way, the app developer can decide for themselves what granularity they want for permissions in their data model.
Query-based Sync Permissions were formerly referred to as Fine-grained Permissions
When first starting, any user who can connect to a Realm file can make any change to the data. This is designed to allow you to get up and running quickly, syncing data, and iterating on your data models. Once you are ready to start implementing access control, an admin can define access control lists inside the Realm file, either on individual objects or whole classes.
Realm is an offline-first database, but permission checks are performed by the server. When a user makes a change to their local database, it will eventually be uploaded to the server, but if the server determines that the user tried to make changes that they were not allowed to make, the server will refuse to integrate it and instruct the client to revert the change. This all happens transparently from the perspective of the app.
A reversal of an illegal change looks like any other change coming from the server.
To minimize the risk of a user unintentionally making illegal changes while offline, the permission metadata is replicated to each client for offline access, and can be queried in the app for UI purposes. No permission checks are done automatically by the client -- the app has to manually query the permission metadata.
The query-based sync permission model is based on roles. Roles are granted permissions, not users, but users, in turn, are assigned roles. This way, a users privileges are the sum of all roles they are part of.
A user can be a member of as many roles as needed. The Realm Object Server automatically adds all users to the special role called everyone
when they first connect.
A unique role is also automatically created for every user in the system when they first connect: __User:<syncIdentity>
. The user is also automatically a member of this role.
In a new Realm file, the everyone
role has all permissions enabled.
When you as a developer are ready to integrate permissions in your app, you would usually define a new administrator
role which has yourself as a member, and then reduce the privileges of the everyone
role. Note that an administrator
role is different than an admin
user on the Realm Object Server.
When assigning a role to a User, the user is added as a member of the Role object instead of the role being attached to the user. Since the Role object is just a normal Realm object, it can be found, queried and manipulated the same way as other objects:
// List all roleslet roles = realm.objects(PermissionRole.self)// You can query roles in order to find a specific onelet role = realm.objects(PermissionRole.self).filter("name = %@", "my-role").first// Making changes to a Role requires a write transactionlet user = getUserId()try! realm.write {role.users.append(user)}// So does creating a new roletry! realm.write {let newRole = realm.create(PermissionRole.self, value: ["my-new-role"])}
// List all rolesRLMResults<RLMPermissionRole *> *roles = [RLMPermissionRole allObjectsInRealm:realm];// You can query roles in order to find a specific oneRLMPermissionRole *role = [RLMPermissionRole objectsInRealm:realm where:@"name = %@", @"my-role"].firstObject;// Making changes to a Role requires a write transactionRLMPermissionUser *user = getUserId();[realm transactionWithBlock:^{[role.users addObject:user];}];// So does creating a new role[realm transactionWithBlock:^{[RLMPermissionRole createInRealm:realm withValue:@[@"my-new-role"]];}];
// List all rolesRealmResults<Role> roles = realm.getRoles();// You can query roles in order to find a specific oneRole role = realm.getRoles().where().equalTo("name", "my-role").findFirst();// Making changes to a Role requires a write transactionString user = getUserId();realm.executeTransaction((Realm r) -> {role.addMember(user);});// So does creating a new rolerealm.executeTransaction((Realm r) -> {r.insert(new Role("my-new-role"));});
// List all roleslet roles = realm.objects(Realm.Permissions.Role);// You can query roles in order to find a specific onelet role = realm.objects(Realm.Permissions.Role).filtered(`name = 'my-role'`)[0];// Making changes to a Role requires a write transactionlet user = getUser();realm.write(() => {role.members.push(user);})// So does creating a new rolerealm.write(() => {realm.create(Realm.Permissions.Role, { name: "my-new-role" });});
// List all rolesvar roles = realm.All<PermissionRole>();// You can query roles in order to find a specific onevar role = realm.All<PermissionRole>().FirstOrDefault(r => r.Name == "my-role");// Making changes to a Role requires a write transactionrealm.Write(() =>{var user = PermissionUser.Get(realm, "some-user-id");role.Users.Add(user);});// So does creating a new rolerealm.Write(() =>{var newRole = PermissionRole.Get(realm, "my-new-role");});
Once you have a Role, it can be granted permissions. This is done by creating a Permission
object containing that role:
// Create Permission object that grants read and update privileges to a Roletry! realm.write {let permission = realm.create(Permission.self, value: {role: getRole(),canRead: true,canUpdate: true})}
// Create Permission object that grants read and update privileges to a Role[realm transactionWithBlock:^{[RLMPermission createInRealm:realm withValue:@{@"role": getRole(),@"canRead": @YES,@"canUpdate": @YES}];}];
// Create Permission object that grants read and update privileges to a RoleRole role = getRole();Permission permission = new Permission.Builder(role).canRead(true).canUpdate(true).build();
// Create Permission object that grants read and update privileges to a Rolelet permission = realm.create(Realm.Permissions.Permission, {role: getRole(),canRead: true,canUpdate: true,});
// Create Permission object that grants read and update privileges to a Rolerealm.Write(() =>{// The permission can be associated with a Realm, a class, or an object.// Alternatively, you can pass in a collection of permissions to add the// new one to.var permission = Permission.Get("my-role", realm);permission.CanRead = true;permission.CanUpdate = true;});
By itself, the Permission object does nothing and the privileges described in the object are not enforced until the object has been added to an appropriate permission list at either the Realm-level, the Class-level or the Object-level. See the respective subsections for details on how to do this.
A single Permission object can be used in multiple places at the same time. This can be useful if you easily want to update privileges across multiple objects at the same time.
The following privileges can be set when creating or modifying the Permission object:
canCreate
canRead
canUpdate
canDelete
canSetPermissions
canQuery
canModifySchema
These privileges have different semantics depending on the level they are applied. See the sections below for the meaning of each privilege at the given level.
Realm-level permissions apply globally to the Realm file and are modified through a special singleton Realm object that is accessed the following way:
// List all Realm-level permissionslet realmPermissions = realm.permissions// Find Realm level permissions for a given rolelet rolePermissions = realmPermissions.filter("role.name = %@", "my-role").first
// List all Realm-level permissionsRLMRealmPermissions *permissions = [RLMRealmPermissions objectInRealm:realm];// Find Realm level permissions for a given roleRLMPermission *rolePermissions = [permissions objectsWhere:@"role.name = %@", @"my-role"]
// Get the wrapper object for all Realm-level permissionsRealmPermissions realmPermissions = realm.getPermissions()// List all Realm-level permissionsRealmList<Permission> allPermissions = realmPermissions.getPermissions()// Find permissions for a given rolePermission rolePermissions = realmPermissions.getPermissions().equalTo("role.name", "my-role").findFirst()
// Get the global Realm-level permissions objectlet realmPermissions = realm.permissions();// Find permissions for a given rolelet rolePermissions = realmPermissions.permissions.filtered(`role.name = 'my-role'`)[0];// List the Realm-level permissions for all roleslet allPermissions = realmPermissions.permissions;
// List all Realm-level permissionsvar realmPermissions = RealmPermission.Get(realm).Permissions;// Find Realm level permissions for a given rolevar rolePermissions = realmPermissions.FirstOrDefault(p => p.Role.Name == "my-role");
Adding Realm-level permissions for a role is done by adding a new Permission object to the list of Realm-level permissions. This requires a write transaction:
// Adding new permissions must be done within a write transactiontry! realm.write {// Grant read-only access at the Realm-level, which means// that users with this role can read all objects in the Realm// unless restricted by Class or Object level permissions.let permissions = realm.permissions.findOrCreate(forRoleNamed: "my-role")permissions.canRead = truepermissions.canQuery = true}
// Permissions must be modified inside a write transaction[realm transactionWithBlock:^{// Grant read-only access at the Realm-level, which means// that users with this role can read all objects in the Realm// unless restricted by Class or Object level permissions.RLMPermission *permissions = [RLMPermission permissionForRoleNamed:@"my-role" onRealm:realm];permissions.canRead = true;permissions.canQuery = true;}];
// Adding new permissions must be done within a write transactionrealm.executeTransaction((Realm r) -> {RealmPermissions realmPermissions = realm.getPermissions()// Grant read-only access at the Realm-level, which means// that users with this role can read all objects in the Realm// unless restricted by Class or Object level permissions.Permission permissions = realmPermissions.findOrCreate("my-role")permissions.setCanRead(true)permissions.setCanQuery(true)});
// Adding new permissions must be done within a write transactionrealm.write(() => {let realmPermissions = realm.permissions().permissions;// Grant read-only access at the Realm-level, which means// that users with this role can read all objects in the Realm// unless restricted by Class or Object level permissions.let role = realm.objects(Realm.Permissions.Role).filtered(`name = 'my-role'`)[0];let permission = realm.create(Realm.Permissions.Permission, {role: role,canRead: true,canQuery: true,});// Add it to the list of permissions for it to take affect.realmPermissions.push(permission);});
// Adding new permissions must be done within a write transactionrealm.Write(() =>{// Grant read-only access at the Realm-level, which means// that users with this role can read all objects in the Realm// unless restricted by Class or Object level permissions.var permission = Permission.Get("my-role", realm);permission.CanRead = true;permission.CanQuery = true;});
Modifying the Realm-level permissions for an existing role is done by finding the permission object for that role and modifying it. This requires a write transaction:
// Modifying permissions must be done within a write transactiontry! realm.write {// Find permissions for the specific rolelet permission = realm.permissions.findOrCreate(forRoleNamed: "my-role")// Prevent `my-role` users from modifying any objects in the Realm.permission.canUpdate = falsepermission.canDelete = false}
// Permissions must be modified inside a write transaction[realm transactionWithBlock:^{// Find permissions for the specific roleRLMPermission *permissions = [RLMPermission permissionForRoleNamed:@"my-role" onRealm:realm];// Prevent `my-role` users from modifying any objects in the Realm.permissions.canRead = false;permissions.canQuery = false;}];
// Modifying permissions must be done within a write transactionrealm.executeTransaction((Realm r) -> {RealmPermissions realmPermissions = realm.getPermissions();// Find permissions for the specific rolePermission permission = realmPermissions.findOrCreate("my-role");// Prevent `my-role` users from modifying any objects in the Realm.permission.setCanUpdate(false);permission.setCanDelete(false);});
// Modifying permissions must be done within a write transactionrealm.write(() => {let realmPermissions = realm.permissions().permissions;// Find permissions for the specfic rolelet permission = realmPermissions.filtered('role.name', 'my-role')[0];// Prevent `my-role` users from modifying any objects in the Realm.permission.canUpdate = false;permission.canDelete = false;});
// Modifying permissions must be done within a write transactionrealm.Write(() =>{// Find permissions for the specific rolevar permission = Permission.Get("my-role", realm);permission.CanUpdate = false;permission.CanDelete = false;});
Normal users can only grant other people access up to the level they themselves have, and only if they have the SetPermissions
privilege. Realm Object Server admin
users can see and edit everything.
The following table describes the effect of enabling or disabling privileges at the Realm level. If a privilege is marked with N/A it means that the privilege has no meaning at this level beyond being required to enable setting it at Class or Object level.
Type | ENABLED | DISABLED |
canCreate | N/A | N/A |
canRead | Role can see the Realm file itself, i.e. being able to meaningfully connect to it. Being able to see individual classes are covered by Class-level permissions. | Role cannot see anything in the Realm file. An user that is offline will still create the file and schema locally on the device, but as soon as the device connects to the server, it will revert all changes (leaving an empty Realm). This will most likely crash the app. |
canUpdate | Role can make changes to objects in the Realm file. This does not include schema changes or changes to permissions which are covered by | Role cannot change anything in the Realm file. It is read-only. Note that the user can write changes to the Realm file while offline, but any change will be reverted by the server once the device is online again. |
canDelete | N/A | N/A |
canQuery | N/A | N/A |
canSetPermissions | Role can set Realm-level permissions. A user with the | Role cannot set or change Realm-level permissions. |
canModifySchema | Role can add classes to the schema, but not properties which are covered by Class-level permissions. | Role cannot modify the schema in the entire Realm. |
Class-level permissions are permissions related to all objects of a given type.
They can be used reduce the scope of a Realm-level privilege, but cannot be used to broaden a privilege granted at the Realm-level. E.g. if canRead
is set at the Realm-level, it is possible to set canRead
to false at the Class-level, which will prevent the Role from seeing just this single class. However, if canRead
was not set set the Realm-level, the value of canRead
at the Class-level will be ignored and the class will not be readable.
Just like Realm-level permissions, Class-level permissions are exposed as Realm objects that can be accessed the following way:
// List the Class-level permissions for the given model classlet classPermissions = realm.permissions(forType: Person.self)// Find Class-level permissions for a given rolelet rolePermissions = classPermissions.filter("role.name = %@", "my-role").first
// List the Class-level permissions for the given model classRLMClassPermission *classPermissions =[RLMClassPermission objectInRealm:realm forClass:Person.class];// Find Class-level permissions for a given roleRLMPermission *rolePermissions = [[classPermissions.permissionsobjectsWhere:@"role.name = %@", @"my-role"]firstObject];
// Get the Class-level permissions object for the Person classClassPermissions classPermissions = realm.getPermissions(Person.class);// List the Class-level permissions for all rolesRealmList<Permission> permissions = classPermissions.getPermissions();// Find Class-level permissions for a given rolePermission rolePermissions = classPermissions.getPermissions().where().equalTo("role.name", "my-role").findFirst();
// Get the Class-level permissions object for the Person classlet classPermissions = realm.permissions('Person');// List the Class-level permissions for all roleslet permissions = classPermissions.permissions;// Find Class-level permissions for a given rolelet rolePermissions = classPermissions.permissions.filtered(`role.name = 'my-role'`)[0];
// List the Class-level permissions for the given model classvar classPermissions = ClassPermission.Get<Person>(realm).Permissions;// Find Class-level permissions for a given rolevar rolePermission = classPermissions.FirstOrDefault(p => p.Role.Name == "my-role");
Adding Class-level permissions for a role is done by adding a new Permission object to the list of permissions. This requires a write transaction:
// Adding new permissions must be done within a write transactiontry! realm.write {// Remove read-access for the Person class for users with the `my-role` role.let permission = realm.permissions(forType: Person.self).findOrCreate(forRoleNamed: "my-role")permission.canRead = false});
// Adding new permissions must be done within a write transaction[realm transactionWithBlock:^{// Remove read-access for the Person class for users with the `my-role` role.RLMPermission *permission = [RLMPermission permissionForRoleNamed:@"my-role"onClass:Person.classrealm:realm];permission.canRead = false;});
// Adding new permissions must be done within a write transactionrealm.executeTransaction((Realm r) -> {ClassPermissions realmPermissions = realm.getPermissions(Person.class);// Remove read-access for the Person class for users with the `my-role` role.Role role = realm.where(Role.class).equalTo("name", "my-role").findFirst();Permission p = new Permission.Builder(role).canRead(false).build();// Add it to the list of permissions for it to take affect.classPermissions.getPermissions().add(permission);});
// Adding new permissions must be done within a write transactionrealm.write(() => {let realmPermissions = realm.permissions('Person').permissions;// Remove read-access for the Person class for users with `my-role` role.let role = realm.objects(Realm.Permissions.Role).filtered(`name = 'my-role'`)[0];let permission = realm.create(Realm.Permissions.Permission, {role: role,canRead: false,});// Add it to the list of permissions for it to take affect.classPermissions.push(permission);});
// Adding new permissions must be done within a write transactionrealm.Write(() =>{var permission = Permission.Get<Person>("my-role", realm);// Alternatively, there's a string-based API if, for some reason,// you can't use the generic one.// var permission = Permission.Get("my-role", "Person", realm);permission.CanRead = false;});
Modifying the Class-level permissions for an existing role is done by finding the permission object for that role and modify it. This requires a write transaction:
// Modifying permissions must be done within a write transactiontry! realm.write {// Find permissions for the role and change itlet permission = realm.permissions(forType: Person.self).findOrCreate(forRoleNamed: "my-role")// Prevent `my-role` users from modifying any Person objects.permission.canUpdate = falsepermission.canDelete = false});
// Modifying permissions must be done within a write transaction[realm transactionWithBlock:^{RLMPermission *permission = [RLMPermission permissionForRoleNamed:@"my-role"onClass:Person.classrealm:realm];// Prevent `my-role` users from modifying any Person objects.permission.canUpdate = false;permission.canDelete = false;});
// Modifying permissions must be done within a write transactionrealm.executeTransaction((Realm r) -> {ClassPermissions classPermissions = realm.getPermissions(Person.class);// Find permissions for the role and change itPermission permission = classPermissions.getPermissions().where().equalTo("role.name", "my-role").findFirst();// Prevent `my-role` users from modifying any Person objects.permission.setCanUpdate(false);permission.setCanDelete(false);});
// Modifying permissions must be done within a write transactionrealm.write(() => {let classPermissions = realm.permissions('Person').permissions;// Find permissions for a specfic rolelet permission = classPermissions.filtered(`role.name = 'my-role'`)[0];// Prevent `my-role` users from modifying any objects in the Realm.permission.canUpdate = false;permission.canDelete = false;});
// Modifying permissions must be done within a write transactionrealm.Write(() =>{var permission = Permission.Get<Person>("my-role", realm);// Prevent `my-role` users from modifying any Person objects.permission.CanUpdate = false;permission.CanDelete = false;});
Normal users can only grant other people access up to the level they themselves have, and only if they have the SetPermissions
privilege. Admin users can see and edit everything.
The following table describes the effect of enabling or disabling a given privilege at the Class level. If a privilege is marked with N/A it means that the privilege has no meaning at this level beyond being required to enable setting it at the Object level.
Type | ENABLED | DISABLED |
canCreate | Role can create objects of this type. If a user has the | Role cannot create objects of this type. |
canRead | Role can see objects of this type. | Role cannot see any objects of this type. It is still allowed to query them, but the query result will be empty |
canUpdate | Role can change properties in objects of this type. | Role cannot make any change to properties in objects of this type. |
canDelete | N/A | N/A |
canQuery | Role is allowed to create a server side Subscription for this type. | Role is not allowed to create a server side Subscription for this type. Note: Local queries will always work. |
canSetPermissions | Role can set Class-level permissions for the class. | Role cannot set class-level permissions for the class. |
canModifySchema | Role can add properties to this class. Deleting and renaming fields is currently not supported by the Realm Object Server. | Role cannot modify the schema for this class. |
Object-level permissions are permissions related to a single Realm object.
They can be used to reduce the scope of Class-level privileges, but cannot be used to broaden a privilege granted at the Class-level. E.g. if canRead
is enabled at the Class-level, it is possible to disable canRead
at the Object-level which will prevent the Role from seeing just this single object. However, if canRead
was disabled at the Class-level, no matter the value of canRead
at the Object-level the object will not be readable.
In order to add permissions to your objects, you must add a special Access Control List (ACL) property. If your objects do not have the ACL property, the objects will be fully accessible by anyone (provided that they are discoverable -- see the canQuery/canRead
permission at the Class level).
The ACL property is added the following way:
// The ACL property is a `List<Permission>` field with a user-defined nameclass Person: Object {@objc dynamic var name = ""let permissions = List<Permission>()}
// The ACL property is a `RLMArray<RLMPermission *>` field// with a user-defined name@interface Person : RLMObject@property NSString *name;@property RLMArray<RLMPermission *><RLMPermission> *permissions;@end
// The ACL property is a `RealmList<Permission>` field// with a user-defined namepublic class Person extends RealmObject {public String name;public RealmList<Permission> permissions = new RealmList<>();}
// The ACL property is a list-of-permission property// with a user-defined nameconst PersonSchema = {name: 'Person',properties: {name: 'string',permissions: '__Permission[]'}};
// The ACL property is an `IList<Permission>` property with a user-defined namepublic class Person : RealmObject{// Other properties ...public IList<Permission> Permissions { get; }}
If there is an ACL property, but no permissions have been added for any role, then nobody except Realm Object Server admin users will have access to the object. This includes the user creating the object.
Adding Object-level permissions for a role is done adding a new Permission object to the list of permissions. This requires a write transaction:
// Adding new permissions must be done within a write transactiontry! realm.write {// Grant users with the `shared-objects` role access to read and modify// this objectlet person = getPerson()let permissions = person.permissions.findOrCreate(forRoleNamed: "shared-objects")permissions.canRead = truepermissions.canUpdate = truepermissions.canDelete = true}
// Adding new permissions must be done within a write transaction[realm transactionWithBlock:^{// Grant users with the `shared-objects` role access to read and modify// this objectPerson *person = getPerson();RLMPermission *permissions = [RLMPermissionpermissionForRoleNamed:@"shared-objects"inArray:person.permissions];permissions.canRead = true;permissions.canUpdate = true;permissions.canDelete = true;}];
// Adding new permissions must be done within a write transactionrealm.executeTransaction((Realm r) -> {Person p = realm.where(Person.class).equalto("id", getId()).findFirst()// Grant users with the `shared-objects` role access to read and modify// this objectRole role = realm.where(Role.class).equalTo("name", "shared-objects").findFirst();Permission p = new Permission.Builder(role).canRead(true).canUpdate(true).canDelete(true).build();// Add it to the list of permissions associated with the object// for it to take affect.p.permissions.add(permission);});
// Adding new permissions must be done within a write transactionrealm.write(() => {let person = realm.objects('Person').filtered(`id = "my-id"`)[0];// Grant users with the `shared-objects` role access to read and modify// this objectlet role = realm.objects(Realm.Permissions.Role).filtered(`id = "shared-objects"`)[0];let permission = realm.create(Realm.Permissions.Permission, {role: role,canRead: true,canUpdate: true,canDelete: true,});// Add it to the list of permissions for it to take affect.person.permissions.push(permission);});
var person = GetSomePerson();// Adding new permissions must be done within a write transactionrealm.Write(() =>{// Grant users with the `shared-objects` role access to read and modify// this object. Using the Permission.Get API that accepts a RealmObject// will use reflection to find the `IList<Permission>` property.var permission = Permission.Get("shared-objects", person);// Alternatively, you can pass in the permission collection directly:// var permission = Permission.Get("shared-objects", person.Permissions);permission.CanRead = true;permission.CanUpdate = true;permission.CanDelete = true;});
Modifying the Object-level permissions for an existing role is done by finding the permission object for that role and modifying it. This requires a write transaction:
// Modifying permissions must be done within a write transactiontry! realm.write {let person = getPerson()// Prevent `shared-objects` users from modifying this object.let permissions = person.permissions.findOrCreate(forRoleNamed: "shared-objects")permissions.canUpdate = falsepermissions.canDelete = false}
// Modifying permissions must be done within a write transaction[realm transactionWithBlock:^{Person *person = getPerson();// Prevent `shared-objects` users from modifying this object.RLMPermission *permissions = [RLMPermissionpermissionForRoleNamed:@"shared-objects"inArray:person.permissions];permissions.canUpdate = falsepermissions.canDelete = false}];
// Modifying permissions must be done within a write transactionrealm.executeTransaction((Realm r) -> {Person p = realm.where(Person.class).equalTo("id", getId()).findFirst();// Find permissions for a specific role and change themPermission permission = p.permissions.where().equalTo("role.name", "shared-objects").findFirst();// Prevent `shared-objects` users from modifying this object.permission.setCanUpdate(false);permission.setCanDelete(false);});
// Modifying permissions must be done within a write transactionrealm.write(() => {let person = realm.objects('Person').filtered(`id = "${getId()}"`)[0];// Find permissions for a specific role and change themlet permission = person.permissions('role.name', 'shared-objects')[0];// Prevent `shared-objects` users from modifying this object.permission.canUpdate = false;permission.canDelete = false;});
var person = GetSomePerson();// Modifying permissions must be done within a write transactionrealm.Write(() =>{// Prevent `shared-objects` users from modifying this object.var permission = Permission.Get("shared-objects", person);permission.CanUpdate = false;permission.CanDelete = false;});
Any object that is reachable through links or collections from an object to which a user has Read access will also be readable by that user, even if they explicitly do not have Read access to the reachable object. This is due to a current limitation in the way object graphs are represented in Realm.
The following table describes the effect of enabling or disabling a given privilege at the Object level:
Type | ENABLED | DISABLED |
canCreate | N/A | N/A |
canRead | Role can see and read this object. This include any referenced objects. | Role cannot read or see this object. |
canUpdate | Role can update properties on this object. The ACL property is a special case handled by | Role is not allowed to change the value of any properties on the object. |
canDelete | Role can delete the object. | Role is not allowed to delete the object. |
canQuery | N/A | N/A |
canSetPermissions | Role can modify Permission objects referenced by the custom ACL property on the object. | Role is not allowed to modify the custom ACL property on the object. |
canModifySchema | N/A | N/A |
Because a User
can be part of multiple roles with many different permissions, it is often useful to be able to determine exactly what the current user can do with an object (or class, or the Realm file).
This can be achieved in the following way:
// Realm privilegeslet privileges = realm.getPrivileges()// Class privileges for `Person`let privileges = realm.getPrivileges(Person.self)// Object privilegeslet person = getPerson()let privileges = realm.getPrivileges(person)
// Realm privilegesstruct RLMRealmPrivileges privileges = [realm privilegesForRealm];// Class privileges for `Person`struct RLMClassPrivileges privileges = [realm privilegesForClass:Person.class];// Object privilegesPerson *person = getPerson();struct RLMObjectPrivileges privileges = [realm privilegesForObject:person];
// Realm privilegesRealmPrivileges privileges = realm.getPrivileges();// Class privileges for `Person`ClassPrivileges privileges = realm.getPrivileges(Person.class);// Object privilegesPerson person = getObject();ObjectPrivileges privileges = realm.getPrivileges(person);
// Realm privilegeslet privileges = realm.privileges();// Class privileges for `Person`let classPrivileges = realm.privileges('Person');// Object privilegeslet person = getPerson();let objectPrivileges = realm.getPrivileges(person);
// Realm privilegesvar privileges = realm.GetPrivileges();// Class privileges for Personvar privileges = realm.GetPrivileges<Person>();// Class privileges for Person using the string APIvar privileges = realm.GetPrivileges("Person");// Object privilegesvar person = realm.Find<Person>(someId);var privileges = realm.GetPrivileges(person);
This can e.g. be used to toggle an Edit button if a user only have read access to an object:
let person = getPerson()let privileges = realm.getPrivileges(person)if privileges.contains(.update) {showEditButton()} else {hideEditButton()}
Person *person = getPerson();struct RLMObjectPrivileges privileges = [realm privilegesForObject:person];if (privileges.update) {showEditButton()}else {hideEditButton()}
Person person = getObject();ObjectPrivileges privileges = realm.getPrivileges(person);if (privileges.canUpdate()) {showEditButton();} else {hideEditButton();}
let person = getPerson();let objectPrivileges = realm.getPrivileges(person);if (privileges.canUpdate) {showEditButton();} else {hideEditButton();}
var person = realm.Find<Person>(someId)var privileges = realm.GetPrivileges(person);if (privileges.HasFlag(ObjectPrivileges.Update)){ShowEditButton();}else{HideEditButton();}
If a user does not have canRead
access to the classes used by the permission system, querying about privileges will always return false , even if the user might have access. See the next section for more details.
Query-based sync permissions are implemented using Realm classes and objects. This means that the permission system itself is subject to the same permission rules as normal model classes.
As a consequence it is possible to disallow users access to the information in the permission system by removing the canRead
privilege for those classes. This is generally not recommended as it prevent users from knowing the full extend of their privileges.
The classes used for implementing the permissions system are:
RealmSwift.RealmPermissionRealmSwift.ClassPermissionRealmSwift.PermissionUserRealmSwift.PermissionRoleRealmSwift.Permission
RLMPermissionRLMRealmPermissionRLMClassPermissionRLMPermissionUserRLMPermissionRole
io.realm.sync.permissions.PermissionUserio.realm.sync.permissions.Permissionio.realm.sync.permissions.RealmPermissionsio.realm.sync.permissions.ClassPermissionsio.realm.sync.permissions.RoleSee the API docs here:https://realm.io/docs/java/latest/api/io/realm/sync/permissions/package-summary.html
// Coming soon
Realms.Sync.PermissionRealms.Sync.PermissionUserRealms.Sync.RealmPermissionRealms.Sync.ClassPermissionRealms.Sync.PermissionRoleSee the API docs here:https://realm.io/docs/dotnet/latest/api/reference/Realms.Sync.Permission.html
Specifically it means that:
A user cannot modify any Realm-level permissions unless they have the setPermissions
privilege on the Class-level permission object for the __Realm
class.
A user cannot modify any Class-level permissions unless they have the setPermissions
privilege on the Class-level permission object for the __Class
class.
This can be verified the following way:
// Check access to Realm-level permissionslet realmPrivs = realm.getPrivileges()realmPrivs.contains(.read) // Can see Realm-level permissionsrealmPrivs.contains(.setPermissions) // Can modify Realm-level permissions// Check access to Class-level permissionslet classPrivs = realm.getPrivileges(ClassPermissions.self)classPrivs.contains(.read) // Can see Class-level permissionsclassPrivs.contains(.setPermissions) // Can modify Class-level permissions
// Check access to Realm-level permissionsstruct RLMRealmPrivilegs realmPrivs = realm.privilegesForRealm;realmPrivs.read; // Can see Realm-level permissionsrealmPrivs.setPermissions; // Can modify Realm-level permissions// Check access to Class-level permissionsstruct RLMClassPrivileges classPrivs = [realm privilegesForClass:ClassPermissions.class];classPrivs.read; // Can see Class-level permissionsclassPrivs.setPermissions; // Can modify Class-level permissions
// Check access to Realm-level permissionsRealmPrivileges realmPrivs = realm.getPrivileges(RealmPermissions.class);realmPrivs.canRead(); // Can see Realm-level permissionsrealmPrivs.canSetPermissions(); // Can modify Realm-level permissions// Check access to Class-level permissionsClassPrivileges classPrivs = realm.getPrivileges(ClassPermissions.class);classPrivs.canRead(); // Can see Class-level permissionsclassPrivs.canSetPermissions(); // Can modify Class-level permissions
// Check access to Realm-level permissionslet realmPrivileges = realm.privileges(Realm.Permissions.Realm);realmPrivileges.canRead; // Can see Realm-level permissionsrealmPrivileges.canSetPermissions; // Can modify Realm-level permissions// Check access to Class-level permissionslet classPrivileges = realm.privileges(Realm.Permissions.Class);classPrivileges.canRead; // Can see Class-level permissionsclassPrivileges.canSetPermissions; // Can modify Class-level permissions
// Check access to Realm-level permissionsvar realmPrivileges = realm.GetPrivileges();// Can see Realm-level permissionsvar canRead = realmPrivileges.HasFlag(RealmPrivileges.Read);// Can modify Realm-level permissionsvar canSetPermissions = realmPrivileges.HasFlag(RealmPrivileges.SetPermissions);// Check access to Class-level permissionsvar classPrivileges = realm.GetPrivileges<ClassPermission>();// Can see Class-level permissionsvar canReadClass = classPrivileges.HasFlag(ClassPrivileges.Read);//Can modify Class-level permissionsvar canSetPermissionsClass = classPrivileges.HasFlag(ClassPrivileges.SetPermissions);
There are two way to view permissions in Studio. First you can see the Realm, Class, and Object permissions through the selection options on the right. When setting permissions through a script or client testing, this will update.
The second method is two view the permissions through the __Permission class. This will show the relationships between the roles, user, and permission objects. In order to see it, you need to enable "Show system classes" Under the "View" menu item.
They are stored in the tables named __Realm
, __Class
, __Role
, __User
and __Permission
.
Realm-level permissions are defined in a special object with id = 0
in the __Realm
table.
Class-level permissions are stored in the __Class
table. Each Realm class has an entry in that table.
Since it is Roles that are granted privileges, not users, it can be considered a security hole if users can add themselves to other roles, effectively elevating their own privileges.
This can be prevented by making the Role
class readonly for anyone except administrators. This is done by modifying the the Class-level permissions for the Role
class the following way:
realm.write(() => {// Get the Class-level Permissions for the Role class.val rolePermissions = realm.permissions(Realm.Permissions.Role);// Reduce privileges to read only for the default "everyone" role// that all users are part of. Users can, now, only modify roles if they// are part of another role that grant that privilege.val permissions = rolePermissions.findOrCreate("everyone");permissions.canCreate = false;permissions.canModify = false;permissions.canDelete = false;})
Modifying the Role this way should either be done by a server side script or alternatively by using Realm Studio:
If a model class does not have an ACL property defined then all objects of that type is available to everyone if the Class-level permission for the class allow it.
If you want to allow some users to see your object and not others, you need to enable the Object-level permissions by adding the ACL property to the schema for that class:
const PersonSchema = {name: 'Person',properties: {name: 'string',permissions: '__Permission[]' // ACL property}};
Once that is done, permissions can now be added to the object. Realm have created pre-defined roles for each user that only have the user as a member. You can use these roles if you only want to share the object with one other person.
realm.write(() => {// Grant full privileges to the creator of the objectlet ownIdentity = user.identity;let role = realm.objects(Realm.Permissions.Role).filtered(`id = "__User:{$ownIdentity}"`)[0];let permission = realm.create(Realm.Permissions.Permission, {role: role,canRead: true,canUpdate: true,canDelete: true,canSetPermissions: true,});// Grant read privileges to another userlet otherUserIdentity = getOtherUserIdentitySync();let role = realm.objects(Realm.Permissions.Role).filtered(`id = "__User:{$otherUserIdentity}"`)[0];let permission = realm.create(Realm.Permissions.Permission, {role: role,canRead: true,});});
Creating an object does not automatically grant admin privileges over that object. You also need to add your own privileges.
Users on the Realm Object Server are defined by an identity
. The identity is used when finding a single users Role or otherwise assigning people to roles.
You can find your own identity this way:
let credentials = Realm.Sync.Credentials.usernamePassword(username, 'password');Realm.Sync.User.login(url, credentials).then((user) => {user.identity // Get the users identity});
Admin users can also lookup other users identity using their email:
let provider = 'password';let userId = 'user@email.com';adminUser.retrieveAccount(provider, userId).then((account) => {account.user_id; // Users identity});
Non-admin users do not, currently , have any way of automatically looking up another users identity.
Our recommendation is to always remove the canModifySchema
permission so that users cannot change the schema design as this could affect other users of the app. While it may be useful during the initial app development and the POC phase to allow developers to quickly iterate on the schema, once multiple developers are using the same realm data for development testing, a fat-fingered schema can bring the rest of the developers to a halt. However, this is something only a server-side app with a Realm admin account should have the ability to change in production.
Once ready to move into a production environment, it is best to disable the everyone role which can give blanket access to objects. This should be deprecated in favor of more specific and customized roles.
Not what you were looking for? Leave Feedback