You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ja...@apache.org on 2019/11/23 12:58:21 UTC

[couchdb] branch access updated (a4fea48 -> bd64cdf)

This is an automated email from the ASF dual-hosted git repository.

jan pushed a change to branch access
in repository https://gitbox.apache.org/repos/asf/couchdb.git.


    from a4fea48  feat: add _users ddocs to _all_docs
     new 91c0f89  add iter file
     new a0936ce  chore: abort attempt at auto-ddoc injection
     new 2000daa  feat: ignore access ddocs for views/shows/lists, etc.
     new bd64cdf  fix: offset and test execution

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/chttpd/src/chttpd_show.erl                     |  10 +
 src/chttpd/src/chttpd_view.erl                     |  12 +-
 src/couch/src/couch_access_native_proc.erl         |   4 +-
 src/couch/src/couch_changes.erl                    |   2 +
 src/couch/src/couch_db.erl                         |  24 ++-
 src/couch/src/couch_db_updater.erl                 |  11 +-
 src/couch/src/couch_doc.erl                        |  11 +-
 src/couch/src/couch_util.erl                       |   7 +
 src/couch/test/couchdb_access_tests.erl            | 205 ++++++++++++++++-----
 src/couch_mrview/src/couch_mrview.erl              |  54 +-----
 .../src/ddoc_cache_entry_validation_funs.erl       |   4 +-
 11 files changed, 224 insertions(+), 120 deletions(-)


[couchdb] 01/04: add iter file

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jan pushed a commit to branch access
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 91c0f893d7d18a8adfa944c88d5cfe1148733440
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Mon Sep 9 12:45:08 2019 +0200

    add iter file
---
 src/couch/src/couch_iter.erl | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/src/couch/src/couch_iter.erl b/src/couch/src/couch_iter.erl
new file mode 100644
index 0000000..a755d8d
--- /dev/null
+++ b/src/couch/src/couch_iter.erl
@@ -0,0 +1,27 @@
+-module(couch_iter).
+-export([start/1, next/1, counter/2]).
+
+start(Max) ->
+    couch_log:info("~nstarting iter with Max: ~p~n", [Max]),
+    P = spawn_link(couch_iter, counter, [0, Max]),
+    couch_log:info("~ndd_iter_start: ~p~n", [P]),
+    P.
+
+counter(N, N) ->
+    couch_log:info("~ndd_counterNN: ~p~n", [N]),
+    receive {From, next} ->
+        From ! {self(), done}
+    end;
+counter(N, Max) ->
+    couch_log:info("~ndd_counterN: ~p Max: ~p ~n", [N, Max]),
+    receive {From, next} ->
+        From ! {self(), N + 1},
+        counter(N + 1, Max)
+    end.
+
+next(Iter) ->
+    couch_log:info("~nnext~n", []),
+    Iter ! {self(), next},
+    receive
+        {Iter, Answer} -> Answer
+    end.


[couchdb] 03/04: feat: ignore access ddocs for views/shows/lists, etc.

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jan pushed a commit to branch access
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 2000daae2d8b84b7a7e3d3c1c8606c12bd97188c
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sun Oct 20 11:18:18 2019 +0200

    feat: ignore access ddocs for views/shows/lists, etc.
---
 src/chttpd/src/chttpd_show.erl                     | 10 ++++++
 src/chttpd/src/chttpd_view.erl                     | 12 ++-----
 src/couch/src/couch_changes.erl                    |  2 ++
 src/couch/src/couch_db.erl                         | 24 +++++++++----
 src/couch/src/couch_db_updater.erl                 | 11 ++++--
 src/couch/src/couch_doc.erl                        | 11 +++++-
 src/couch/src/couch_util.erl                       |  7 ++++
 src/couch/test/couchdb_access_tests.erl            | 39 ++++++++++++++++++++--
 .../src/ddoc_cache_entry_validation_funs.erl       |  4 ++-
 9 files changed, 98 insertions(+), 22 deletions(-)

diff --git a/src/chttpd/src/chttpd_show.erl b/src/chttpd/src/chttpd_show.erl
index c6d232c..bb1d52f 100644
--- a/src/chttpd/src/chttpd_show.erl
+++ b/src/chttpd/src/chttpd_show.erl
@@ -34,6 +34,8 @@ handle_doc_show_req(#httpd{
         path_parts=[_, _, _, _, ShowName, DocId]
     }=Req, Db, DDoc) ->
 
+    ok =  couch_util:validate_design_access(DDoc),
+
     % open the doc
     Options = [conflicts, {user_ctx, Req#httpd.user_ctx}],
     Doc = maybe_open_doc(Db, DocId, Options),
@@ -46,6 +48,8 @@ handle_doc_show_req(#httpd{
         path_parts=[_, _, _, _, ShowName, DocId|Rest]
     }=Req, Db, DDoc) ->
 
+    ok =  couch_util:validate_design_access(DDoc),
+
     DocParts = [DocId|Rest],
     DocId1 = ?l2b(string:join([?b2l(P)|| P <- DocParts], "/")),
 
@@ -103,11 +107,13 @@ show_etag(#httpd{user_ctx=UserCtx}=Req, Doc, DDoc, More) ->
 handle_doc_update_req(#httpd{
         path_parts=[_, _, _, _, UpdateName]
     }=Req, Db, DDoc) ->
+    ok =  couch_util:validate_design_access(DDoc),
     send_doc_update_response(Req, Db, DDoc, UpdateName, nil, null);
 
 handle_doc_update_req(#httpd{
         path_parts=[_, _, _, _, UpdateName | DocIdParts]
     }=Req, Db, DDoc) ->
