Need help with channel structure

62 views
Skip to first unread message

Christoph Berlin

unread,
Apr 2, 2015, 2:46:24 PM4/2/15
to mobile-c...@googlegroups.com
Hi everyone,

we are struggling a bit a little bit with finding a suitable channel structure for our project. Here are the requirements:

1) Each user has a profile document (first name, last name, user ID, etc.) Those profile documents are initially accessible by the owner only
2) A user can create a project (represented by a project document that has a name, members, etc.)
3) The owner of the document can invite other users to join the project, in this case the members of the projects get added to the project channel

So that works, the users have access to the project file but how would we enable access to each user profile document so that users can see each other's information?  It seems easy but its not (for us at least)...

Another way to look at it, is the couchbase labs chat app. Instead of having all profile be accessible by everyone, how could we limit exposure by adding the chat profiles into a channel. New members would have to be added via email address...

Attempts: 
1) We initially thought that we add those profile documents to the project channel but that doesn't work because the profile document is not aware of all the projects the owner has access. The project file has a member array but the profile documents don't. We could maintain a manual configuration within the profile document (for example memberships) but that seems kind of backwards and hard to maintain.

2) Another idea was to have the sync function take care of it but that doesn't work because the sync function works on a document basis - meaning only when a document changes it triggers a change. We cannot update a different document than the one who triggered the change. 

3) Being tired of it we thought about just ignoring the fact of protecting the profile account and making them public but then they all get synced to each device...and that doesn't seem to make sense as well if you have a million profiles...

Does anyone have any ideas how to accomplish this? Any pointer is much appreciated it.
Christoph

Again: 


Project document -> creates project channel with members being able to access
Profile documents -> profile channel with owner access

How to enable sync for all profile documents of the members in the project.


Jakob Hoydis

unread,
Apr 3, 2015, 9:23:17 AM4/3/15
to mobile-c...@googlegroups.com
Hi,

One option would be to create a channel for every user to which he/she gets access.
The channel name is a simple function of the user id, e.g. 'user_'+id. The profile document of a user is the only document in this channel

The project document contains an array of the user ids participating in the project, e.g. userList. The sync function will give then every user access to all other user channels as well as to the channel to the project it self..

The sync function could possible look like something like this:

