I'm working on an application supported by CouchDB. Essentially, I want to create a database for each individual user of my app. To accomplish this, the admin user will create the database, but going forward, the user will need to access their database (using HTTP Auth over SSL). I've been having a hell of a time figuring this out.
The best resource I have found is in the CouchDB wiki, at this link:
http://wiki.apache.org/couchdb/Security_Features_Overview#Authorization
It suggests that you can set per-database authorization by creating a document called "_security" to which you add a hash of admins and readers. When I attempt to create that document, the message I get back is "Bad special document member: _security".
$ curl -X GET http://localhost:5984
{"couchdb":"Welcome","version":"1.0.1"}
Any help would be appreciated!
Cheers,
Aaron.
There should be no problem with that aproach.
Let's say you have a database "test", and have an admin account already:
curl -X PUT http://localhost:5984/test -u "admin:123"
Now you can create a _security document for it:
curl -X PUT http://localhost:5984/test/_security -u "admin:123" -d '{"admins":{"names":[], "roles":[]}, "readers":{"names":["joe"],"roles":[]}}'
Them only the user "joe" will be able to read the database. To create the user you must have already the sha1 hashed password:
curl -X POST http://localhost:5984/_users -d '{"_id":"org.couchdb.user:joe","type":"user","name":"joe","roles":[],"password_sha":"c348c1794df04a0473a11234389e74a236833822", "salt":"1"}' -H "Content-Type: application/json"
This user have the password "123" hashed using sha1 with salt "1" (sha1("123"+"1")), so he can read the database:
curl -X GET http://localhost:5984/test -u "joe:123"
He can read any document now on that database, and no other user (but him and admin) can.
UPDATED: Writer security
The above method issues the reader problem, but the reader permission here actually mean "read/write common docs", so it allows to write docs except for design-docs. The "admin"s in the _security doc are allowed to write do design-docs in this database.
The other approach, as taken from your own answer, is the "validate_doc_update", you can have a validate_doc_update as follow in a file:
function(new_doc, old_doc, userCtx) {
if(!userCtx || userCtx.name != "joe") {
throw({forbidden: "Bad user"});
}
}
And push it into a couchdb design:
curl -X PUT http://localhost:5984/test/_design/security -d "{ \"validate_doc_update\": \"function(new_doc,doc,userCtx) { if(userCtx || userCtx.name != 'joe') {throw({forbidden: 'Bad user'})}}\"}" --user 'admin:123'
Them "joe" can write to the database using Basic Authentication:
curl -X PUT http://localhost:5984/test/foobar -d '{"foo":"bar"}' -u 'joe:123'
As you also addressed you can use the _session api to get a cookie for authentication:
curl http://localhost:5984/_session -v -X POST -d 'name=joe&password=123' -H "Content-Type: application/x-www-form-urlencodeddata"
This will return a header like:
Set-Cookie: AuthSession=am9lOjRDRDE1NzQ1Oj_xIexerFtLI6EWrBN8IWYWoDRz; Version=1; Path=/; HttpOnly
So you can include the cookie "AuthSession=am9lOjRDRDE1NzQ1Oj_xIexerFtLI6EWrBN8IWYWoDRz" in your next requests and they will be authenticated.
I've been doing more research and testing, and I want to summarize where I've gotten to, and what still isn't working for me.
First off, apologies for those who read this question: I was looking for ways to set permissions for people to write, not read, the database. It turns out be be a big difference: the techniques for creating a "reader" are entirely different from creating a "writer" (that term actually doesn't exist, though I wonder why).
In brief: you have to add a user to the _users database, which is a list of the users that have access to any database in your CouchDB instance. I was able to do that by issuing a command similar to:
curl -X PUT http://admin:password@localhost:5984/_users/org.couchdb.user:username -d '{"type":"user", "hashed_password":"2bf184a2d152aad139dc4facd7710ee848c2af27", "name":"username", "roles":[]}'
Note you need to apparently namespace the user name with the "org.couchdb.user" prefix. I used a Ruby hashing method to get the hashed_password value:
require 'digest/sha1'
pass_hash = Digest::SHA1.hexdigest(password)
This gets an apparently valid user into the database. The next step is to assign that user as a "writer" (ha, there it is again!) for the new database that I created. So I might do something like:
curl -X PUT http://admin:password@localhost:5984/newdatabase
and then
curl -X PUT http://admin:password@localhost:5984/newdatabase/_design/security -d @security.json
That .json file contains a Javascript function for the "validate_doc_update" key, and that function looks like this:
function(new_doc, old_doc, userCtx) {
if(userCtx.name != username) {
throw({forbidden: "Please log in first."});
}
}
It's roundabout, but it makes sense. However, I now am running into a problem: apparently the userCtx variable doesn't get populated until the user is authenticated. This article suggests that all you have to do is pass the credentials through an HTTP request to a special _session database, like so:
curl -X POST http://username:password@localhost:5984/_session
I can do that for my admin user, and the userCtx var will be populated. But for my newly-created user, it fails:
$ curl http://org.couchdb.user:username:password@localhost:5984/_session
{"ok":true,"userCtx":{"name":null,"roles":[]},"info":{"authentication_db":"_users","authentication_handlers":["cookie","oauth","default"]}}
Note the userCtx hash is null. I wonder if that namespace thing is causing the problem? It's got a freakin' colon in it, so maybe there's some confusion about the password? I've tried making it without the namespace, and it doesn't work at all; at least here my request appears to be hitting the database and getting a response.
I'm stuck at this point. If anyone can check my assumptions and progress thus far, I hope we can all figure out how to make this work.
Thanks!
Aaron.
You may want to check out Matt Woodward's - The Definitive Guide to CouchDB Authentication and Security http://blog.mattwoodward.com/2012/03/definitive-guide-to-couchdb.html