Permalink
Browse files

Add dropbox databases support.

Dropobx databases are databases only acceping write but can only be read
by selected users. Only nodes' admins or dropbox members can read
documents
that have  been dropped in a dropbox database.

To mark a database as a Drop Box, add the property `{"dropbox": true}`
to the security object.

Dropbox members are admins (global or db admins) and users sets in the
`dropbox_members` property:

    {"dropbox_members": {"names": [], "roles": []}}

Example of usage:

    $ curl -XPUT admin:test@127.0.0.1:5984/testdb
    {"ok":true}
    $ curl -XPUT 127.0.0.1:5984/testdb/test -d'{}'
    {"ok":true,"id":"test","rev":"1-967a00dff5e02add41819138abb3284d"}
    $ curl -XPUT 127.0.0.1:5984/testdb/test1 -d'{}'
    {"ok":true,"id":"test1","rev":"1-967a00dff5e02add41819138abb3284d"}
    $ curl -XGET 127.0.0.1:5984/testdb/_all_docs
    {"total_rows":2,"offset":0,"rows":[
    {"id":"test","key":"test","value":{"rev":"1-967a00dff5e02add41819138abb3284d"}},
    {"id":"test1","key":"test1","value":{"rev":"1-967a00dff5e02add41819138abb3284d"}}]}
    $ curl -XGET 127.0.0.1:5984/testdb/test1
    {"_id":"test1","_rev":"1-967a00dff5e02add41819138abb3284d"}
    $ curl -XPUT admin:test@127.0.0.1:5984/testdb/_security -d'{"dropbox": true}'
    {"ok":true}
    $ curl -XGET 127.0.0.1:5984/testdb/test1
    {"error":"forbidden","reason":"Only administrators can view docs in a dropbox database."}
    $ curl -XGET 127.0.0.1:5984/testdb/_all_docs
    {"error":"forbidden","reason":"Only admins can access _all docs"}
    $ curl -XPUT 127.0.0.1:5984/testdb/test2 -d'{}'
    {"ok":true,"id":"test2","rev":"1-967a00dff5e02add41819138abb3284d"}
    $ curl -XGET 127.0.0.1:5984/testdb/test2
    {"error":"forbidden","reason":"Only administrators can view docs in a dropbox database."}
    $ curl -XGET admin:test@127.0.0.1:5984/testdb/_all_docs
    {"total_rows":3,"offset":0,"rows":[
    {"id":"test","key":"test","value":{"rev":"1-967a00dff5e02add41819138abb3284d"}},
    {"id":"test1","key":"test1","value":{"rev":"1-967a00dff5e02add41819138abb3284d"}},
    {"id":"test2","key":"test2","value":{"rev":"1-967a00dff5e02add41819138abb3284d"}}
    ]}
  • Loading branch information...
