diff options
author | Jan Lehnardt <jan@apache.org> | 2019-08-26 10:09:41 +0200 |
---|---|---|
committer | Jan Lehnardt <jan@apache.org> | 2019-08-26 10:09:41 +0200 |
commit | a4fea4870b619b5537813c06ad7657c4afd1a4de (patch) | |
tree | 3fc61fc1ff09cdc4b4faf8746839596530886449 | |
parent | bde3a721b59ebf940c23bd7bcb170e00b98fa51d (diff) | |
download | couchdb-a4fea4870b619b5537813c06ad7657c4afd1a4de.tar.gz |
feat: add _users ddocs to _all_docs
-rw-r--r-- | src/couch/src/couch_db_updater.erl | 1 | ||||
-rw-r--r-- | src/couch/src/couch_doc.erl | 4 | ||||
-rw-r--r-- | src/couch/test/couchdb_access_tests.erl | 25 | ||||
-rw-r--r-- | src/couch_mrview/src/couch_mrview.erl | 47 |
4 files changed, 73 insertions, 4 deletions
diff --git a/src/couch/src/couch_db_updater.erl b/src/couch/src/couch_db_updater.erl index 47f1ff137..245554b44 100644 --- a/src/couch/src/couch_db_updater.erl +++ b/src/couch/src/couch_db_updater.erl @@ -338,6 +338,7 @@ refresh_validate_doc_funs(#db{name = <<"shards/", _/binary>> = Name} = Db) -> refresh_validate_doc_funs(Db0) -> Db = Db0#db{user_ctx=?ADMIN_USER}, {ok, DesignDocs} = couch_db:get_design_docs(Db), + % TODO: filter out non-admin ddocs ProcessDocFuns = lists:flatmap( fun(DesignDocInfo) -> {ok, DesignDoc} = couch_db:open_doc_int( diff --git a/src/couch/src/couch_doc.erl b/src/couch/src/couch_doc.erl index 76692d3ab..1ec31e2d0 100644 --- a/src/couch/src/couch_doc.erl +++ b/src/couch/src/couch_doc.erl @@ -399,6 +399,10 @@ is_deleted(Tree) -> false end. +get_access({Props}) -> + get_access(couch_doc:from_json_obj({Props})); +get_access(#doc{access=Access}) -> + Access. get_validate_doc_fun({Props}) -> get_validate_doc_fun(couch_doc:from_json_obj({Props})); diff --git a/src/couch/test/couchdb_access_tests.erl b/src/couch/test/couchdb_access_tests.erl index f985528f3..fca6bb060 100644 --- a/src/couch/test/couchdb_access_tests.erl +++ b/src/couch/test/couchdb_access_tests.erl @@ -92,6 +92,7 @@ access_test_() -> % _all_docs with include_docs fun should_let_admin_fetch_all_docs/2, fun should_let_user_fetch_their_own_all_docs/2, + fun should_let_user_fetch_their_own_all_docs_plus_users_ddocs/2, % _changes fun should_let_admin_fetch_changes/2, @@ -283,6 +284,30 @@ should_let_user_fetch_their_own_all_docs(_PortType, Url) -> ?_assertEqual(2, length(proplists:get_value(<<"rows">>, Json))). % TODO ?_assertEqual(2, proplists:get_value(<<"total_rows">>, Json)). +should_let_user_fetch_their_own_all_docs_plus_users_ddocs(_PortType, Url) -> + {ok, 201, _, _} = test_request:put(Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), + {ok, 201, _, _} = test_request:put(Url ++ "/db/_design/foo", + ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"_users\"]}"), + {ok, 201, _, _} = test_request:put(Url ++ "/db/_design/bar", + ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"houdini\"]}"), + {ok, 201, _, _} = test_request:put(Url ++ "/db/b", + ?USERX_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), + + % % TODO: add allowing non-admin users adding non-admin ddocs + % {ok, 201, _, _} = test_request:put(Url ++ "/db/_design/x", + % ?ADMIN_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), + + {ok, 201, _, _} = test_request:put(Url ++ "/db/c", + ?ADMIN_REQ_HEADERS, "{\"c\":3,\"_access\":[\"y\"]}"), + {ok, 201, _, _} = test_request:put(Url ++ "/db/d", + ?USERY_REQ_HEADERS, "{\"d\":4,\"_access\":[\"y\"]}"), + {ok, 200, _, Body} = test_request:get(Url ++ "/db/_all_docs?include_docs=true", + ?USERX_REQ_HEADERS), + {Json} = jiffy:decode(Body), + ?debugFmt("~nHSOIN: ~p~n", [Json]), + ?_assertEqual(3, length(proplists:get_value(<<"rows">>, Json))). + % _changes should_let_admin_fetch_changes(_PortType, Url) -> {ok, 201, _, _} = test_request:put(Url ++ "/db/a", diff --git a/src/couch_mrview/src/couch_mrview.erl b/src/couch_mrview/src/couch_mrview.erl index 72fbb92e7..645041b00 100644 --- a/src/couch_mrview/src/couch_mrview.erl +++ b/src/couch_mrview/src/couch_mrview.erl @@ -280,23 +280,62 @@ query_changes_access(Db, StartSeq, Fun, Options, Acc) -> VName = <<"_access_by_seq">>, query_view(Db, DDoc, VName, Args, Callback, Acc). +get_design_docs(Db) -> + {ok, DDocs} = couch_db:get_design_docs(Db), + lists:filter(fun({DDoc}) -> + couch_util:get_value(<<"_access">>, DDoc) =:= [<<"_users">>] + end, DDocs). + query_all_docs_access(Db, Args0, Callback0, Acc) -> % query our not yest existing, home-grown _access view. % use query_view for this. DDoc = access_ddoc(), UserCtx = couch_db:get_user_ctx(Db), UserName = UserCtx#user_ctx.name, - % TODO: add roles Args1 = prefix_startkey_endkey(UserName, Args0, Args0#mrargs.direction), Args = Args1#mrargs{reduce=false}, - % filter out the user-prefix from the key, so _all_docs looks normal - % this isn’t a separate function because I’m binding Callback0 and I don’t - % know the Erlang equivalent of JS’s fun.bind(this, newarg) + + % get all _users design docs so we can splice them into the result + % in the right places + DDocs = get_design_docs(Db), + couch_log:info("~nget_user_design_docs: DDocs ~p~n", [DDocs]), + DDIterator = couch_iter:start(length(DDocs)), + % couch_log:info("~nDDocs~p~n", [DDocs]), + Callback = fun ({row, Props}, Acc0) -> + + % filter out the user-prefix from the key, so _all_docs looks normal + % this isn’t a separate function because I’m binding Callback0 and I + % don’t know the Erlang equivalent of JS’s fun.bind(this, newarg) + [_User, Key] = proplists:get_value(key, Props), Row0 = proplists:delete(key, Props), Row = [{key, Key} | Row0], + + couch_log:info("~nRow~p~n", [Row]), + % if new row id is > than next ddocs id, call Callback0 with ddoc + % id first, move DDocs iterator one forward + DDNextIdx = couch_iter:next(DDIterator), + case DDNextIdx of + done -> done; + N -> + {DDNext} = lists:nth(N, DDocs), + DDID = couch_util:get_value(<<"_id">>, DDNext), + DDRev = couch_util:get_value(<<"_rev">>, DDNext), + case Key > DDID of + true -> + DDRow = [ + {key, DDID}, + {id, DDID}, + {value, DDRev}, + {doc, {DDNext}} + ], + Callback0({row, DDRow}, Acc0); + _Else + -> ok + end + end, Callback0({row, Row}, Acc0); (Row, Acc0) -> Callback0(Row, Acc0) |