Search code examples
javascriptparse-platformparse-cloud-code

How could you model Parse database for secure multi-user shared object access?


I'm trying to model my Parse database for ACL access with Roles. Roles will contain many users so that users can have groups with whom they share data. I am also locking all CLPs 100%. Only User will have public find() & write() access for login and creation.

I have not yet seen an exact situation where this was explained at all. I have seen many examples of relations, pointers and ACL Role access but none for shared, secured database. So far I cannot understand or extrapolate how I can have locked CLPs (no find()) and Role based ACL access. Can pointers or relations enable this somehow?

Current get colors either returns all objects with master key (obviously the opposite of what I want) or access denied.

Parse.Cloud.define("ccGetColors", async (request) => {
    let currentUser = request.user;
    let query = new Parse.Query("Color");
    // query.equalTo("user", currentUser);
    let results = await query.find({ useMasterKey: true });
    if(results.length === 0) throw new Error('No results found!');
    let steralizedResults = [];
    for (let i = 0; i < results.length; i++) {
      let object = results[i];
      let color = object.get("color");
      steralizedResults.push(color);
    }
    return steralizedResults;
  });

Adding user to roles

Parse.Cloud.define("ccAddUserToRole", async function(request) {
    let user = request.user;
    // Get group admin ID
    let userQuery = new Parse.Query("AddUser");
    let results = await userQuery.find({ useMasterKey: true });
    let resultObject = results[0];
    let groupAdminObject = resultObject.get("groupAdmin");
    let groupAdminID = groupAdminObject.id;
    // Concatonate group name
    let groupName = "Group_" + groupAdminID;
    // Query for group role with group ID
    let roleQuery = new Parse.Query(Parse.Role);
    roleQuery.contains("name", groupName);
    roleQuery.first({ useMasterKey: true }).then(function(role) {
        console.log(role);
        role.relation("users").add(user);
        role.save(null, { useMasterKey: true });
    });
  });
  1. Currently role points to users, this is working nicely adding users as per above.
  2. My color class has Role based ACL access and works fine for a single user.
  3. All CPLs are currently locked and I want to keep it that way.

Can someone help point me in the right direction? Either pointer or relation me in the right direction ;)


Solution

  • I got this to happen exactly as I intend, albeit, in a bit of a roundabout way not directly using relations or pointers. I'm sure there is an even easier way of doing this however I don't know about it.

    In my simple demo project I have a color class representing simple data, the class includes a color and a groupOwner. The group owner property is the role name which is saved with each new object. On query for all colors I'm checking for user's role and referencing this against the colors the user has access to. This way only authorized objects are returned based on user's role.

    In my project all CLPs are 100% locked except login and signup, and even these I'm looking at locking to call through cloud code. I want as much server control as possible and as much hidden code as possible.

    If anyone has input or a better way please share :)

    Parse.Cloud.define("ccSaveColor", async (request) => {
        let colorString = request.params.color;
        const query = await new Parse.Query(Parse.Role).equalTo('users', request.user).find({ useMasterKey: true })
        let role = await query[0];
        var groupACL = new Parse.ACL();
        groupACL.setRoleWriteAccess(role, true);
        groupACL.setRoleReadAccess(role, true);
        let Color = Parse.Object.extend("Color");
        let color = new Color();
        color.set("groupOwner", role.get("name"));
        color.set("color", colorString);
        color.setACL(groupACL);
        return await color.save(null, { useMasterKey: true });
      });
      
      Parse.Cloud.define("ccGetColors", async (request) => {
        const roleQuery = await new Parse.Query(Parse.Role).equalTo('users', request.user).find({ useMasterKey: true })
        let role = await roleQuery[0];
        let query = new Parse.Query("Color");
        query.equalTo("groupOwner", role.get("name"));
        let results = await query.find({ useMasterKey: true });
        if(results.length === 0) throw new Error('No results found!');
        let steralizedResults = [];
        for (let i = 0; i < results.length; i++) {
          let object = results[i];
          let color = object.get("color");
          steralizedResults.push(color);
        }
        return steralizedResults;
      });