+    ok =  couch_util:validate_design_access(DDoc),
     DocId = ?l2b(string:join([?b2l(P) || P <- DocIdParts], "/")),
     Options = [conflicts, {user_ctx, Req#httpd.user_ctx}],
     Doc = maybe_open_doc(Db, DocId, Options),
@@ -159,12 +165,14 @@ handle_view_list_req(#httpd{method=Method,
         path_parts=[_, _, DesignName, _, ListName, ViewName]}=Req, Db, DDoc)
         when Method =:= 'GET' orelse Method =:= 'OPTIONS' ->
     Keys = chttpd:qs_json_value(Req, "keys", undefined),
+    ok =  couch_util:validate_design_access(DDoc),
     handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, Keys);
 
 % view-list request with view and list from different design docs.
 handle_view_list_req(#httpd{method=Method,
         path_parts=[_, _, _, _, ListName, DesignName, ViewName]}=Req, Db, DDoc)
         when Method =:= 'GET' orelse Method =:= 'OPTIONS' ->
+    ok =  couch_util:validate_design_access(DDoc),
     Keys = chttpd:qs_json_value(Req, "keys", undefined),
     handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, Keys);
 
@@ -174,6 +182,7 @@ handle_view_list_req(#httpd{method=Method}=Req, _Db, _DDoc)
 
 handle_view_list_req(#httpd{method='POST',
         path_parts=[_, _, DesignName, _, ListName, ViewName]}=Req, Db, DDoc) ->
+    ok =  couch_util:validate_design_access(DDoc),
     chttpd:validate_ctype(Req, "application/json"),
     ReqBody = chttpd:body(Req),
     {Props2} = ?JSON_DECODE(ReqBody),
@@ -183,6 +192,7 @@ handle_view_list_req(#httpd{method='POST',
 
 handle_view_list_req(#httpd{method='POST',
         path_parts=[_, _, _, _, ListName, DesignName, ViewName]}=Req, Db, DDoc) ->
+    ok =  couch_util:validate_design_access(DDoc),
     chttpd:validate_ctype(Req, "application/json"),
     ReqBody = chttpd:body(Req),
     {Props2} = ?JSON_DECODE(ReqBody),
diff --git a/src/chttpd/src/chttpd_view.erl b/src/chttpd/src/chttpd_view.erl
index 4d1b80a..6ee21ba 100644
--- a/src/chttpd/src/chttpd_view.erl
+++ b/src/chttpd/src/chttpd_view.erl
@@ -16,12 +16,6 @@
 
 -export([handle_view_req/3, handle_temp_view_req/2]).
 
-validate_design_access(DDoc) ->
-    is_users_ddoc(DDoc).
-
-is_users_ddoc(#doc{access=[<<"_users">>]}) -> ok;
-is_users_ddoc(_) -> throw({forbidden, <<"per-user ddoc access">>}).
-
 multi_query_view(Req, Db, DDoc, ViewName, Queries) ->
     Args0 = couch_mrview_http:parse_params(Req, undefined),
     {ok, #mrst{views=Views}} = couch_mrview_util:ddoc_to_mrst(Db, DDoc),
@@ -57,7 +51,7 @@ design_doc_view(Req, Db, DDoc, ViewName, Keys) ->
 
 handle_view_req(#httpd{method='POST',
     path_parts=[_, _, _, _, ViewName, <<"queries">>]}=Req, Db, DDoc) ->
-    ok = validate_design_access(DDoc),
+    ok =  couch_util:validate_design_access(DDoc),
     chttpd:validate_ctype(Req, "application/json"),
     Props = couch_httpd:json_body_obj(Req),
     case couch_mrview_util:get_view_queries(Props) of
@@ -74,14 +68,14 @@ handle_view_req(#httpd{path_parts=[_, _, _, _, _, <<"queries">>]}=Req,
 
 handle_view_req(#httpd{method='GET',
         path_parts=[_, _, _, _, ViewName]}=Req, Db, DDoc) ->
-    ok = validate_design_access(DDoc),
+    ok =  couch_util:validate_design_access(DDoc),
     couch_stats:increment_counter([couchdb, httpd, view_reads]),
     Keys = chttpd:qs_json_value(Req, "keys", undefined),
     design_doc_view(Req, Db, DDoc, ViewName, Keys);
 
 handle_view_req(#httpd{method='POST',
         path_parts=[_, _, _, _, ViewName]}=Req, Db, DDoc) ->
-    ok = validate_design_access(DDoc),
+    ok =  couch_util:validate_design_access(DDoc),
     chttpd:validate_ctype(Req, "application/json"),
     Props = couch_httpd:json_body_obj(Req),
     Keys = couch_mrview_util:get_view_keys(Props),
diff --git a/src/couch/src/couch_changes.erl b/src/couch/src/couch_changes.erl
index c5b5edf..da225bb 100644
--- a/src/couch/src/couch_changes.erl
+++ b/src/couch/src/couch_changes.erl
@@ -213,6 +213,7 @@ configure_filter("_view", Style, Req, Db) ->
     case [?l2b(couch_httpd:unquote(Part)) || Part <- ViewNameParts] of
         [DName, VName] ->
             {ok, DDoc} = open_ddoc(Db, <<"_design/", DName/binary>>),
