Cascading Security Rules

I’m trying to figure out if this is possible or how to model it in Parse: a collaborative recipe library. If my app was single user, it’d be straight forward :blush: When adding collaboration, it gets more complicated.

Use Case
User A has a library of recipes. User A invites User B to their library and grants them ReadWrite access level.

In Parse
I’m planning having a Library and Recipe class (there’s likely going to be many more child classes to a Library). Granting access to User B could be implemented as an ACL. How do I grant access to all the recipes contained in this library?

Solution A
Iterate through children recipes and add User B to the ACL on each record.

Solution B
When creating a Library, create a role. When a recipe gets created, it gets added to this role. User A adds User B to the role and hence, is given the same ACL on all the records.
Con: There will be as many Roles as there are Library! Is that a problem?

Are there other ways to model this?

Thanks!

There is another way not using ACLs, which is creating a collection Invites where you store which user invited which other user to their library. Then you lock down the Library collection to be accessible with master key only and create a Cloud Function where you check access rights.

This gives you full flexibility for custom rights management that may not be currently possible with ACLs. For example: allowed to only access the number of recipes in the library without invite, but access the full library with invite.

Right, CloudFunctions is also what the Secure Todo list example on back4app recommends. The problem is that CloudFunctions won’t allow me use any of the PFObject stuff, including LiveQueries. I’m left reimplementing serialization from the function’s response. :frowning:

Am I right?

OK I read a little further on cloud functions: they return AnyCodables in ParseSwift, which I assume I can use to build PFObjects. Still investigating LiveQueries…

I don’t know how I didn’t find this link before, this seems to prove that my solution B is doable. My question remains: will having multiple roles cause performance issues?

Here you mention AnyCodable and ParseSwift (ParseSwift SDK), but then mention PFObject (iOS Obj-c SDK), are you using ParseSwift or iOS Objc SDK?

If you are using ParseSwift, the CloudCode returns AnyCodable because your return type can be anything (I might/should be able to improve this by allowing the dev to specify the type, but I need to look into it). This doesn’t stop you from casting the result to your Library type. You can cast an AnyCodable to anything, lets say a string dictionary, [String: String] by doing something like:

I don’t know how I didn’t find this link before, this seems to prove that my solution B is doable. My question remains: will having multiple roles cause performance issues?

I’ll leave this for someone on the server team to answer…

You could use afterLiveQueryEvent with request.sendEvent to prevent objects being sent.

Personally, I would utilize the full power of roles, with variable names. That way read access/write access in managed by the role (you won’t need to worry about LQ sending unsecure events), and role.users is managed by a one to many relation. You can index role.name faster results.

I’d be happy to stand corrected but according to the docs:

Parse.Role is a subclass of Parse.Object , and has all of the same features, such as a flexible schema, automatic persistence, and a key value interface. All the methods that are on Parse.Object also exist on Parse.Role

So I would assume having multiple roles would have the same performance impact as having a large collection.

1 Like

Alright I got confused! I plan on using ParseSwift, therefore exit PFObject!

Thanks! I think I’ll go the Role path, test it all and see.

Thanks again for the replies, I appreciate the help!

I’ve been able to play around a little with ACLs and roles. This is all working very nicely.

I am also now understanding that Roles also have ACLs! This means I’ll be able to restrict who can change the role to library owner’s (through code, not through a pointer).

Current Solution
classes: Recipe, Library. Recipe has a library reference column but it’s only for query purposes.
I’ll create a “library-OBJECTID-read” and a “library-OBJECTID-readwrite” role for each Library row. When creating new recipes, I’ll add both roles.
When a user wants to share a library, they’ll be editing the role to add users. That’s it! The invitee will instantly see the new records.

I’ll implement something more fancier (like public invite links or invite codes) and use a CloudFunction to accept the invite and get added to the role (using the master key). But that’ll be another test :blush:

Just want to mention that when you get to Cloud Functions I improved the return types (based on when you asked the original question in this thread) when using ParseSwift in the 1.2.0 release, you can see the PR here:

1 Like

Awesome! I’m actually playing with that CloudFunctions now, I’ll update to 1.2.