1
votes

We are designing a collaboration software db structure and using MEAN stack, and my lack of ACL permissions experience prompted this question.

High level, the software is for collaborators and auditors to work/view tasks in projects.

The 'Project' entity/table in MongoDB is used to manage the ACL groups. Each project has a 'Collaborator' array of user pointers and a 'Auditor' array of user pointers. Users in the Collaborators array are added to the projects 'Collaborator' ACL group with read/write access and users in Auditors array are added to the projects auditors acl group, which has read only.

There is another entity called 'Task' in MongoDB and each task is tied to one, and only one, project. A project can have multiple tasks. The task get's the projects Collaborate ACL group and Auditor ACL group added to it.

So that all works great. Now, user Bob is a collaborator on project A and an auditor on project B. If Bob queries the database for tasks, he'll get back both tasks he can read/write on (project A) and tasks he can read only (project B). But how does the front end know which tasks he has write permissions and which he has read only? Because the front end needs to show 'edit' button only next to tasks user has write permissions for.

I see in ACL permissions, I can do a call to check if a user has write privileges on an individual object, but that's per object and would be performance prohibitive to do an additional call per object, even if it was done on the server before sending the initial query response.

Can I query the Task entity group in mongo with a filter like 'AND current user permissions contain Write'? Or how should this be handled?

1

1 Answers

1
votes

Having projectACL like this:

projectAcl{
    id:1,
    projectId:1,        

    // those subDocuments can be also a groupId and reference for exteral document
    auditors:[{userNAme:"John", userId:2},{userNAme:"Marry", userId:12}], 
    colaborators:[{userNAme:"Anna", userId:3},{userNAme:"Eric", userId:4}]
}

Then when calling for objects - server side code need to apply "effective permissions"

task{
    id:23,
    projectId:1,
    /*
    all fields needed here
    */
    // and here we have fields aded in serwerSide code
    readOnly:null //
}

readOnly - will be callculated as a quick check if we have an entry in acl list

below aggregate to list all projects for given user with ACL list - that could be useful to set task/projects permissions or adding an extra layer security using CQRS pattern

var givenUserId = 4;
var matchArraysByUser = {
    $match : {
        $or : [{
                "auditors.userId" : givenUserId
            }, {
                "colaborators.userId" : givenUserId
            }
        ]
    }
}

var filterArrysByUser = {
    $project : {
        _id : 0,
        projectId : 1, //all needed fields set to 1
        colaborator : {
            $filter : {
                input : "$colaborators",
                as : "colaborator",
                cond : {
                    $eq : ["$$colaborator.userId", givenUserId]
                }
            }
        },

        auditor : {
            $filter : {
                input : "$auditors",
                as : "auditor",
                cond : {
                    $eq : ["$$auditor.userId", givenUserId]
                }
            }
        },
    }
}

var group = {
    $group : {
        _id : givenUserId,
        "auditor" : {
            $addToSet : {
                $cond : {
                    if  : {
                        $ne : ["$auditor", []]
                    },
                then : "$projectId",
                else  : null
            }
        }
    },
    "colaborator" : {
        $addToSet : {
            $cond : {
                if  : {
                    $ne : ["$colaborator", []]
                },
            then : "$projectId",
            else  : null
        }
    }
}}}

db.projAcl.aggregate([matchArraysByUser, filterArrysByUser, group])

output:

{
    "_id" : 4.0,
    "auditor" : [ 
        null, 
        2.0
    ],
    "colaborator" : [ 
        1.0, 
        null
    ]
}