+            ok =  couch_util:validate_design_access(DDoc),
             check_member_exists(DDoc, [<<"views">>, VName]),
             FilterType = try
                 true = couch_util:get_nested_json_value(
@@ -245,6 +246,7 @@ configure_filter(FilterName, Style, Req, Db) ->
     case [?l2b(couch_httpd:unquote(Part)) || Part <- FilterNameParts] of
         [DName, FName] ->
             {ok, DDoc} = open_ddoc(Db, <<"_design/", DName/binary>>),
+            ok =  couch_util:validate_design_access(DDoc),
             check_member_exists(DDoc, [<<"filters">>, FName]),
             case couch_db:is_clustered(Db) of
                 true ->
diff --git a/src/couch/src/couch_db.erl b/src/couch/src/couch_db.erl
index a4d0e67..47a35f1 100644
--- a/src/couch/src/couch_db.erl
+++ b/src/couch/src/couch_db.erl
@@ -648,7 +648,10 @@ get_design_docs(#db{name = <<"shards/", _/binary>> = ShardDbName}) ->
         Response
     end;
 get_design_docs(#db{} = Db) ->
-    FoldFun = fun(FDI, Acc) -> {ok, [FDI | Acc]} end,
+    FoldFun = fun(FDI, Acc) ->
+        couch_log:info("~n===========~nget_design_docs() FDI: ~p~n--------------~n", [FDI]),
+        {ok, [FDI | Acc]}
+    end,
     {ok, Docs} = fold_design_docs(Db, FoldFun, [], []),
     {ok, lists:reverse(Docs)}.
 
@@ -902,12 +905,18 @@ group_alike_docs([Doc|Rest], [Bucket|RestBuckets]) ->
 
 
 validate_doc_update(#db{}=Db, #doc{id= <<"_design/",_/binary>>}=Doc, _GetDiskDocFun) ->
-    case catch check_is_admin(Db) of
-        ok -> validate_ddoc(Db#db.name, Doc);
-        Error -> Error
-    end;
-validate_doc_update(#db{validate_doc_funs = undefined} = Db, Doc, Fun) ->
+    case couch_doc:has_access(Doc) of
+        true ->
+            validate_ddoc(Db#db.name, Doc);
+        _Else ->
+            case catch check_is_admin(Db) of
+                ok -> validate_ddoc(Db#db.name, Doc);
+                Error -> Error
+            end
+        end;
+validate_doc_update(#db{validate_doc_funs = undefined, name = Name} = Db, Doc, Fun) ->
     ValidationFuns = load_validation_funs(Db),
+    couch_log:info("~n====== attemptint to write Doc: ~p, VDUs: ~p, for Db: ~p~n===========~n", [Doc, ValidationFuns, Name]),
     validate_doc_update(Db#db{validate_doc_funs=ValidationFuns}, Doc, Fun);
 validate_doc_update(#db{validate_doc_funs=[]}=Db, _Doc, _GetDiskDocFun) ->
 	ok;
@@ -955,11 +964,13 @@ validate_doc_update_int(Db, Doc, GetDiskDocFun) ->
 
 % to be safe, spawn a middleman here
 load_validation_funs(#db{main_pid=Pid, name = <<"shards/", _/binary>>}=Db) ->
+    couch_log:info("~n~nload_validation_funs_shards~n~n", []),
     {_, Ref} = spawn_monitor(fun() ->
         exit(ddoc_cache:open(mem3:dbname(Db#db.name), validation_funs))
     end),
     receive
         {'DOWN', Ref, _, _, {ok, Funs}} ->
+            couch_log:info("~n~nloaded_validation_funs_shards: ~p~n~n", [Funs]),
             gen_server:cast(Pid, {load_validation_funs, Funs}),
             Funs;
         {'DOWN', Ref, _, _, {database_does_not_exist, _StackTrace}} ->
@@ -971,6 +982,7 @@ load_validation_funs(#db{main_pid=Pid, name = <<"shards/", _/binary>>}=Db) ->
     end;
 load_validation_funs(#db{main_pid=Pid}=Db) ->
     {ok, DDocInfos} = get_design_docs(Db),
+    couch_log:info("~n~nload_validation_funs() -> DDocInfos: ~p~n~n", [DDocInfos]),
     OpenDocs = fun
         (#full_doc_info{}=D) ->
             {ok, Doc} = open_doc_int(Db, D, [ejson_body]),
diff --git a/src/couch/src/couch_db_updater.erl b/src/couch/src/couch_db_updater.erl
index 245554b..75e374f 100644
--- a/src/couch/src/couch_db_updater.erl
+++ b/src/couch/src/couch_db_updater.erl
@@ -198,9 +198,12 @@ handle_info({update_docs, Client, GroupedDocs, NonRepDocs, MergeConflicts,
                     couch_event:notify(Db2#db.name, {ddoc_updated, DDocId})
                 end, UpdatedDDocIds),
                 couch_event:notify(Db2#db.name, ddoc_updated),
+                couch_log:info("~n--------~nupdating ddoc cache with ids: ~p", [UpdatedDDocIds]),
                 ddoc_cache:refresh(Db2#db.name, UpdatedDDocIds),
                 refresh_validate_doc_funs(Db2);
             false ->
+                couch_log:info("~n--------~nupdating ddoc cache NOPE", []),
+                
                 Db2
         end,
         {noreply, Db3, hibernate_if_no_idle_limit()}
@@ -339,6 +342,7 @@ 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
+    couch_log:info("~n~nrefresh_validate_doc_funs() -> DesignDocs: ~p~n~n", [DesignDocs]),
     ProcessDocFuns = lists:flatmap(
         fun(DesignDocInfo) ->
             {ok, DesignDoc} = couch_db:open_doc_int(
@@ -622,12 +626,13 @@ update_docs_int(Db, DocsList, LocalDocs, MergeConflicts, FullCommit) ->
         length(LocalDocs2)
     ),
 
-    % Check if we just updated any design documents, and update the validation
-    % funs if we did.
+    % Check if we just updated any non-access design documents,
+    % and update the validation funs if we did.
+    NonAccessIds = [Id || [{_Client, #doc{id=Id,access=[]}}|_] <- DocsList],
     UpdatedDDocIds = lists:flatmap(fun
         (<<"_design/", _/binary>> = Id) -> [Id];
         (_) -> []
-    end, Ids),
+    end, NonAccessIds),
 
     {ok, commit_data(Db1, not FullCommit), UpdatedDDocIds}.
 
diff --git a/src/couch/src/couch_doc.erl b/src/couch/src/couch_doc.erl
index 1ec31e2..6e4b38c 100644
--- a/src/couch/src/couch_doc.erl
+++ b/src/couch/src/couch_doc.erl
@@ -25,7 +25,7 @@
 
 -export([with_ejson_body/1]).
 -export([is_deleted/1]).
-
+-export([has_access/1, has_no_access/1]).
 
 -include_lib("couch/include/couch_db.hrl").
 
@@ -404,6 +404,15 @@ get_access({Props}) ->
 get_access(#doc{access=Access}) ->
     Access.
 
+has_access(Doc) ->
+    has_access1(get_access(Doc)).
+    
+has_no_access(Doc) ->
+    not has_access1(get_access(Doc)).
+
+has_access1([]) -> false;
+has_access1(_) -> true.
+
 get_validate_doc_fun({Props}) ->
     get_validate_doc_fun(couch_doc:from_json_obj({Props}));
 get_validate_doc_fun(#doc{body={Props}}=DDoc) ->
diff --git a/src/couch/src/couch_util.erl b/src/couch/src/couch_util.erl
index 62e17ce..4fa2fde 100644
--- a/src/couch/src/couch_util.erl
+++ b/src/couch/src/couch_util.erl
@@ -39,6 +39,7 @@
 -export([check_config_blacklist/1]).
 -export([check_md5/2]).
 -export([set_mqd_off_heap/1]).
+-export([validate_design_access/1]).
 
 -include_lib("couch/include/couch_db.hrl").
 
@@ -749,3 +750,9 @@ check_config_blacklist(Section) ->
     _ ->
         ok
     end.
+
+validate_design_access(DDoc) ->
+    is_users_ddoc(DDoc).
+
+is_users_ddoc(#doc{access=[<<"_users">>]}) -> ok;
+is_users_ddoc(_) -> throw({forbidden, <<"per-user ddoc access">>}).
diff --git a/src/couch/test/couchdb_access_tests.erl b/src/couch/test/couchdb_access_tests.erl
index a3c692b..41b300c 100644
--- a/src/couch/test/couchdb_access_tests.erl
+++ b/src/couch/test/couchdb_access_tests.erl
@@ -40,6 +40,7 @@ before_all() ->
     Couch = test_util:start_couch([chttpd]),
     Hashed = couch_passwords:hash_admin_password("a"),
     ok = config:set("admins", "a", binary_to_list(Hashed), _Persist=false),
+    % ok = config:set("log", "level", "debug", _Persist=false),
 
     % cleanup and setup
     {ok, _, _, _} = test_request:delete(url() ++ "/db", ?ADMIN_REQ_HEADERS),
@@ -70,6 +71,8 @@ access_test_() ->
         fun should_let_admin_create_doc_without_access/2,
         fun should_let_user_create_doc_for_themselves/2,
         fun should_not_let_user_create_doc_for_someone_else/2,
+        fun should_let_user_create_access_ddoc/2,
+        fun access_ddoc_should_have_no_effects/2,
 
         % Doc updates
         fun users_with_access_can_update_doc/2,
@@ -93,7 +96,7 @@ access_test_() ->
         fun should_let_admin_fetch_all_docs/2,
         fun should_let_user_fetch_their_own_all_docs/2,
         % potential future feature
-        %fun should_let_user_fetch_their_own_all_docs_plus_users_ddocs/2%,
+        % % % fun should_let_user_fetch_their_own_all_docs_plus_users_ddocs/2%,
 
         % _changes
         fun should_let_admin_fetch_changes/2,
@@ -104,7 +107,7 @@ access_test_() ->
         fun should_not_allow_user_access_ddoc_view_request/2,
         fun should_allow_admin_users_access_ddoc_view_request/2,
         fun should_allow_user_users_access_ddoc_view_request/2
-                
+
         % TODO: create test db with role and not _users in _security.members
         % and make sure a user in that group can access while a user not
         % in that group cant
@@ -152,6 +155,38 @@ should_not_let_user_create_doc_for_someone_else(_PortType, Url) ->
         ?USERY_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
     ?_assertEqual(403, Code).
 
+should_let_user_create_access_ddoc(_PortType, Url) ->
+    {ok, Code, _, _} = test_request:put(Url ++ "/db/_design/dx",
+        ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
+    ?_assertEqual(201, Code).
+
+access_ddoc_should_have_no_effects(_PortType, Url) ->
+    Ddoc = "{ \"_access\":[\"x\"], \"validate_doc_update\": \"function(newDoc, oldDoc, userCtx) { throw({unauthorized: 'throw error'})}\",   \"views\": {     \"foo\": {       \"map\": \"function(doc) { emit(doc._id) }\"     }   },   \"shows\": {     \"boo\": \"function() {}\"   },   \"lists\": {     \"hoo\": \"function() {}\"   },   \"update\": {     \"goo\": \"function() {}\"   },   \"filters\": {     \"loo\": \"function() {}\"   }   }",
+    {ok, Code, _, _} = test_request:put(Url ++ "/db/_design/dx",
+        ?USERX_REQ_HEADERS, Ddoc),
+    ?_assertEqual(201, Code),
+    {ok, Code1, _, _} = test_request:put(Url ++ "/db/b",
+        ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
+    ?_assertEqual(401, Code1),
+    {ok, Code2, _, _} = test_request:get(Url ++ "/db/_design/dx/_view/foo",
+        ?USERX_REQ_HEADERS),
+    ?_assertEqual(403, Code2),
+    {ok, Code3, _, _} = test_request:get(Url ++ "/db/_design/dx/_show/boo/b",
+        ?USERX_REQ_HEADERS),
+    ?_assertEqual(403, Code3),
+    {ok, Code4, _, _} = test_request:get(Url ++ "/db/_design/dx/_list/hoo/foo",
+        ?USERX_REQ_HEADERS),
+    ?_assertEqual(403, Code4),
+    {ok, Code5, _, _} = test_request:post(Url ++ "/db/_design/dx/_update/goo",
+        ?USERX_REQ_HEADERS, ""),
+    ?_assertEqual(403, Code5),
+    {ok, Code6, _, _} = test_request:get(Url ++ "/db/_changes?filter=dx/loo",
+        ?USERX_REQ_HEADERS),
+    ?_assertEqual(403, Code6),
+    {ok, Code7, _, _} = test_request:get(Url ++ "/db/_changes?filter=_view&view=dx/foo",
+        ?USERX_REQ_HEADERS),
+    ?_assertEqual(403, Code7).
+
 % Doc updates
 
 users_with_access_can_update_doc(_PortType, Url) ->
diff --git a/src/ddoc_cache/src/ddoc_cache_entry_validation_funs.erl b/src/ddoc_cache/src/ddoc_cache_entry_validation_funs.erl
index 2182dea..c037a30 100644
--- a/src/ddoc_cache/src/ddoc_cache_entry_validation_funs.erl
+++ b/src/ddoc_cache/src/ddoc_cache_entry_validation_funs.erl
@@ -30,7 +30,9 @@ ddocid(_) ->
 
 
 recover(DbName) ->
-    {ok, DDocs} = fabric:design_docs(mem3:dbname(DbName)),
+    {ok, DDocs0} = fabric:design_docs(mem3:dbname(DbName)),
+    DDocs = lists:filter(fun couch_doc:has_no_access/1, DDocs0),
+    couch_log:info("~n~n~n~nrecover validation funs: ~p From DDocs0: ~p~n~n~n~n", [DDocs, DDocs0]),
     Funs = lists:flatmap(fun(DDoc) ->
         case couch_doc:get_validate_doc_fun(DDoc) of
             nil -> [];


[couchdb] 02/04: chore: abort attempt at auto-ddoc injection

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jan pushed a commit to branch access
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit a0936ce69e1e591153fe44ade4de508e9f2a7da1
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sun Oct 20 09:36:17 2019 +0200

    chore: abort attempt at auto-ddoc injection
---
 src/couch/src/couch_access_native_proc.erl |  4 +--
 src/couch/src/couch_iter.erl               | 27 ----------------
 src/couch/test/couchdb_access_tests.erl    | 50 ++++++++++++++++--------------
 src/couch_mrview/src/couch_mrview.erl      | 46 ++++-----------------------
 4 files changed, 34 insertions(+), 93 deletions(-)

diff --git a/src/couch/src/couch_access_native_proc.erl b/src/couch/src/couch_access_native_proc.erl
index 4f62a10..fb94150 100644
--- a/src/couch/src/couch_access_native_proc.erl
+++ b/src/couch/src/couch_access_native_proc.erl
@@ -32,7 +32,7 @@
 
 -record(st, {
     indexes = [],
-    timeout = 5000
+    timeout = 5000 % TODO: make configurable
 }).
 
 start_link() ->
@@ -110,7 +110,7 @@ code_change(_OldVsn, St, _Extra) ->
 % -seq, since that one we will always need, and by-access-id can be opt-in.
 % the second dimension is the number of emit kv pairs:
 % [ // the return value
-%   [ // the first views
+%   [ // the first view
 %     ['k1', 'v1'], // the first k/v pair for the first view
 %     ['k2', 'v2']  // second, etc.
 %   ],
diff --git a/src/couch/src/couch_iter.erl b/src/couch/src/couch_iter.erl
deleted file mode 100644
index a755d8d..0000000
--- a/src/couch/src/couch_iter.erl
+++ /dev/null
@@ -1,27 +0,0 @@
--module(couch_iter).
--export([start/1, next/1, counter/2]).
-
-start(Max) ->
-    couch_log:info("~nstarting iter with Max: ~p~n", [Max]),
-    P = spawn_link(couch_iter, counter, [0, Max]),
-    couch_log:info("~ndd_iter_start: ~p~n", [P]),
-    P.
-
-counter(N, N) ->
-    couch_log:info("~ndd_counterNN: ~p~n", [N]),
-    receive {From, next} ->
-        From ! {self(), done}
-    end;
-counter(N, Max) ->
-    couch_log:info("~ndd_counterN: ~p Max: ~p ~n", [N, Max]),
-    receive {From, next} ->
-        From ! {self(), N + 1},
-        counter(N + 1, Max)
-    end.
-
-next(Iter) ->
-    couch_log:info("~nnext~n", []),
-    Iter ! {self(), next},
-    receive
-        {Iter, Answer} -> Answer
-    end.
diff --git a/src/couch/test/couchdb_access_tests.erl b/src/couch/test/couchdb_access_tests.erl
index fca6bb0..a3c692b 100644
--- a/src/couch/test/couchdb_access_tests.erl
+++ b/src/couch/test/couchdb_access_tests.erl
@@ -92,7 +92,8 @@ 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,
+        % potential future feature
+        %fun should_let_user_fetch_their_own_all_docs_plus_users_ddocs/2%,
 
         % _changes
         fun should_let_admin_fetch_changes/2,
@@ -284,29 +285,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))).
+% Potential future feature:%
+% 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) ->
diff --git a/src/couch_mrview/src/couch_mrview.erl b/src/couch_mrview/src/couch_mrview.erl
index 645041b..0f4665b 100644
--- a/src/couch_mrview/src/couch_mrview.erl
+++ b/src/couch_mrview/src/couch_mrview.erl
@@ -228,6 +228,9 @@ access_ddoc() ->
         id = <<"_design/_access">>,
         body = {[
             {<<"language">>,<<"_access">>},
+            {<<"options">>, {[
+                {<<"include_design">>, true}
+            ]}},
             {<<"views">>, {[
                 {<<"_access_by_id">>, {[
                     {<<"map">>, <<"_access/by-id-map">>},
@@ -280,12 +283,6 @@ 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.
@@ -295,47 +292,16 @@ query_all_docs_access(Db, Args0, Callback0, Acc) ->
     Args1 = prefix_startkey_endkey(UserName, Args0, Args0#mrargs.direction),
     Args = Args1#mrargs{reduce=false},
 
-    % 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)
-
+            % 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)


[couchdb] 04/04: fix: offset and test execution

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jan pushed a commit to branch access
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit bd64cdfdbcca98edf52194e5e678a7735ccb956f
Author: Jan Lehnardt <ja...@apache.org>
AuthorDate: Sun Oct 20 13:40:55 2019 +0200

    fix: offset and test execution
---
 src/couch/test/couchdb_access_tests.erl | 220 +++++++++++++++++++++-----------
 src/couch_mrview/src/couch_mrview.erl   |   8 +-
 2 files changed, 144 insertions(+), 84 deletions(-)

diff --git a/src/couch/test/couchdb_access_tests.erl b/src/couch/test/couchdb_access_tests.erl
index 41b300c..cd62fbe 100644
--- a/src/couch/test/couchdb_access_tests.erl
+++ b/src/couch/test/couchdb_access_tests.erl
@@ -95,8 +95,8 @@ 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,
-        % potential future feature
-        % % % fun should_let_user_fetch_their_own_all_docs_plus_users_ddocs/2%,
+        % % potential future feature
+        % % fun should_let_user_fetch_their_own_all_docs_plus_users_ddocs/2%,
 
         % _changes
         fun should_let_admin_fetch_changes/2,
@@ -161,31 +161,33 @@ should_let_user_create_access_ddoc(_PortType, Url) ->
     ?_assertEqual(201, Code).
 
 access_ddoc_should_have_no_effects(_PortType, Url) ->
-    Ddoc = "{ \"_access\":[\"x\"], \"validate_doc_update\": \"function(newDoc, oldDoc, userCtx) { throw({unauthorized: 'throw error'})}\",   \"views\": {     \"foo\": {       \"map\": \"function(doc) { emit(doc._id) }\"     }   },   \"shows\": {     \"boo\": \"function() {}\"   },   \"lists\": {     \"hoo\": \"function() {}\"   },   \"update\": {     \"goo\": \"function() {}\"   },   \"filters\": {     \"loo\": \"function() {}\"   }   }",
-    {ok, Code, _, _} = test_request:put(Url ++ "/db/_design/dx",
-        ?USERX_REQ_HEADERS, Ddoc),
-    ?_assertEqual(201, Code),
-    {ok, Code1, _, _} = test_request:put(Url ++ "/db/b",
-        ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
-    ?_assertEqual(401, Code1),
-    {ok, Code2, _, _} = test_request:get(Url ++ "/db/_design/dx/_view/foo",
-        ?USERX_REQ_HEADERS),
-    ?_assertEqual(403, Code2),
-    {ok, Code3, _, _} = test_request:get(Url ++ "/db/_design/dx/_show/boo/b",
-        ?USERX_REQ_HEADERS),
-    ?_assertEqual(403, Code3),
-    {ok, Code4, _, _} = test_request:get(Url ++ "/db/_design/dx/_list/hoo/foo",
-        ?USERX_REQ_HEADERS),
-    ?_assertEqual(403, Code4),
-    {ok, Code5, _, _} = test_request:post(Url ++ "/db/_design/dx/_update/goo",
-        ?USERX_REQ_HEADERS, ""),
-    ?_assertEqual(403, Code5),
-    {ok, Code6, _, _} = test_request:get(Url ++ "/db/_changes?filter=dx/loo",
-        ?USERX_REQ_HEADERS),
-    ?_assertEqual(403, Code6),
-    {ok, Code7, _, _} = test_request:get(Url ++ "/db/_changes?filter=_view&view=dx/foo",
-        ?USERX_REQ_HEADERS),
-    ?_assertEqual(403, Code7).
+    ?_test(begin
+        Ddoc = "{ \"_access\":[\"x\"], \"validate_doc_update\": \"function(newDoc, oldDoc, userCtx) { throw({unauthorized: 'throw error'})}\",   \"views\": {     \"foo\": {       \"map\": \"function(doc) { emit(doc._id) }\"     }   },   \"shows\": {     \"boo\": \"function() {}\"   },   \"lists\": {     \"hoo\": \"function() {}\"   },   \"update\": {     \"goo\": \"function() {}\"   },   \"filters\": {     \"loo\": \"function() {}\"   }   }",
+        {ok, Code, _, _} = test_request:put(Url ++ "/db/_design/dx",
+            ?USERX_REQ_HEADERS, Ddoc),
+        ?assertEqual(201, Code),
+        {ok, Code1, _, _} = test_request:put(Url ++ "/db/b",
+            ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
+        ?assertEqual(201, Code1),
+        {ok, Code2, _, _} = test_request:get(Url ++ "/db/_design/dx/_view/foo",
+            ?USERX_REQ_HEADERS),
+        ?assertEqual(403, Code2),
+        {ok, Code3, _, _} = test_request:get(Url ++ "/db/_design/dx/_show/boo/b",
+            ?USERX_REQ_HEADERS),
+        ?assertEqual(403, Code3),
+        {ok, Code4, _, _} = test_request:get(Url ++ "/db/_design/dx/_list/hoo/foo",
+            ?USERX_REQ_HEADERS),
+        ?assertEqual(403, Code4),
+        {ok, Code5, _, _} = test_request:post(Url ++ "/db/_design/dx/_update/goo",
+            ?USERX_REQ_HEADERS, ""),
+        ?assertEqual(403, Code5),
+        {ok, Code6, _, _} = test_request:get(Url ++ "/db/_changes?filter=dx/loo",
+            ?USERX_REQ_HEADERS),
+        ?assertEqual(403, Code6),
+        {ok, Code7, _, _} = test_request:get(Url ++ "/db/_changes?filter=_view&view=dx/foo",
+            ?USERX_REQ_HEADERS),
+        ?assertEqual(403, Code7)
+    end).
 
 % Doc updates
 
@@ -220,6 +222,7 @@ users_with_access_can_not_remove_access(_PortType, Url) ->
     ?_assertEqual(403, Code).
 
 % Doc reads
+
 should_let_admin_read_doc_with_access(_PortType, Url) ->
     {ok, 201, _, _} = test_request:put(Url ++ "/db/a",
         ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
@@ -267,6 +270,7 @@ user_can_not_read_doc_without_access(_PortType, Url) ->
     ?_assertEqual(403, Code).
 
 % Doc deletes
+
 should_let_admin_delete_doc_with_access(_PortType, Url) ->
     {ok, 201, _, _} = test_request:put(Url ++ "/db/a",
         ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
@@ -291,6 +295,7 @@ should_not_let_user_delete_doc_for_someone_else(_PortType, Url) ->
     ?_assertEqual(403, Code).
 
 % _all_docs with include_docs
+
 should_let_admin_fetch_all_docs(_PortType, Url) ->
     {ok, 201, _, _} = test_request:put(Url ++ "/db/a",
         ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
@@ -306,46 +311,65 @@ should_let_admin_fetch_all_docs(_PortType, Url) ->
     ?_assertEqual(4, proplists:get_value(<<"total_rows">>, Json)).
 
 should_let_user_fetch_their_own_all_docs(_PortType, Url) ->
-    {ok, 201, _, _} = test_request:put(Url ++ "/db/a",
-        ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
-    {ok, 201, _, _} = test_request:put(Url ++ "/db/b",
-        ?USERX_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),
-    ?_assertEqual(2, length(proplists:get_value(<<"rows">>, Json))).
-    % TODO    ?_assertEqual(2, proplists:get_value(<<"total_rows">>, Json)).
+    ?_test(begin
+        {ok, 201, _, _} = test_request:put(Url ++ "/db/a",
+            ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
+        {ok, 201, _, _} = test_request:put(Url ++ "/db/b",
+            ?USERX_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),
+        Rows = proplists:get_value(<<"rows">>, Json),
+        ?assertEqual([{[{<<"id">>,<<"a">>},
+               {<<"key">>,<<"a">>},
+               {<<"value">>,<<"1-23202479633c2b380f79507a776743d5">>},
+               {<<"doc">>,
+                {[{<<"_id">>,<<"a">>},
+                  {<<"_rev">>,<<"1-23202479633c2b380f79507a776743d5">>},
+                  {<<"a">>,1},
+                  {<<"_access">>,[<<"x">>]}]}}]},
+             {[{<<"id">>,<<"b">>},
+               {<<"key">>,<<"b">>},
+               {<<"value">>,<<"1-d33fb05384fa65a8081da2046595de0f">>},
+               {<<"doc">>,
+                {[{<<"_id">>,<<"b">>},
+                  {<<"_rev">>,<<"1-d33fb05384fa65a8081da2046595de0f">>},
+                  {<<"b">>,2},
+                  {<<"_access">>,[<<"x">>]}]}}]}], Rows),
+        ?assertEqual(2, length(Rows)),
+        ?assertEqual(4, proplists:get_value(<<"total_rows">>, Json)),
+
+        {ok, 200, _, Body1} = test_request:get(Url ++ "/db/_all_docs?include_docs=true",
+            ?USERY_REQ_HEADERS),
+        {Json1} = jiffy:decode(Body1),
+        ?assertEqual( [{<<"total_rows">>,4},
+            {<<"offset">>,2},
+            {<<"rows">>,
+                [{[{<<"id">>,<<"c">>},
+                 {<<"key">>,<<"c">>},
+                 {<<"value">>,<<"1-92aef5b0e4a3f4db0aba1320869bc95d">>},
+                 {<<"doc">>,
+                  {[{<<"_id">>,<<"c">>},
+                    {<<"_rev">>,<<"1-92aef5b0e4a3f4db0aba1320869bc95d">>},
+                    {<<"c">>,3},
+                    {<<"_access">>,[<<"y">>]}]}}]},
+                {[{<<"id">>,<<"d">>},
+                 {<<"key">>,<<"d">>},
+                 {<<"value">>,<<"1-ae984f6550038b1ed1565ac4b6cd8c5d">>},
+                 {<<"doc">>,
+                  {[{<<"_id">>,<<"d">>},
+                    {<<"_rev">>,<<"1-ae984f6550038b1ed1565ac4b6cd8c5d">>},
+                    {<<"d">>,4},
+                    {<<"_access">>,[<<"y">>]}]}}]}]}], Json1)
+    end).
 
-% Potential future feature:%
-% 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",
         ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
@@ -362,21 +386,38 @@ should_let_admin_fetch_changes(_PortType, Url) ->
     ?_assertEqual(4, AmountOfDocs).
 
 should_let_user_fetch_their_own_changes(_PortType, Url) ->
-    {ok, 201, _, _} = test_request:put(Url ++ "/db/a",
-        ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
-    {ok, 201, _, _} = test_request:put(Url ++ "/db/b",
-        ?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",
-        ?ADMIN_REQ_HEADERS, "{\"d\":4,\"_access\":[\"y\"]}"),
-    {ok, 200, _, Body} = test_request:get(Url ++ "/db/_changes",
-        ?USERX_REQ_HEADERS),
-    {Json} = jiffy:decode(Body),
-    AmountOfDocs = length(proplists:get_value(<<"results">>, Json)),
-    ?_assertEqual(2, AmountOfDocs).
+    ?_test(begin
+        {ok, 201, _, _} = test_request:put(Url ++ "/db/a",
+            ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"),
+        {ok, 201, _, _} = test_request:put(Url ++ "/db/b",
+            ?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",
+            ?ADMIN_REQ_HEADERS, "{\"d\":4,\"_access\":[\"y\"]}"),
+        {ok, 200, _, Body} = test_request:get(Url ++ "/db/_changes",
+            ?USERX_REQ_HEADERS),
+        {Json} = jiffy:decode(Body),
+        ?assertMatch([{<<"results">>,
+               [{[{<<"seq">>,
+                   <<"2-", _/binary>>},
+                  {<<"id">>,<<"a">>},
+                  {<<"changes">>,
+                   [{[{<<"rev">>,<<"1-23202479633c2b380f79507a776743d5">>}]}]}]},
+                {[{<<"seq">>,
+                   <<"3-", _/binary>>},
+                  {<<"id">>,<<"b">>},
+                  {<<"changes">>,
+                   [{[{<<"rev">>,<<"1-d33fb05384fa65a8081da2046595de0f">>}]}]}]}]},
+              {<<"last_seq">>,
+               <<"3-", _/binary>>},
+              {<<"pending">>,2}], Json),
+        AmountOfDocs = length(proplists:get_value(<<"results">>, Json)),
+        ?assertEqual(2, AmountOfDocs)
+    end).
 
 % views
+
 should_not_allow_admin_access_ddoc_view_request(_PortType, Url) ->
     DDoc = "{\"a\":1,\"_access\":[\"x\"],\"views\":{\"foo\":{\"map\":\"function() {}\"}}}",
     {ok, Code, _, _} = test_request:put(Url ++ "/db/_design/a",
@@ -419,3 +460,28 @@ should_allow_user_users_access_ddoc_view_request(_PortType, Url) ->
 
 port() ->
     integer_to_list(mochiweb_socket_server:get(chttpd, port)).
+
+% Potential future feature:%
+% 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))).
diff --git a/src/couch_mrview/src/couch_mrview.erl b/src/couch_mrview/src/couch_mrview.erl
index 0f4665b..0664c01 100644
--- a/src/couch_mrview/src/couch_mrview.erl
+++ b/src/couch_mrview/src/couch_mrview.erl
@@ -597,13 +597,7 @@ all_docs_fold(Db, #mrargs{direction=Dir, keys=Keys0}=Args, Callback, UAcc) ->
 
 
 map_fold(Db, View, Args, Callback, UAcc) ->
-    {ok, Total} = case View#mrview.def of
-        <<"_access/by-id-map">> ->
-            % TODO: couch_mrview_util:get_access_row_count(View, [Args#mrargs.start_key]);
-            {ok, 0};
-        _Else ->
-            couch_mrview_util:get_row_count(View)
-    end,
+    {ok, Total} = couch_mrview_util:get_row_count(View),
     Acc = #mracc{
         db=Db,
         total_rows=Total,