Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Advanced permissions with couchdb

We have a couchapp application with multiple users and a complex system of permissions. Our models are of two kinds: Foo and bar.

Users have admin access to their own Foo and Bar, and can be given permission to see, change and delete other people's Foo and bar.

Example:

User Sabrina has these models:

Foo {
  _id: 1
}
Foo {
  _id: 2
}
Bar {
  _id:1
}
Bar {
  _id:2
}

Of course the real models are larger documents.

She wants to give Giulia read access to her Foos, and read and write access to her first bar. She also wants Giulia not to be able to see her second Bar.

How can we model this kind of permissions in couchdb?

This is the solution we are using, but it seems a lot complex and we wonder if there's a simpler one:

We have a selection of roles: {username}:admin: can read, write, delete everything on every database related to the user {username}:foos:read: can read every document in the foos database related to the user {username}:foos:write: can write every document in the foos database related to the user {username}:{bar}:read: can read the Bar database related to the user {username}:{bar}:write: can write the Bar database related to the user

When Sabrina register to the app, we create a new sabrina-foos database, and we give to the user Sabrina the role sabrina:admin.

The sabrina-foos database is created with a _security document granting access to roles sabrina:admin, sabrina:foos:read, sabrina:foos:write.

The sabrina-foos database is created with a validation function which allows write access to the roles sabrina:admin, sabrina:foos:write.

When Sabrina decides to let Giulia see her foos, we give Giulia the role sabrina:foos:read

When Sabrina creates a new Bar called 'Bar 1', we create a new sabrina-bar_1 database.

The sabrina-bar_1 database is created with a _security document granting access to roles sabrina:admin, sabrina:bar_1:read, sabrina:bar_1:write

The sabrina-bar_1 database is created with a validation function which allows write access to the roles sabrina:admin, sabrina:bar_1:write.

Of course, being this a CouchApp, the creation of databases and editing of user models is handled by a Node Process.

like image 912
Matteo Suppo Avatar asked Jul 24 '14 14:07

Matteo Suppo


People also ask

Is CouchDB secure?

CouchDB 3.0 follows many of the security practices of the old school, SQL databases. You must supply an admin password upon installation, and all newly created databases are accessible only to server admin users by default, instead of world-readable and world-writeable. CouchDB 3.0 also adds more granular user roles.

What are the key features of CouchDB?

Features of CouchDBReplication: It provides the simplest form of replication and no other database is so simple to replicate. Document Storage: It is a NoSQL database that follows document storage where each field is uniquely named and contains values of various data types such as text, number, Boolean, lists, etc.

Does CouchDB have collections?

Couchdb does not have the concept of collections. However, you can achieve similar results using type identifiers on your documents in conjunction with Couchdb views. When you save a document in Couchdb add a field that specifies the type.


1 Answers

Your design is good. From your question it looks like you want document level authentication. couchdb offers no protection on individual documents so the only choice left is to partition the documents by databases and set read and write permissions on them.

There are two alternatives. The easiest one is to use rcouch's validate docs on read. I am not too sure but I think in couch db 2.0 all of rouch's features have been merged so if you are willing to wait a bit you can use couchdb 2.0 (it should be out any time now!) instead.

Another method is to do what you are doing but in a _users database. In a _users database you can create users and authenticate them with _sessions api. So here is how it will work.

  1. Every new user that you create will go in a _users database.
  2. Within the user document you can store a list of what documents the user is allowed to view or if you are worried that a single user document will grow too big then just store a pointer to maybe a database that contains the actual list the user can view.
  3. Use the _sessions api first to authenticate the user and then to get a list of docs that the user is authorized to read or edit.
  4. Finally fetch those documents and show it to the user.

The advantage of this method is that you will need a minimum of 2 and at most 3 http queries. One to authenticate and get a pointer to the list. Second to get the actual list of documents to be fetched. Third to fetch those documents. In return your architecture is greatly simplified.

A very cool property of _users database is that you can increase the auth cache size in the config to hold the _user objects in the memory so the access times will be very fast.

What If I have ten thousand documents but I can only retrieve one of them? Would the function be called ten thousand times?

I don't have rcouch installed at the moment but there is an easy way to test this by logging:-

function(doc, userCtx) {
    log("function called");
    if ((typeof doc.name !== 'undefined') && (doc.name != userCtx.name)) {
        throw({unauthorized: userCtx.name + ' cannnot read ' + doc._id});
    }
}

The log function will print a log message in the log files and also on the console couchdb is running on so you can see for yourself how many times the update function is being called. Would be nice if you could share the results :)

like image 194
Akshat Jiwan Sharma Avatar answered Oct 09 '22 07:10

Akshat Jiwan Sharma