Creating a per-document access control list (ACL) for an application using MongoDB comes with specific requirements:
- Each
find
orupdate
query can be expanded with a crafted ACL query to determine if the operation is permitted, without increasing database queries. This should not impact performance due to index intersection in this particular application. - Document permissions should cater to unauthorized users, authorized users, and specific users.
- Permissions must be defined as either
allow
ordeny
.
The challenge lies in implementing 'deny' permissions efficiently.
How can I formulate a MongoDB query to assess a permission chain involving both 'allow' and 'deny' specifications?
For example:
var PERMISSION = 1;
{
someData: "test",
// The ACL structure
security: {
unauthorized: {
allow: [PERMISSION]
},
authorized: {
allow: [PERMISSION]
},
users: [
{_id: "userId", allow:[], deny:[PERMISSION]}
]
}
}
This document grants 'PERMISSION' access to all unauthorized and authorized users, except for a user with ID "userId" who is denied 'PERMISSION'.
What MongoDB query would accurately evaluate this permission hierarchy?
Existing Query:
{"$or": [
{
"security.unauthorized.allow": PERMISSION,
"security.unauthorized.deny": {"$ne": PERMISSION}
},
{
"security.authorized.allow": PERMISSION,
"security.authorized.deny": {"$ne": PERMISSION}
},
{
"security.users._id": "userId",
"security.users.allow": PERMISSION,
"security.users.deny": {"$ne": PERMISSION}
}
]}
While this query successfully checks 'allow' permissions, it fails when encountering a 'deny' deeper in the chain.
The goal is to resolve the 'deny' permission issue without introducing additional inverted queries into the chain.
If there was a way to combine array element matching with denial checks, such as "security.users._id": "userId"
paired with
"security.users.deny": {$ne: permission}
, then the 'deny' problem could be resolved by verifying the finest-grain 'deny' at the top of the chain and the 'allow' at the bottom.
An '$xor' operator could potentially address this challenge:
{"$or": [
{
"security.unauthorized.allow": PERMISSION,
"security.unauthorized.deny": {"$ne": PERMISSION},
"security.authorized.deny": {"$ne": PERMISSION},
"$xor": [
{"security.users._id": "userId"},
{"security.users.deny": PERMISSION}
]
},
{
"security.authorized.allow": PERMISSION,
"security.authorized.deny": {"$ne": PERMISSION}
},
{
"security.users._id": "userId",
"security.users.allow": PERMISSION,
"security.users.deny": {"$ne": PERMISSION}
}
]}
Although constructing an '$xor' operation is possible, the resultant performance may not be optimal.
Alternative schema and structural suggestions are also welcomed.