Search code examples
parse-platformaclparse-cloud-codebefore-saveafter-save

parse.com inherited ACL + roles - afterSave or beforeSave, tricky scenario


Here's what I am trying to achieve but somehow I am stuck and I am not sure what's the proper approach, I cannot find any good examples of such case so I am seeking your help.

Each registered user can add new object to class "List". Once the new item is created I call afterSave function and assign proper ACL creating new role ("membersOf_" + List.id). Next, user can add new object to class "Items", which will store List.id as a reference and ACL for item should be inherited from list. Lists and Items can be shared between multiple users of their choice. There are few problems in such case:

  • when creating new List, I need to create new role and assign creator to it and add such role to created List
  • when creating new Item, I need to pass List.id as a payload and validate with cloud code if current user can create such item (assigned to specified List) by checking first if he has proper permissions to List
  • if permission check is ok, I need give this item the same ACL as List has and proceed with saving

Here's my afterSave for List, creating role properly and assigning ACL to List object. (1) I am missing adding this role to user (creator)

Parse.Cloud.afterSave("List", function(request, response) {
    var list = request.object;
    var user = Parse.User.current();
    if (list.existed()) {
        // quit on update, proceed on create
        return;
    }
    var roleName = "membersOf_" + list.id;
    var listRole = new Parse.Role(roleName, new Parse.ACL(user));
    return listRole.save().then(function(listRole) {
        var acl = new Parse.ACL();
        acl.setPublicReadAccess(false);
        acl.setPublicWriteAccess(false);
        acl.setReadAccess(listRole, true);
        acl.setWriteAccess(listRole, true);
        var itemData = new Parse.Object("List", {
            ACL: acl
        });
        return itemData.save('objectId', list.id);
    });

    // to do - add user to this role too
});

Here's my Item beforeSave to validate if user can actually create such object, I am checking if he can query the List table, if he get >0 results for such List that means he will be ok to adding an Item assigned to this List. (2) Missing ACL inheritance

Parse.Cloud.beforeSave("Item", function(request, response) {
    var item = request.object;

    var listId = request.object.get("list");
    var user = Parse.User.current();

    var List = Parse.Object.extend("List");
    var query = new Parse.Query(List);
    query.equalTo("objectId", listId);

    query.first({
        success: function(list) {
            if (list.id) {
                response.success();
            }
            else {
                response.error('No such list or you don\'t have permission to perform this operation.');
            }
        },
        error: function(error) {
            response.error(error);
        }
    });
});

Can someone point me to the proper solution or help solve that puzzle? I am missing two things: - (1) I need to add user (creator) to new role created in afterSave - (2) I need to add the same ACL to Item, inherit it from List object

I have tried many things, passing ACL in afterSave for Item, modifying payload in beforeSave. Many different functions following documentation and different examples, but still no luck. Any advice would be awesome!


Solution

  • Ok, I think I finally figured it out. Hopefully this will help someone in future.

    Here are final beforeSave and afterSave functions adding user to specified role and assigning the same ACL to Item object

    Parse.Cloud.afterSave("List", function(request, response) {
        var list = request.object;
        var user = Parse.User.current();
        if (list.existed()) {
            // quit on update, proceed on create
            return;
        }
        var roleName = "membersOf_" + list.id;
        var listRole = new Parse.Role(roleName, new Parse.ACL(user));
    
    //+ adding user to role in this line:
        listRole.relation("users").add(user);
    
        return listRole.save().then(function(listRole) {
            var acl = new Parse.ACL();
            acl.setPublicReadAccess(false);
            acl.setPublicWriteAccess(false);
            acl.setReadAccess(listRole, true);
            acl.setWriteAccess(listRole, true);
            var itemData = new Parse.Object("List", {
                ACL: acl
            });
            return itemData.save('objectId', list.id);
        });
    
        // to do - add user to this role too
    });
    
    
    Parse.Cloud.beforeSave("Item", function(request, response) {
        var item = request.object;
    
        var listId = request.object.get("list");
        var user = Parse.User.current();
    
    // + modifying payload with the same ACL here
        var acl = new Parse.ACL();
        acl.setPublicReadAccess(false);
        acl.setPublicWriteAccess(false);
        acl.setRoleWriteAccess("membersOf_" + listId, true);
        acl.setRoleReadAccess("membersOf_" + listId,  true);
        item.set('ACL', acl);
    
    
        var List = Parse.Object.extend("List");
        var query = new Parse.Query(List);
        query.equalTo("objectId", listId);
    
        query.first({
            success: function(list) {
                if (list.id) {
                    response.success();
                }
                else {
                    response.error('No such list or you don\'t have permission to perform this operation.');
                }
            },
            error: function(error) {
                response.error(error);
            }
        });
    });