1 parent 27eb739 commit 742846156eb5b881f88b88c6deecbef4e66ed2a0 @benoitc benoitc committed May 7, 2012
@@ -30,6 +30,7 @@
-export([changes_since/4,changes_since/5,read_doc/2,new_revid/1]).
-export([check_is_admin/1, check_is_member/1]).
-export([reopen/1, is_system_db/1, compression/1]).
+-export([is_dropbox/1, get_dropbox_members/1, check_is_dropbox_member/1]).
-include("couch_db.hrl").
@@ -317,6 +318,7 @@ get_design_docs(Db) ->
{ok, _, Docs} = couch_btree:fold(by_id_btree(Db), FoldFun, [], KeyOpts),
Docs.
+
check_is_admin(#db{user_ctx=#user_ctx{name=Name,roles=Roles}}=Db) ->
{Admins} = get_admins(Db),
AdminRoles = [<<"_admin">> | couch_util:get_value(<<"roles">>, Admins, [])],
@@ -359,6 +361,39 @@ check_is_member(#db{user_ctx=#user_ctx{name=Name,roles=Roles}=UserCtx}=Db) ->
end
end.
+check_is_dropbox_member(#db{user_ctx=#user_ctx{name=Name,
+ roles=Roles}=UserCtx}=Db) ->
+ case (catch check_is_admin(Db)) of
+ ok -> ok;
+ _ ->
+ {Members} = get_dropbox_members(Db),
+ DropboxRoles = couch_util:get_value(<<"roles">>, Members,[]),
+ WithAdminRoles = [<<"_admin">> | DropboxRoles],
+ DropboxNames = couch_util:get_value(<<"names">>, Members,[]),
+ case DropboxNames ++ DropboxRoles of
+ [] ->
+ % no dropbox members, only admins can access to this
+ throw({forbidden, <<"Only admins can access to",
+ " this dropboxdb.">>});
+ _Else ->
+ case WithAdminRoles -- Roles of
+ WithAdminRoles -> % same list, not an reader role
+ case DropboxNames -- [Name] of
+ DropboxNames -> % same names, not a reader
+ ?LOG_DEBUG("Not a dropbox member: UserCtx ~p " ++
+ "vs Names ~p Roles ~p",[UserCtx, DropboxNames,
+ WithAdminRoles]),
+ throw({unauthorized,
+ <<"You are not authorized to access this db.">>});
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end
+ end
+ end.
+
get_admins(#db{security=SecProps}) ->
couch_util:get_value(<<"admins">>, SecProps, {[]}).
@@ -367,6 +402,9 @@ get_members(#db{security=SecProps}) ->
couch_util:get_value(<<"members">>, SecProps,
couch_util:get_value(<<"readers">>, SecProps, {[]})).
+get_dropbox_members(#db{security=SecProps}) ->
+ couch_util:get_value(<<"dropbox_members">>, SecProps, {[]}).
+
get_security(#db{security=SecProps}) ->
{SecProps}.
@@ -379,6 +417,9 @@ set_security(#db{update_pid=Pid}=Db, {NewSecProps}) when is_list(NewSecProps) ->
set_security(_, _) ->
throw(bad_request).
+is_dropbox(#db{security=SecProps}) ->
+ couch_util:get_value(<<"dropbox">>, SecProps, false).
+
validate_security_object(SecProps) ->
Admins = couch_util:get_value(<<"admins">>, SecProps, {[]}),
% we fallback to readers here for backwards compatibility
@@ -1316,7 +1357,19 @@ make_doc(#db{updater_fd = Fd} = Db, Id, Deleted, Bp, RevisionPath) ->
atts = Atts,
deleted = Deleted
},
- after_doc_read(Db, Doc).
+
+ case is_dropbox(Db) of
+ true ->
+ case (catch couch_db:check_is_dropbox_member(Db)) of
+ ok ->
+ Doc;
+ _ ->
+ throw({forbidden, <<"Only administrators can view ",
+ "docs in a dropbox database.">>})
+ end;
+ _ ->
+ after_doc_read(Db, Doc)
+ end.
after_doc_read(#db{after_doc_read = nil}, Doc) ->
@@ -89,6 +89,18 @@ handle_design_req(#httpd{
false -> ok
end,
+ case couch_db:is_dropbox(Db) of
+ true ->
+ case (catch couch_db:check_is_dropbox_member(Db)) of
+ ok -> ok;
+ _ ->
+ throw({forbidden, <<"Only admins can access design document",
+ "acions for dropbox databases">>})
+ end;
+ false ->
+ ok
+ end,
+
% load ddoc
DesignId = <<"_design/", DesignName/binary>>,
DDoc = couch_httpd_db:couch_doc_open(Db, DesignId, nil, [ejson_body]),
@@ -103,18 +103,31 @@ handle_cleanup_req(Req, _Db) ->
all_docs_req(Req, Db, Keys) ->
case couch_db:is_system_db(Db) of
true ->
- case (catch couch_db:check_is_admin(Db)) of
+ all_doc_if_admin(Req, Db, Keys,
+ fun couch_db:check_is_admin/1,
+ <<"Only admins can access _all_docs",
+ " of system databases.">>);
+ false ->
+ case couch_db:is_dropbox(Db) of
+ true ->
+ all_doc_if_admin(Req, Db, Keys,
+ fun couch_db:check_is_dropbox_member/1,
+ <<"Only admins can access _all docs",
+ " in a dropbox db">>);
+ false ->
+ do_all_docs_req(Req, Db, Keys)
+ end
+ end.
+
+
+all_doc_if_admin(Req, Db, Keys, CheckFun, Msg) ->
+ case (catch CheckFun(Db)) of
ok ->
do_all_docs_req(Req, Db, Keys);
_ ->
- throw({forbidden, <<"Only admins can access _all_docs",
- " of system databases.">>})
- end;
- false ->
- do_all_docs_req(Req, Db, Keys)
+ throw({forbidden, Msg})
end.
-
do_all_docs_req(Req, Db, Keys) ->
Args0 = parse_qs(Req, Keys),
ETagFun = fun(Sig, Acc0) ->

0 comments on commit 7428461

Please sign in to comment.