Data access security
Limiting access to data is one of the most crucial points at developing an app. With ApiOmat, you can define various levels of access to either classes and/or objects to make your application as secure as you want.
Basic access rules
With every app you create, you can define roles needed to interact with your data. This is done in the class editor using the security panel.
Predefined roles are:
-
Everybody – Everybody having your API-Key (can be found at the App-Setup page in dashboard); username/password credentials are not needed.
-
App user – The users of your app; all instances of the class User or derived classes having a username and a password.
-
Object owner – The user who created the specific object.
Roles have a hierarchical behaviour; if a guest is able to write an object of the class, a user or the owner may do also.
You can also define a role for each type of interaction with objects:
-
Create – Object is initially created (POST)
-
Read – Object is read (GET)
-
Write – Object gets updated or deleted (PUT/DELETE)
Using the radio buttons in the security panel, you can set separate roles for each method. Since the combination of owner and create does not make sense, the radio button is missing there.
All these settings are on a class level basis, meaning that all objects of the selected class follow the settings you did for that class.
While these possibilities may suffice for most use cases, ApiOmat allows you to configure access in two much more detailed ways:
-
using ACLs
-
using Dynamic Roles / custom role check methods
Note regarding the default User class
The default access rules of the basics.User class allow all users to read the data of other users. You can either create a subclass and modify the access rules, or change the configuration of the module "Basics" accordingly (see Basics Module).
Access Control Lists (ACL)
ALCs enables you to do two major things:
-
define your own roles and
-
using these roles to set permissions on class and object levels
With ACLs, you can set grant rights in addition to create/read/write. The grant right is needed to modify the ACLs for an object. With no grant roles defined, only the owner of an object has the right to modify ACLs for an object. If grant roles are specified, even the owner needs to be in that role. Grant is also hierarchical, meaning that users with grant right can also write or read.
Resources
If you do not want your resources (files or images) to be accessed by guests, you can check the “Restrict resource access” box in the security panel. Each resource you attach to an object after setting this option will get the read and write permissions of the object automatically (the permission that's defined in the class of the object as well as the object-specific permission). For example, if your class is readable by role “myRole”, the resources attached to objects of that class are as well.
If you use the Dynamic Roles feature introduced in ApiOmat 3.0.0 and have a custom role check method assigned to a class, and use restrictResourceAccess, the role check method will also be called when a request to the resource is received (only since ApiOmat v3.0.1).
Please pay attention that the "restrictResourceAccess" setting only applies to new resources, not to those added to an object before the setting.
When adding a resource to multiple objects, only the permissions of the latest object are copied to the resource. This only applies when using the direct URL of a resource and making a PUT request to multiple objects. This is not the case when using the Dashboard > v3.0.0, SDK > v2.6.1 or new static data REST endpoints directly, because in those cases a new resource is created with every request.
Roles
In addition to the three roles Everybody, User, and Owner, you can create your own roles in data editor or via SDK's and use the role names in the security settings. Creating a role in the data editor is very easy:
-
switch to data editor
-
select the Role class in Basics module on the left menu
-
hit the plus icon on the upper right to create a new role
-
define a name for the role in the popup
-
add user names of the users who have this role in the members attribute
Back in the class editor, you can select the Roles (ACL) radio button for the method you want to allow for your group (read/write/grant). Select the Roles name next to the radio button. You can add even more roles, each on a new line. Hit deploy and you are done.
One thing to remember:Using ACLs forbids access to the class or object to all users who are NOT members of the role. This includes owners of an object!
Object level permissions
Now that you have your own roles, you can do even more than just define class level permissions: You can also set role permissions on object level.
Therefore, each object has the three attributes
-
allowedRolesRead
-
allowedRolesWrite
-
allowedRolesGrant
Like in the class editor, you can add one role name per line here which is allowed to read the object, write the object or which role is allowed to change the ACLs for this object (the grant privilege). All settings from the class level are inherited; if you allow role A to write the object on class level and role B on you objects level, all users in role A and B are able to write the object. Especially, you can not remove the roles defined on class level on the object.
Of course, you can set both roles and these object-based values in your SDK with the setter methods and with server code.
New attributes since ApiOmat 2.5
As of ApiOmat version 2.5, you have the possibility to distinguish the roles from class level and object level within the returned JSON response. For now, there are additional fields named allowedRolesReadFromClass, allowedRolesWriteFromClass, allowedRolesCreateFromClass and allowedRolesGrantFromClass which include the class level settings and the fields. The object level settings are contained in the fields allowedRolesReadFromObject, allowedRolesWriteFromObject and allowedRolesGrantFromObject.
The combined values are still available in allowedRolesRead, allowedRolesWrite and allowedRolesGrant.
You can get these fields in the JSON response and also in our Dashboard, but unfortunately, these fields are currently not available in our SDKs. We're currently working on this issue to make these fields available in the SDKs.
The following small Java example (other languages are similar) will show how to create an “agent” role with two users which have access to
a secret document:
Role agents =
new
Role();
agents.setName(
"agents"
);
String[] agentNames = {
"Foo"
,
"Bar"
};
agents.setMembers(Arrays.asList(agentNames));
agents.saveAsync(
new
AOMEmptyCallback() {
@Override
public
void
isDone(ApiomatRequestException exception) {
if
(exception !=
null
) {
// ...
}
}
});
AOMRole *agents = [[AOMRole alloc] init];
[agents setName:@
"agents"
];
NSMutableArray *agentNames = [NSMutableArray arrayWithObjects:@
"Foo"
, @
"Bar"
, nil];
[agents setMembers:agentNames];
[agents saveAsyncWithBlock:^(NSError *error) {
if
(error)
{
...
}
}];
var agents = AOMRole()
agents.name =
"agents"
let agentNames = [
"Foo"
, @
"Bar"
]
agents.members = agentNames
agents.save { (error) in
if
error {
...
}
}
var
agents=
new
Apiomat.Role();
agents.setName(
"agents"
);
var
agentNames=[
"Foo"
,
"Bar"
];
agents.setMember(agentNames);
var
saveCB = {
onOk :
function
() {
console.log(
"saved"
);
onError :
function
(error) {
console.log(
"Some error occured: ("
+ error.statusCode +
")"
+ error.message);
}
};
agents.save(saveCB);
const agents =
new
Role();
agents.name =
"agents"
;
agents.member = [
"Foo"
,
"Bar"
];
await agents.save();
Now you can create a document and allow access to it only by the users belonging to the role “agents”:
Document doc =
new
Document();
doc.setName(
"Secure document! Burn after reading!"
);
String[] docReaders ={
"agents"
};
doc.setAllowedRolesWrite(
new
HashSet(Arrays.asList(docReaders)));
doc.saveAsync(
new
AOMEmptyCallback() {
@Override
public
void
isDone(ApiomatRequestException exception) {
if
(exception !=
null
) {
// ...
}
}
});
Document *doc = [[Document alloc] init];
[doc setName:@
"Secure document! Burn after reading!"
];
NSMutableArray *docReaders = [NSMutableArray arrayWithObject:@
"agents"
];
[doc setAllowedRolesWrite:docReaders];
[doc saveAsyncWithBlock:^(NSError *error) {
if
(error)
{
...
}
}];
var doc = Document()
doc.name =
"Secure document! Burn after reading!"
let docReaders = [
"agents"
]
doc.allowedRolesWrite = docReaders
doc.save { (error) in
if
error {
...
}
}
var
doc=
new
Apiomat.Document();
doc.setName(
"Secure document! Burn after reading!"
);
var
docReaders=[
"agents"
];
doc.SetAllowedRolesWrite(docReaders);
var
saveCB = {
onOk :
function
() {
console.log(
"saved"
);
},
onError :
function
(error) {
console.log(
"Some error occured: ("
+ error.statusCode +
")"
+ error.message);
}
};
doc.save(saveCB);
const doc =
new
Document();
doc.name =
"Secure document! Burn after reading!"
;
doc.allowedRolesWrite = [
"agents"
];
await doc.save();
Dynamic Roles / custom role check methods
You can also implement your own role check method in one Native Module class and use it for protecting any other of your module classes. If other customers' modules are visible to you because either they granted you the necessary permissions or because the module is released, you can also use their custom role check methods as well.
Dynamic Roles work similar to the Authentication Classes, but instead of assigning them to the backend, you assign them to classes, so you have a much more fine grained control.
For more information about this feature, see Dynamic Roles.