Authorization
Introduction
See Understanding RESTHeart Security for an high level view of the RESTHeart security model.
RESTHeart is built around a pluggable architecture. It comes with a strong security implementation but you can easily extend it by implementing plugins. This section documents the authorizers available out-of-the-box.
RESTHeart offers two authorizers out-of-the-box:
-
Mongo ACL Authorizer
-
File ACL Authorizer
Both authorizers provide a simple, declarative and a rich, ACL-based model for authorization.
It’s even possible to develop custom authorizers. Please refer to Develop Security Plugins for more information.
Mongo ACL Authorizer
mongoAclAuthorizer authorizes requests according to the Access Control List defined in a MongoDB collection.
The configuration allows:
-
defining the collection to use to store ACL documents (
acl-db
andacl-collection
). -
enabling the root role (
root-role
): users with root role, can execute any requests, regardless of the permissions set in the ACL. Set it to null (root-role: null
) to disable it. -
controlling the ACL caching.
mongoAclAuthorizer:
acl-db: restheart
acl-collection: acl
# clients with root-role can execute any request
root-role: admin
cache-enabled: true
cache-size: 1000
cache-ttl: 5000
cache-expire-policy: AFTER_WRITE
Format of permissions
The properties of the permission document are:
property | type | description |
---|---|---|
predicate |
string |
If the undertow predicate resolves the request then the request is authorized. Many examples of predicates can be found in the file acl.yml |
roles |
JSON array of strings |
The roles that are applied the ACL document. The special role |
priority |
number |
A request might fulfill several predicates; an ACL document with higher priority has higher evaluation precedence. |
mongo |
|
For requests handled by the |
An example permission document follows (for more examples check acl.json):
{
"_id": "userCanGetOwnCollection",
"roles": ["user"],
"predicate": "method(GET) and path-template('/{userid}') and equals(@user._id, ${userid}) and qparams-contain(page) and qparams-blacklist(filter, sort)",
"priority": 100,
"mongo": {
"readFilter": { "_$or": [{ "status": "public" }, { "author": "@user._id" }] },
"projectResponse": { "log": 0 }
}
}
The permission allows the request if:
-
the authenticated user has
user
role -
the request method is
GET
-
the request path is
/{userid}
, where the{userid}
is the id of the authenticated user -
the request specifies the query parameter
page
, due to predicateqparams-contain(page)
-
the request does not specify the query parameters
filter
andsort
, due to predicateqparams-blacklist(filter, sort)
If the request is authorized by this permission then:
-
the property
log
is removed from the response, due toprojectResponse
-
a
readFilter
applies, so only document withstatus=public
orauthor=<userid>
are returned
Predicates
The ACL permissions use the undertow predicate language to define the condition a request must met to be authorized.
A simple example of predicate is (method(GET) or method(POST)) and path('/coll')
that authorizes GET
or POST
requests when the URI is /coll
RESTHeart extends this language introducing the following new predicates that extend and simplify the ACL permission definition:
predicate | ||
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
checks if |
|
|
|
if the request content is |
|
|
if the request content is |
|
|
if the request content is |
Note: the double quotes in values
since each element must be valid bson such as 1
(number), "1"
(string), "bar"
(string) or {"foo": "bar"}
(object)
MongoPermissions
For requests handled by the MongoService
(i.e. the service that implements the REST API for MongoDB) the permission can specify the MongoPermissions
object.
{
"mongo": {
"allowManagementRequests": false,
"allowBulkPatch": false,
"allowBulkDelete": false,
"allowWriteMode": false,
"readFilter": {"$or": [ {"status": "public"}, {"author": "@user._id"} ] },
"writeFilter": {"author": "@user._id"},
"mergeRequest": {"author": "@user._id"},
"projectResponse": {"secret": 0 }
}
}
mongo permission | description |
---|---|
|
DB Management Requests are forbidden by default (create/delete/update dbs, collection, file buckets schema stores and schemas, list/create/delete indexes, read db and collection metadata). To allow these requests, |
|
bulk PATCH requests are forbidden by default, to allow these requests, |
|
bulk DELETE requests are forbidden by default, to allow these requests, |
|
requests cannot use the query parameter |
Note that, in order to allow those requests, not only the corresponding flag must be set to true
but the permission predicate
must resolve to true
.
Consider the following examples.
The next one won’t allow the role user
to execute a bulk PATCH even if the allowBulkPatch
is true
since the predicate
requires the request verb to be GET
{
"roles": [ "user" ],
"predicate": "path-prefix('coll') and method(GET)"
"mongo": {
"allowBulkPatch": true
}
}
The next request allows to PATCH the collection coll
and all documents in it, but won’t allow to execute a bulk PATCH (i.e. the request PATCH /coll/*?filter={ "status": "draft" }
since the allowBulkPatch
is false
{
"roles": [ "user" ],
"predicate": "path-prefix('coll') and method(PATCH)",
"mongo": {
"allowBulkPatch": false
}
}
readFilter and writeFilter
Tip
|
readFilter and writeFilter allows to partition data by roles.
|
These are optional filters that are added to read and write requests respectively when authorized by an ACL permission that defines them.
The readFilter
applies to GET requests to limits the returned document to the ones that match the specified condition.
The writeFilter
applies to write request to allow updating only the documents that match the specified condition.
Warning
|
writeFilter only limits updates and cannot avoid creating documents that don’t match the filter. The properties used in the filter should be set using mongo.mergeRequest .
|
mergeRequest
mergeRequest
allows to merge the specified properties to the request content. In this way, server-side evaluated properties can be enforced.
In the following example:
{
"roles": [ "user" ],
"predicate": "path-prefix('coll') and method(PATCH)",
"mongo": {
"mergeRequest": {"author": "@user._id"}
}
}
the property author
is evaluated to be the userid
of the authenticated client.
@user
is a special variable that allows accessing the properties of the user object. The following variables are available:
variable | description |
---|---|
|
the user object (excluding the password), e.g. |
|
the properties of the request, e.g. |
|
the |
|
the current date time |
|
the value of the |
projectResponse
projectResponse
allows to project the response content, i.e. to remove properties.
It can be used with positive or negative logic.
The following hides the properties secret
and a.nested.secret
(you can use the dot notation!). All other properties are returned.
{
"roles": [ "user" ]
"predicate": "path-prefix('coll') and method(PATCH)",
"mongo": {
"projectResponse": {"secret": 0, "a.nested.secret": 0 }
}
}
The following only returns the property public
(you can use the dot notation!). All other properties are hidden.
{
"roles": [ "user" ]
"predicate": "path-prefix('coll') and method(PATCH)",
"mongo": {
"projectResponse": {"public": 1 }
}
}
File ACL Authorizer
fileAclAuthorizer allows defining roles permissions in the configuration or in a separate YAML configuration file.
fileAclAuthorizer:
#conf-file: ./acl.yml
permissions:
- role: admin
predicate: path-prefix('/')
priority: 0
The conf-file
path is either absolute, or relative to the restheart configuration file (if specified) or relative to the plugins directory (if using the default configuration).
The permission’s options are fully equivalent to the ones handled by the mongoAclAuthorizer, only the yml format is used in place of Json.
An example follows (for more examples check acl.yml):
roles: [ "user" ]
predicate: >
method(GET) and path-template('/{userid}') and equals(@user._id, ${userid}) and qparams-contain(page) and qparams-blacklist(filter, sort)
priority: 100
mongo:
readFilter: >
{ "_$or": [{ "status": "public" }, { "author": "@user._id" }] }
projectResponse: >
{ "log": 0 }
Tip
|
Watch Authorization via file and MongoDB |
Programmatic Configuration of ACL
Security policy rules can be defined programmatically by allowing both inclusive and exclusive security policies through veto and permission predicates.
The ACLRegistry
can be injected with @Inject("acl-registry")
and allows defining Access Control Lists (ACLs) programmatically:
public interface ACLRegistry {
/**
* Registers a veto predicate that determines if a request should be denied.
* When the predicate evaluates to true, the request is immediately forbidden (vetoed).
* Additionally, a request will also be denied if it is not explicitly authorized by any
* allow predicates or any other active allowing authorizers.
*
* @param veto The veto predicate to register. This predicate should return true to veto (deny) the request,
* and false to let the decision be further evaluated by allow predicates or other authorizers.
*/
public void registerVeto(Predicate<Request<?>> veto);
/**
* Registers an allow predicate that determines if a request should be authorized.
* The request is authorized if this predicate evaluates to true, provided that no veto predicates
* or other active vetoer authorizers subsequently deny the request. This method helps in setting up
* conditions under which requests can proceed unless explicitly vetoed.
*
* @param allow The allow predicate to register. This predicate should return true to authorize the request,
* unless it is vetoed by any veto predicates or other vetoing conditions.
*/
public void registerAllow(Predicate<Request<?>> allow);
/**
* Registers a predicate that determines whether requests handled by the ACLRegistryAllower
* require authentication. This method is used to specify conditions under which authentication
* is mandatory. Typically, authentication is required unless there are allow predicates
* explicitly authorizing requests that are not authenticated.
*
* @param authenticationRequired The predicate to determine if authentication is necessary.
* It should return true if the request must be authenticated,
* otherwise false if unauthenticated requests might be allowed.
*/
public void registerAuthenticationRequirement(Predicate<Request<?>> authenticationRequired);
}
This registry is utilized by the ACLRegistryVetoer
and ACLRegistryAllower
authorizers to manage request permissions. The ACLRegistryVetoer
denies requests based on veto predicates, while the ACLRegistryAllower
grants permission to proceed with requests based on allow predicates.
A request is permitted to proceed if it is not denied by any ACLRegistryVetoer
and at least one ACLRegistryAllower
approves it.
Example
The following code registers a veto that denies all request to /deny
and a permission that allows any request to /allow
:
@Inject("acl-registry")
ACLRegistry registry;
@OnInit
public void init() {
registry.registerVeto(r -> r.getPath().equals("/deny"));
registry.registerAllow(r -> r.getPath().equals("/allow"));
}