You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2013/06/21 23:53:10 UTC
git commit: updated refs/heads/master to 8d7ab8b
Updated Branches:
refs/heads/master 136b28991 -> 8d7ab8b18
Add a configurable whitelist of public user props
By default no user properties are public and attempts to view a users
document other than your own will return a 404. If the public_fields
setting of the users_db config section is set to a list of field
names, however, you will see that subset of fields for any user.
Also, if `public_fields` is set and non-empty,
`_users/_all_docs?include_docs=true` will return documents with stripped
field.
Contributed with code parts from @indutny
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/8d7ab8b1
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/8d7ab8b1
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/8d7ab8b1
Branch: refs/heads/master
Commit: 8d7ab8b18dd20f8785e69f4420c6f93a2edbfa60
Parents: 136b289
Author: Robert Newson <rn...@apache.org>
Authored: Fri Jun 21 11:01:13 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Fri Jun 21 22:49:46 2013 +0100
----------------------------------------------------------------------
etc/couchdb/default.ini.tpl.in | 2 ++
share/www/script/test/users_db_security.js | 44 +++++++++++++++++++++++++
src/couch_mrview/src/couch_mrview_http.erl | 43 ++++++++++++++++++++++--
src/couchdb/couch_users_db.erl | 15 +++++++--
4 files changed, 99 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/8d7ab8b1/etc/couchdb/default.ini.tpl.in
----------------------------------------------------------------------
diff --git a/etc/couchdb/default.ini.tpl.in b/etc/couchdb/default.ini.tpl.in
index 736d9cd..5eb7ebc 100644
--- a/etc/couchdb/default.ini.tpl.in
+++ b/etc/couchdb/default.ini.tpl.in
@@ -67,6 +67,8 @@ timeout = 600 ; number of seconds before automatic logout
auth_cache_size = 50 ; size is number of cache entries
allow_persistent_cookies = false ; set to true to allow persistent cookies
iterations = 10 ; iterations for password hashing
+; comma-separated list of public fields, 404 if empty
+; public_fields =
[cors]
credentials = false
http://git-wip-us.apache.org/repos/asf/couchdb/blob/8d7ab8b1/share/www/script/test/users_db_security.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/users_db_security.js b/share/www/script/test/users_db_security.js
index d439fcb..cdc3f17 100644
--- a/share/www/script/test/users_db_security.js
+++ b/share/www/script/test/users_db_security.js
@@ -256,6 +256,50 @@ couchTests.users_db_security = function(debug) {
// log in one last time so run_on_modified_server can clean up the admin account
TEquals(true, CouchDB.login("jan", "apple").ok);
});
+
+ run_on_modified_server([
+ {
+ section: "couch_httpd_auth",
+ key: "iterations",
+ value: "1"
+ },
+ {
+ section: "couch_httpd_auth",
+ key: "public_fields",
+ value: "name,type"
+ },
+ {
+ section: "admins",
+ key: "jan",
+ value: "apple"
+ }
+ ], function() {
+ var res = usersDb.open("org.couchdb.user:jchris");
+ TEquals("jchris", res.name);
+ TEquals("user", res.type);
+ TEquals(undefined, res.roles);
+ TEquals(undefined, res.salt);
+ TEquals(undefined, res.password_scheme);
+ TEquals(undefined, res.derived_key);
+
+ // log in one last time so run_on_modified_server can clean up the admin account
+ TEquals(true, CouchDB.login("jan", "apple").ok);
+
+ var all = usersDb.allDocs({ include_docs: true });
+ T(all.rows);
+ if (all.rows) {
+ T(all.rows.every(function(row) {
+ T(row.doc);
+ if (row.doc) {
+ return Object.keys(row.doc).every(function(key) {
+ return key === 'name' || key === 'type';
+ });
+ } else {
+ return false;
+ }
+ }));
+ }
+ });
};
usersDb.deleteDb();
http://git-wip-us.apache.org/repos/asf/couchdb/blob/8d7ab8b1/src/couch_mrview/src/couch_mrview_http.erl
----------------------------------------------------------------------
diff --git a/src/couch_mrview/src/couch_mrview_http.erl b/src/couch_mrview/src/couch_mrview_http.erl
index 91587f1..61db4c0 100644
--- a/src/couch_mrview/src/couch_mrview_http.erl
+++ b/src/couch_mrview/src/couch_mrview_http.erl
@@ -106,8 +106,22 @@ all_docs_req(Req, Db, Keys) ->
ok ->
do_all_docs_req(Req, Db, Keys);
_ ->
- throw({forbidden, <<"Only admins can access _all_docs",
- " of system databases.">>})
+ DbName = ?b2l(Db#db.name),
+ case couch_config:get("couch_httpd_auth",
+ "authentication_db",
+ "_users") of
+ DbName ->
+ case couch_config:get("couch_httpd_auth", "public_fields") of
+ undefined ->
+ throw({forbidden, <<"Only admins can access _all_docs",
+ " of system databases.">>});
+ _ ->
+ do_all_docs_req(Req, Db, Keys)
+ end;
+ _ ->
+ throw({forbidden, <<"Only admins can access _all_docs",
+ " of system databases.">>})
+ end
end;
false ->
do_all_docs_req(Req, Db, Keys)
@@ -126,7 +140,16 @@ do_all_docs_req(Req, Db, Keys) ->
Args = Args0#mrargs{preflight_fun=ETagFun},
{ok, Resp} = couch_httpd:etag_maybe(Req, fun() ->
VAcc0 = #vacc{db=Db, req=Req},
- couch_mrview:query_all_docs(Db, Args, fun view_cb/2, VAcc0)
+ DbName = ?b2l(Db#db.name),
+ Callback = case couch_config:get("couch_httpd_auth",
+ "authentication_db",
+ "_users") of
+ DbName ->
+ fun filtered_view_cb/2;
+ _ ->
+ fun view_cb/2
+ end,
+ couch_mrview:query_all_docs(Db, Args, Callback, VAcc0)
end),
case is_record(Resp, vacc) of
true -> {ok, Resp#vacc.resp};
@@ -154,6 +177,20 @@ design_doc_view(Req, Db, DDoc, ViewName, Keys) ->
end.
+filtered_view_cb({row, Row0}, Acc) ->
+ Row1 = lists:map(fun({doc, null}) ->
+ {doc, null};
+ ({doc, Body}) ->
+ Doc = couch_users_db:strip_non_public_fields(#doc{body=Body}),
+ {doc, Doc#doc.body};
+ (KV) ->
+ KV
+ end, Row0),
+ view_cb({row, Row1}, Acc);
+filtered_view_cb(Obj, Acc) ->
+ view_cb(Obj, Acc).
+
+
view_cb({meta, Meta}, #vacc{resp=undefined}=Acc) ->
Headers = [{"ETag", Acc#vacc.etag}],
{ok, Resp} = couch_httpd:start_json_response(Acc#vacc.req, 200, Headers),
http://git-wip-us.apache.org/repos/asf/couchdb/blob/8d7ab8b1/src/couchdb/couch_users_db.erl
----------------------------------------------------------------------
diff --git a/src/couchdb/couch_users_db.erl b/src/couchdb/couch_users_db.erl
index de76142..9b875ba 100644
--- a/src/couchdb/couch_users_db.erl
+++ b/src/couchdb/couch_users_db.erl
@@ -12,7 +12,7 @@
-module(couch_users_db).
--export([before_doc_update/2, after_doc_read/2]).
+-export([before_doc_update/2, after_doc_read/2, strip_non_public_fields/1]).
-include("couch_db.hrl").
@@ -101,10 +101,21 @@ after_doc_read(Doc, #db{user_ctx = UserCtx} = Db) ->
_ when Name =:= DocName ->
Doc;
_ ->
- throw(not_found)
+ Doc1 = strip_non_public_fields(Doc),
+ case Doc1 of
+ #doc{body={[]}} ->
+ throw(not_found);
+ _ ->
+ Doc1
+ end
end.
get_doc_name(#doc{id= <<"org.couchdb.user:", Name/binary>>}) ->
Name;
get_doc_name(_) ->
undefined.
+
+strip_non_public_fields(#doc{body={Props}}=Doc) ->
+ Public = re:split(couch_config:get("couch_httpd_auth", "public_fields", ""),
+ "\\s*,\\s*", [{return, binary}]),
+ Doc#doc{body={[{K, V} || {K, V} <- Props, lists:member(K, Public)]}}.