What is the most effective way to design a MongoDB query that can assess a permission hierarchy involving both allow and deny rules within my Access Control

Creating a per-document access control list (ACL) for an application using MongoDB comes with specific requirements:

  1. Each find or update 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.
  2. Document permissions should cater to unauthorized users, authorized users, and specific users.
  3. Permissions must be defined as either allow or deny.

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.

Answer №1

This solution provides a basic approach, although it may exhibit some performance issues.

function createAclQuery(userId, permission) {
    // If userId is provided, assess the entire permissions hierarchy
    return userId ? {
        $or: [
            // Starting from the broadest permission level, evaluate downward
            {
                "security.unauthorized.allow": permission,
                "security.unauthorized.deny": {$ne: permission},

                // Check for absence of denial at finer levels
                "security.authorized.deny": {$ne: permission},
                $or: [
                    {"security.users._id": {$ne: userId}},
                    {"security.users._id": userId, "security.users.deny": {$ne: permission}}
                ]
            },
            {
                "security.authorized.allow": permission,
                "security.authorized.deny": {$ne: permission},

                // Verify lack of denial at finer levels
                $or: [
                    {"security.users._id": {$ne: userId}},
                    {"security.users._id": userId, "security.users.deny": {$ne: permission}}
                ]
            },
            {
                "security.users._id": userId,
                "security.users.allow": permission,
                "security.users.deny": {$ne: permission}
            }
        ]
    } : {
        // If userId is null or undefined, only consider unauthorized permission
        "security.unauthorized.allow": permission,
        "security.unauthorized.deny": {$ne: permission}
    }
};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Trying to assign a value to 'currentStatus' using "this" on an undefined property

In my attempt to display the state of formSubmit in vue js, I've encountered an issue. My lack of familiarity with using "this" has led to errors in the code, particularly when trying to indicate the status using "this.currentStatus". This is the cod ...

What is the best location for storing an individual user's UI configuration settings?

I recently built an ASP.NET MVC app using KnockoutJS, incorporating a data grid which allows for customizable visibility of columns, column sizes, and filter generation in JSON format. This grid also supports remote sorting, filtering, and ordering. User ...

Prevent Copying and Pasting within the Text Editor

@if (Model.CanMaintainNcrLineManagement) { <tr> <td>@Html.TextAreaFor(model => model.Description, new { id = "txArNcrLineDescriptionValue", @style = "height:520px" })</td> </tr> } else { <tr class="read-only-editor"> &l ...

Stop jQuery function from activating twice during scrolling

I'm looking for a solution to optimize my code that detects if an element is in the viewport and triggers certain actions. Currently, it re-runs the code every time a scroll event occurs when the element is above the fold. Is there a way to make it on ...

Struggling with incorporating MongoDB as the backend for a Django project running on Django 1.7

I followed a tutorial to set up my app, which can be found at . However, when I configured my backend in settings.py as per the instructions provided in the link, I encountered the following error: NotImplementedError: subclasses of BaseDatabaseIntrospec ...

Having trouble retrieving the attribute of an appended element in jQuery?

I am facing an issue where I am unable to retrieve the ID-attribute of an element that has been appended into my HTML. Each time I try, the result is always 'undefined'. How can I resolve this problem? jQuery('form#formular').append(&a ...

Utilizing Slick Carousel to dynamically apply animation classes to active slides

I am attempting to focus on the active slide within my slick carousel created by ken wheeler in order to apply an animation to the p element within that slide. Therefore, when the slide is active, the p element will bounce in (or something), and then when ...

An issue in cordova android related to cross-domain restrictions

My small cordova app utilizes beacon plugins to send a get request to a specific page once beacons are discovered. However, I have been facing issues with sending the get request to my server using the code snippet below with jsonp. Despite trying out di ...

Verify that the computer is connected to the Internet by sending an ajax request to Google

Whenever I need to test my internet connection, I rely on the following code snippet: const checkInternetConnection = () => { $('input').ajaxError(function(){ alert("failed"); }); $.get('http://www.google.com', f ...

Making HTTP requests in a specific sequence in Angular 2

In my Angular 2 App, I am working on implementing two sequential HTTP requests. The goal is to save the refresh and access tokens in LocalStorage when the user clicks the login button, followed by executing another HTTP request to fetch User info. I came a ...

What is the best way to find the actual position of this user within my array?

I found this example in the Vue JS documentation here When using a filtered v-for, how can I obtain the actual index of a user in the array, rather than the index of the current iteration? <div id="filter-by-example"> <ul> <li v-for= ...

The AJAX function fails to trigger the MVC controller method

I'm attempting to enable inline editing by cell, rather than by row, when double-clicking. Although the initial setup is working, it's not updating the record as expected - the "SaveCustomer" call to the controller isn't triggered. Can anyon ...

Having Trouble Concealing an Element with jQuery UI

I am attempting to implement a jQuery slide effect where pressing a button causes the first paragraph tag to slide out of view and the second paragraph tag to slide into the viewport. Despite utilizing jQuery UI for the slide effects, I am encountering dif ...

Warning: Attempting to destructure the property 'name' from 'req.body', which is undefined, is causing a TypeError

Currently, I am diving into the world of MERN Stack web development and running into a unique issue. When using Postmate to input data from the body to the database, everything works smoothly when done from the server.js file. However, when attempting the ...

Having difficulty accessing the sound file despite inputting the correct path. Attempts to open it using ./ , ../ , and ../../ were unsuccessful

While attempting to create a blackjack game, I encountered an issue. When I click the hit button, a king card picture should appear along with a sound. However, the sound does not play and the error message Failed to load resource: net::ERR_FILE_NOT_FOUND ...

Using the defer attribute on my script tag has caused a delay in loading my script due to its

Whenever I include the defer and async attributes in my <script src="/all.js" async defer></script> tags, the script within the HTML page stops functioning properly due to jQuery being loaded with defer as well. To work around this issue, I hav ...

Retrieving data from a C# datatable in JSON format and presenting it in a jQuery datatable

Recently, I've been diving into Jquery Datatable and trying to work with it using a JSON string. However, despite my efforts over the past couple of days, I haven't been able to get the desired output. Here's a snippet of my HTML : <bo ...

What is the best way to show various model schemas in the ngOnInit() function using Angular, Mongodb, and Express

I am facing a challenge loading two different model schemas onto my home-page.html using 'let cat of cats' and 'let dog of dogs'. When the home-page loads, I intend for each table to be populated with data specific to the animal, but it ...

The webpack development server is failing to inject CSS

Upon reviewing the stdout output after executing the script that triggers webpack-dev-server, it seems like the scss | css files are being processed and emitted correctly. However, when visiting "localhost:8080" in devtools, the CSS is not injected by the ...

Is there a way to keep a label in a Material-UI TextField from getting obscured by an adornment when the input is not in focus?

Here is the code for my custom TextField component: <TextField fullWidth: true, classes, helperText: errorText, FormHelperTextProps: getInputProps({ error }), InputProps: getInputProps({ error, endAdornment: error ? <Warning ...