function (doc) {
switch (doc.type) {
case 'profile':
channel('user_'+doc.id); //add the profile to the user channel
access(doc.id,[ 'user_'+doc.id ]); //give the user access to his/her own channel
break; 

case 'project':
channel('project_'+doc.id); //create a channel for the project
           for (var i=0; i<doc.userList.length;i++) {
access(doc.userList[i],['project_'+doc.id]); //give each user access to the project channel
for (var j=0; j<doc.userList.length;j++) {
if (j !=i ) {
access(doc.userList[i],['user_'+doc.userList[j]]); //give each user access to the user channels of all other users 
}  
}
 } 
break;
}


Is this any helpful?

Cheers,

Jakob

Christoph Berlin

unread,
Apr 3, 2015, 12:53:38 PM4/3/15
to mobile-c...@googlegroups.com
Hey Jakob,

Thanks so much for your detailed response land this looks pretty neat! We didn't think of creating a channel for each user and then assigning others. Duh! 

But if I am not mistaken it doesn't handle removal of users because the already removed user and his channel don't get overwritten once the user is removed. We could identify the removed users as a difference of old doc and doc but then we cannot remove access of the other users of that channel?

Thanks Christoph


--
You received this message because you are subscribed to a topic in the Google Groups "Couchbase Mobile" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mobile-couchbase/bYq7K_tfLV8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mobile-couchba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/mobile-couchbase/32a6308b-f363-4656-8d59-2d22f7b0455f%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jakob Hoydis

unread,
Apr 5, 2015, 3:26:39 PM4/5/15
to mobile-c...@googlegroups.com
Hi Christop,
 
But if I am not mistaken it doesn't handle removal of users because the already removed user and his channel don't get overwritten once the user is removed. We could identify the removed users as a difference of old doc and doc but then we cannot remove access of the other users of that channel?

Yes, this is indeed a problem. I have spoken about a possible solution in a similar setting at a Couchbase meetup in London (https://spraed.net/spraeds/0029b75c84b8, starting from slide 35, https://www.youtube.com/watch?v=T2wl7363hws). Maybe this is already helpful to you.

Otherwise, I might have a possible solution for your problem which I will briefly outline below. Maybe there is a better way of doing it, but I have none for the moment :

When a new user is added to a project, you create for every user and every other user of the project a new document with content 

{
type : 'user-doc' 
userId1 : 'xxx',
userId2 : 'yyy',
delete : false
}

When you create this document, the sync function will give userId1 access to the channel of userId2:

function(doc) {
case 'user-doc':
channel('user_'+doc.userId1); //add the user-doc to the channel of the first user
if (!doc.delete) {
access(doc.userId1,[ 'user_'+doc.userId2 ]); //give the user access to the channel of the other user 
}
break; 
}

This means that the user-doc decides which user has access to which other user profiles. So if there are 3 user in a project, you need to create 3x2=6 user-docs in total.

Now, when you remove a user from a project, you update the corresponding delete-property in all user-docs which are concerned:

{
type : 'user-doc' 
userId1 : 'xxx',
userId2 : 'yyy',
delete : true 
}

The Sync function will now remove the channel containing the profile of user 2 from the list of user1's channels.
However, you still have the profile of user2 in user1's CBLite instance. Therefore, you will need to listen for this document change in your app and manually remove the profile from CBLite,

In other words, you use the user-doc to signal to your app that a profile should be deleted. 


However, a problem with this solution is that (i) the user-doc gets replicated although it is not needed anymore and (ii) the solution does not work when a user is logged in on multiple devices. To solve (i) you could let the app or (your server) delete the user-doc after the user profile was deleted. Solving (ii) requires some more effort.

Rather than just having a delete property in the document, you could have an array describing which Sync Gateway Session of a user has deleted the profile already, e.g.

{
type : 'user-doc' 
userId1 : 'xxx',
userId2 : 'yyy',
delete : true,
 sessions : [
{ sessionId : '1', removed : true}, { sessionId : '2', removed : false}
]
}

When an app removes a user profile, it will validate the sessions entry corresponding to its sessionId (e.g., its Sync Gateway Session Cookie). Only when all sessionIds in the array are validated. Sync Gateway will remove the user-doc from the user channel and you could deleted it from the data base.


I know this is a bit complicated but it should work. Let me know if anything is unclear with this solution or if you have an idea of how to improve it.

Another option could be to have document for every user which simply contains a list of all user profiles to which the user access. The Sync function would then use this document to give a user access to the channels containing the profiles. When a user is deleted from the list, the sync function revokes access to the channel. However, to delete it from CBLite, you needed to listen do changes in this document and remove the profile locally.

Cheers,

Jakob




Christoph Berlin

unread,
Apr 7, 2015, 11:55:40 AM4/7/15
to mobile-c...@googlegroups.com
Hey Jakob,

First of all thanks a ton for this extremely detailed outline and your help. It helped me a lot to understand where the weaknesses of SG are and that we are not alone. We initially thought that it must be us not understanding the concept but it seems that it is a much bigger issue for lots of people.

But why is that? I would think granting access is pretty much the basis for any production deployment and fine grained access control is a must. For example it would be relatively easy to accomplish our task if we would have the ability to update documents referenced from another document change. Meaning if project document gets updated, the sync function could gather all profile files with a reference to this project document and update the channel permissions. If course that would require features such as cross document reference and removal of channel access.

As for now you approach seems very holistic but it naturally is relatively complex. Looking at our challenges, we could get away with the fact that documents don't get removed from the local database once access is removed, a feature I am hoping will make it into the CB product soon.

So what about this slightly confusing but straight forward idea:
  1. User A joins project Z
  2. Project Z grants channel access to user A
  3. User A determines what projects he/she has access to during runtime
  4. Maintains an array of projects in userProfile.memberships which grants access to project channel
Once the user gets removed from project A, the item in memberships get removed as well, revoking access to the user profile.

Of course the elephant in the room is the fact that access removal is based on the runtime of the user but ultimately it only affects the user profile of that user meaning this user never logs in again people still have access to his profile information but then he couldn't update information as well...

Not pretty, in fact pretty nasty, but it works...

Thanks Christoph
Reply all
Reply to author
Forward
0 new messages