Return-Path: Delivered-To: apmail-couchdb-dev-archive@www.apache.org Received: (qmail 86686 invoked from network); 7 Feb 2010 09:20:04 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 7 Feb 2010 09:20:04 -0000 Received: (qmail 2439 invoked by uid 500); 7 Feb 2010 09:20:03 -0000 Delivered-To: apmail-couchdb-dev-archive@couchdb.apache.org Received: (qmail 2345 invoked by uid 500); 7 Feb 2010 09:20:02 -0000 Mailing-List: contact dev-help@couchdb.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@couchdb.apache.org Delivered-To: mailing list dev@couchdb.apache.org Received: (qmail 2335 invoked by uid 99); 7 Feb 2010 09:20:02 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 07 Feb 2010 09:20:02 +0000 X-ASF-Spam-Status: No, hits=-0.0 required=10.0 tests=SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: domain of b.candler@pobox.com designates 208.72.237.25 as permitted sender) Received: from [208.72.237.25] (HELO sasl.smtp.pobox.com) (208.72.237.25) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 07 Feb 2010 09:19:52 +0000 Received: from sasl.smtp.pobox.com (unknown [127.0.0.1]) by a-pb-sasl-quonix.pobox.com (Postfix) with ESMTP id DBFAD97FDB for ; Sun, 7 Feb 2010 04:19:29 -0500 (EST) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=pobox.com; h=date:from:to :subject:message-id:references:mime-version:content-type :in-reply-to; s=sasl; bh=ff3R28co3RSKCxETmuR4N62x7TQ=; b=NRvZMBv vBQLPEtBVxGYTjDEkUkDMEmuNuglCaRObaBEkAJU2kWXYI8zKWNwvDP5enljrz0G Ps73WQsVTtDLtfrL/EdtqBhO9ZwUWKn06WzAtY2w6kTONcoLo62IRQ6eDSe0PMaS jnxNRDY8jpStPI0/REU6v3pbAabUwC7j7hRM= DomainKey-Signature: a=rsa-sha1; c=nofws; d=pobox.com; h=date:from:to :subject:message-id:references:mime-version:content-type :in-reply-to; q=dns; s=sasl; b=HGkJKceLnW7yTyMCRmtGibaMa09fIDUIT IK8ckUDDm2BA9jo9LrDXMFPnkyYm4M4P3/fWA4bnehIKRUoNXzFcC0BGdQ4/whLJ 7bQL7wOieOjqrbYWrWAXHmRYEZiMJffmy77vTlbPOe6yvPuw9cefYl/SznEcQYDZ 7ffPKEARKA= Received: from a-pb-sasl-quonix. (unknown [127.0.0.1]) by a-pb-sasl-quonix.pobox.com (Postfix) with ESMTP id D8DC197FDA for ; Sun, 7 Feb 2010 04:19:29 -0500 (EST) Received: from zino (unknown [87.194.77.98]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by a-pb-sasl-quonix.pobox.com (Postfix) with ESMTPSA id 9721A97FD9 for ; Sun, 7 Feb 2010 04:19:29 -0500 (EST) Received: from lists by zino with local (Exim 4.69) (envelope-from ) id 1Ne3Ie-0001Fu-13 for dev@couchdb.apache.org; Sun, 07 Feb 2010 09:19:28 +0000 Date: Sun, 7 Feb 2010 09:19:28 +0000 From: Brian Candler To: dev@couchdb.apache.org Subject: Re: DB ACLs (was Re: 0.11 Release / Feature Freeze for 1.0) Message-ID: <20100207091927.GA4771@uk.tiscali.com> References: <20100203212426.GA10515@uk.tiscali.com> <015a01caa529$3d24e230$b76ea690$@com> <2C591A9F-55E4-49DD-A3E3-9BA075EAE633@apache.org> <20100205224225.GA8463@uk.tiscali.com> <20100206095856.GA5057@uk.tiscali.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.20 (2009-06-14) X-Pobox-Relay-ID: E52B1814-13C9-11DF-BF23-6AF7ED7EF46B-28021239!a-pb-sasl-quonix.pobox.com On Sat, Feb 06, 2010 at 10:52:57AM -0800, Chris Anderson wrote: > I'd be pretty surprised if the ACLs that ship with 0.11 are > significantly different from what I committed last week. I thought so, which is why I haven't coded up any alternative in erlang. > Can you explain your application needs? I'm pretty sure that the > current ACLs can support them. But if you have a common use case falls > out side what can be done with readers / admins / validations, maybe > there are minimal tweaks we can do to make it easier for you. OK. Let me start by outlining what I have now; you'll see that in many ways this *does* align well to couchdb's new security model. >From the user's point of view ----------------------------- A user signs up for a database. They are its initial administrator. They can grant access to other users. As well as read access, they can grant: [X] Update [X] Admin rights to those other users. "Update" allows database writes, and "Admin" allows adding new users and changing their update/admin rights flags. I plan to make "Update" more granular in future. For safety, an admin cannot remove themselves, nor change their own rights flags. They have to ask another admin on the database to do it for them. That's so that databases can't end up being accidentally orphaned, without any administrator, which would be a dead-end situation. When you login as a user, you could have access to multiple databases. In that case you are given a list to choose from. For audit purposes, all saves automatically update the "updated_by" and "updated_at" fields (with username and timestamp respectively). This is enforced for "Update" users. However an admin user is allowed to import a CSV file containing "updated_by" and "updated_at" columns, and those uploaded values are honoured. That is to make it possible to accurately restore a database from a CSV file. So, an admin user could destroy the audit information if they wanted to, but regular update users cannot. The current implementation -------------------------- Each database is a separate couchdb database, with all mediation via a Rails app. There is a global authorisation database with "User" and "Db" docs. "User" contains usernames and passwords, and system-wide prefs for that user (e.g. timezone) There is one "Db" doc per database, and its id is the same as the database name. It contains { "authz":{ "user1":[], "user2":["admin"], } } plus database-level preferences. By putting these docs in the global database rather than the databases to which they refer, I can use a view to find all databases that a user has access to. These docs give me what _readers, _admins and _security will provide. Issues with changing to couchdb native features ----------------------------------------------- (1) I could implement the "update" right in couchdb using the _security document, perhaps putting something like {"updaters":{ "names":[], "roles":[] } to be similar to _readers and _admins. This means that the authz info is spread across three places. In futon you either won't be able to set the "update" right at all, or you'll have to do it in a completely different way to readers and admins. (2) In couchdb, an "admin" confers two distinct rights: the ability to change rights on the database, and the ability to modify design docs. My "admin" right is only the first of these. So I have to either: (a) bite the bullet and let admin users be full database admins too, or (b) implement a more restricted "manager" right myself. This would require and external process sitting in front of couchdb, which ran with admin rights, and mediated requests for adding and removing users and rights. This would not be necessary if the access controls were in a "real" doc, because I could use validate_doc_update to limit who could add what access (in the same way as validate_doc_update already works in the _users database) (3) There's no concurrency control on _readers, _admins or _security. It *will* break if two people try to change them at once. Again, the workaround is for me not to grant any real database _admins, but have a front-end manager process which performs these changes. The front-end will need to contain a mutex to serialise these requests. (4) I am currently backing up the system just by dumping _all_docs, and there's enough info to restore the whole system from there. It's line-based and git-packs nicely. However once there are hidden _reader, _admin and _security resources, those will have to be backed up separately for each database. (5) I won't be able to use _all_dbs to show what databases the user has access to, either because it will show to much, or because it will be blocked to non-server-admins. This isn't a huge problem if I ask the user to type in the database name they want to access the first time they use it, and then I store it in some sort of persistent cache, so that next time they can select it from a list. A spare field in their _users database entry would be the obvious place, but it's not clear if this will be allowed going forward. (Futon uses a persistent cookie, I believe, which wouldn't work well for me because a user switching to a different browser or PC would lose the list of databases they had access to. Maybe using _users for the list of recent databases would be better for futon too). (6) Other privacy issues as described before: I don't want user A to see that user X exists on the system, and certainly not to see their encrypted password. Furthermore I don't want user A to know the names of any databases that they don't have access to. I can workaround the first by blocking read access to _users and implementing privileged services in front for changing passwords and preferences. The second requires me to put couchdb behind a HTTP reverse proxy, unless _all_dbs becomes an admin-only resource as has been proposed. (7) Timestamps are slightly awkward. I can update timestamps in an _update function, but not prevent users from going direct to the database. The best I can think of is to add a second layer of checks in validate_doc_update: for example, that the "updated_at" timestamp must be plus or minus two minutes from the current time, and "updated_by" field must match the userctx.name. So in summary, the main points are: * I can't trust end-users to be _admins, because there are too many ways they can shoot themselves in the foot, and couchdb doesn't provide validate_doc_update level of control. Result: I have to build external processes to mediate admin-type requests, and document the APIs. * It's all just rather messy. To check if they're an _admin I need to look in userctx. To check if they have update rights I need to look in the _security resource. To enable access to the database I need to list them in the _readers resource. Regards, Brian.