From 6c256d5cb6289fd68362cb4eaad45bbb4ed1adf0 Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Sat, 6 Aug 2022 17:53:28 +0200 Subject: [PATCH] chore(access): erlfmt --- src/chttpd/src/chttpd_db.erl | 11 +- src/chttpd/src/chttpd_view.erl | 28 +- src/couch/src/couch_access_native_proc.erl | 64 +- src/couch/src/couch_bt_engine.erl | 1 - src/couch/src/couch_btree.erl | 8 +- src/couch/src/couch_db.erl | 199 +-- src/couch/src/couch_db_updater.erl | 116 +- src/couch/src/couch_doc.erl | 7 +- src/couch/src/couch_util.erl | 5 +- src/couch/test/eunit/couchdb_access_tests.erl | 1293 +++++++++++------ .../eunit/couchdb_update_conflicts_tests.erl | 3 +- src/couch_index/src/couch_index_updater.erl | 13 +- src/couch_index/src/couch_index_util.erl | 2 +- src/couch_mrview/src/couch_mrview.erl | 113 +- src/couch_mrview/src/couch_mrview_updater.erl | 56 +- src/couch_mrview/src/couch_mrview_util.erl | 2 +- src/couch_replicator/src/couch_replicator.erl | 17 +- .../src/couch_replicator_scheduler_job.erl | 15 +- src/fabric/src/fabric_doc_update.erl | 6 +- 19 files changed, 1224 insertions(+), 735 deletions(-) diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl index b199118a605..3b2ce761b55 100644 --- a/src/chttpd/src/chttpd_db.erl +++ b/src/chttpd/src/chttpd_db.erl @@ -1047,7 +1047,7 @@ db_doc_req(#httpd{method = 'DELETE'} = Req, Db, DocId) -> Rev -> Body = {[{<<"_rev">>, ?l2b(Rev)}, {<<"_deleted">>, true}]} end, - Doc = #doc{revs=Revs,body=Body,deleted=true,access=Doc0#doc.access}, + Doc = #doc{revs = Revs, body = Body, deleted = true, access = Doc0#doc.access}, send_updated_doc(Req, Db, DocId, couch_doc_from_req(Req, Db, DocId, Doc)); db_doc_req(#httpd{method = 'GET', mochi_req = MochiReq} = Req, Db, DocId) -> #doc_query_args{ @@ -1498,7 +1498,7 @@ receive_request_data(Req, LenLeft) when LenLeft > 0 -> receive_request_data(_Req, _) -> throw(<<"expected more data">>). -update_doc_result_to_json({#doc{id=Id,revs=Rev}, access}) -> +update_doc_result_to_json({#doc{id = Id, revs = Rev}, access}) -> update_doc_result_to_json({{Id, Rev}, access}); update_doc_result_to_json({error, _} = Error) -> {_Code, Err, Msg} = chttpd:error_info(Error), @@ -2083,13 +2083,12 @@ parse_shards_opt("placement", Req, Default) -> throw({bad_request, Err}) end end; - - parse_shards_opt("access", Req, Value) when is_list(Value) -> parse_shards_opt("access", Req, list_to_existing_atom(Value)); parse_shards_opt("access", _Req, Value) when Value =:= true -> case config:get_boolean("per_doc_access", "enabled", false) of - true -> true; + true -> + true; false -> Err = ?l2b(["The `access` option is not available on this CouchDB installation."]), throw({bad_request, Err}) @@ -2099,7 +2098,6 @@ parse_shards_opt("access", _Req, Value) when Value =:= false -> parse_shards_opt("access", _Req, _Value) -> Err = ?l2b(["The `access` value should be a boolean."]), throw({bad_request, Err}); - parse_shards_opt(Param, Req, Default) -> couch_log:error("~n parse_shards_opt Param: ~p, Default: ~p~n", [Param, Default]), Val = chttpd:qs_value(Req, Param, Default), @@ -2109,7 +2107,6 @@ parse_shards_opt(Param, Req, Default) -> false -> throw({bad_request, Err}) end. - parse_engine_opt(Req) -> case chttpd:qs_value(Req, "engine") of undefined -> diff --git a/src/chttpd/src/chttpd_view.erl b/src/chttpd/src/chttpd_view.erl index f74088dbcc6..44459b3cfc9 100644 --- a/src/chttpd/src/chttpd_view.erl +++ b/src/chttpd/src/chttpd_view.erl @@ -69,20 +69,20 @@ fabric_query_view(Db, Req, DDoc, ViewName, Args) -> Max = chttpd:chunked_response_buffer_size(), VAcc = #vacc{db = Db, req = Req, threshold = Max}, Options = [{user_ctx, Req#httpd.user_ctx}], -% {ok, Resp} = fabric:query_view(Db, Options, DDoc, ViewName, -% fun view_cb/2, VAcc, Args), -% {ok, Resp#vacc.resp}. -% % TODO: This might just be a debugging leftover, we might be able -% % to undo this by just returning {ok, Resp#vacc.resp} -% % However, this *might* be here because we need to handle -% % errors here now, because access might tell us to. -% case fabric:query_view(Db, Options, DDoc, ViewName, -% fun view_cb/2, VAcc, Args) of -% {ok, Resp} -> -% {ok, Resp#vacc.resp}; -% {error, Error} -> -% throw(Error) -% end. + % {ok, Resp} = fabric:query_view(Db, Options, DDoc, ViewName, + % fun view_cb/2, VAcc, Args), + % {ok, Resp#vacc.resp}. + % % TODO: This might just be a debugging leftover, we might be able + % % to undo this by just returning {ok, Resp#vacc.resp} + % % However, this *might* be here because we need to handle + % % errors here now, because access might tell us to. + % case fabric:query_view(Db, Options, DDoc, ViewName, + % fun view_cb/2, VAcc, Args) of + % {ok, Resp} -> + % {ok, Resp#vacc.resp}; + % {error, Error} -> + % throw(Error) + % end. {ok, Resp} = fabric:query_view( Db, diff --git a/src/couch/src/couch_access_native_proc.erl b/src/couch/src/couch_access_native_proc.erl index 965b124de4a..38c8e573814 100644 --- a/src/couch/src/couch_access_native_proc.erl +++ b/src/couch/src/couch_access_native_proc.erl @@ -13,7 +13,6 @@ -module(couch_access_native_proc). -behavior(gen_server). - -export([ start_link/0, set_timeout/2, @@ -29,71 +28,55 @@ code_change/3 ]). - -record(st, { indexes = [], - timeout = 5000 % TODO: make configurable + % TODO: make configurable + timeout = 5000 }). start_link() -> gen_server:start_link(?MODULE, [], []). - set_timeout(Pid, TimeOut) when is_integer(TimeOut), TimeOut > 0 -> gen_server:call(Pid, {set_timeout, TimeOut}). - prompt(Pid, Data) -> gen_server:call(Pid, {prompt, Data}). - init(_) -> {ok, #st{}}. - terminate(_Reason, _St) -> ok. - handle_call({set_timeout, TimeOut}, _From, St) -> - {reply, ok, St#st{timeout=TimeOut}}; - + {reply, ok, St#st{timeout = TimeOut}}; handle_call({prompt, [<<"reset">>]}, _From, St) -> - {reply, true, St#st{indexes=[]}}; - + {reply, true, St#st{indexes = []}}; handle_call({prompt, [<<"reset">>, _QueryConfig]}, _From, St) -> - {reply, true, St#st{indexes=[]}}; - + {reply, true, St#st{indexes = []}}; handle_call({prompt, [<<"add_fun">>, IndexInfo]}, _From, St) -> {reply, true, St}; - handle_call({prompt, [<<"map_doc">>, Doc]}, _From, St) -> {reply, map_doc(St, mango_json:to_binary(Doc)), St}; - handle_call({prompt, [<<"reduce">>, _, _]}, _From, St) -> {reply, null, St}; - handle_call({prompt, [<<"rereduce">>, _, _]}, _From, St) -> {reply, null, St}; - handle_call({prompt, [<<"index_doc">>, Doc]}, _From, St) -> {reply, [[]], St}; - handle_call(Msg, _From, St) -> {stop, {invalid_call, Msg}, {invalid_call, Msg}, St}. handle_cast(garbage_collect, St) -> erlang:garbage_collect(), {noreply, St}; - handle_cast(Msg, St) -> {stop, {invalid_cast, Msg}, St}. - handle_info(Msg, St) -> {stop, {invalid_info, Msg}, St}. - code_change(_OldVsn, St, _Extra) -> {ok, St}. @@ -115,7 +98,8 @@ code_change(_OldVsn, St, _Extra) -> map_doc(_St, {Doc}) -> case couch_util:get_value(<<"_access">>, Doc) of undefined -> - [[],[]]; % do not index this doc + % do not index this doc + [[], []]; Access when is_list(Access) -> Id = couch_util:get_value(<<"_id">>, Doc), Rev = couch_util:get_value(<<"_rev">>, Doc), @@ -123,21 +107,33 @@ map_doc(_St, {Doc}) -> Deleted = couch_util:get_value(<<"_deleted">>, Doc, false), BodySp = couch_util:get_value(<<"_body_sp">>, Doc), % by-access-id - ById = case Deleted of - false -> - lists:map(fun(UserOrRole) -> [ - [[UserOrRole, Id], Rev] - ] end, Access); - _True -> [[]] - end, + ById = + case Deleted of + false -> + lists:map( + fun(UserOrRole) -> + [ + [[UserOrRole, Id], Rev] + ] + end, + Access + ); + _True -> + [[]] + end, % by-access-seq - BySeq = lists:map(fun(UserOrRole) -> [ - [[UserOrRole, Seq], [{rev, Rev}, {deleted, Deleted}, {body_sp, BodySp}]] - ] end, Access), + BySeq = lists:map( + fun(UserOrRole) -> + [ + [[UserOrRole, Seq], [{rev, Rev}, {deleted, Deleted}, {body_sp, BodySp}]] + ] + end, + Access + ), ById ++ BySeq; Else -> % TODO: no comprende: should not be needed once we implement % _access field validation - [[],[]] + [[], []] end. diff --git a/src/couch/src/couch_bt_engine.erl b/src/couch/src/couch_bt_engine.erl index bd778f33b49..d653a8eea20 100644 --- a/src/couch/src/couch_bt_engine.erl +++ b/src/couch/src/couch_bt_engine.erl @@ -672,7 +672,6 @@ id_tree_split(#full_doc_info{} = Info) -> id_tree_join(Id, {HighSeq, Deleted, DiskTree}) -> % Handle old formats before data_size was added id_tree_join(Id, {HighSeq, Deleted, #size_info{}, DiskTree}); - id_tree_join(Id, {HighSeq, Deleted, Sizes, DiskTree}) -> id_tree_join(Id, {HighSeq, Deleted, Sizes, DiskTree, []}); id_tree_join(Id, {HighSeq, Deleted, Sizes, DiskTree, Access}) -> diff --git a/src/couch/src/couch_btree.erl b/src/couch/src/couch_btree.erl index d7ca7bab4bf..b908421f202 100644 --- a/src/couch/src/couch_btree.erl +++ b/src/couch/src/couch_btree.erl @@ -116,9 +116,11 @@ full_reduce_with_options(Bt, Options0) -> end, [UserName] = proplists:get_value(start_key, Options0, <<"">>), EndKey = {[UserName, {[]}]}, - Options = Options0 ++ [ - {end_key, EndKey} - ], + Options = + Options0 ++ + [ + {end_key, EndKey} + ], fold_reduce(Bt, CountFun, 0, Options). size(#btree{root = nil}) -> diff --git a/src/couch/src/couch_db.erl b/src/couch/src/couch_db.erl index 2b775693771..fdfae0f3e12 100644 --- a/src/couch/src/couch_db.erl +++ b/src/couch/src/couch_db.erl @@ -139,7 +139,8 @@ ]). -include_lib("couch/include/couch_db.hrl"). --include_lib("couch_mrview/include/couch_mrview.hrl"). % TODO: can we do without this? +% TODO: can we do without this? +-include_lib("couch_mrview/include/couch_mrview.hrl"). -include("couch_db_int.hrl"). -define(DBNAME_REGEX, @@ -289,7 +290,7 @@ wait_for_compaction(#db{main_pid = Pid} = Db, Timeout) -> is_compacting(DbName) -> couch_server:is_compacting(DbName). -has_access_enabled(#db{access=true}) -> true; +has_access_enabled(#db{access = true}) -> true; has_access_enabled(_) -> false. is_read_from_ddoc_cache(Options) -> @@ -305,10 +306,11 @@ open_doc(Db, IdOrDocInfo) -> open_doc(Db, Id, Options0) -> increment_stat(Db, [couchdb, database_reads]), - Options = case has_access_enabled(Db) of - true -> Options0 ++ [conflicts]; - _Else -> Options0 - end, + Options = + case has_access_enabled(Db) of + true -> Options0 ++ [conflicts]; + _Else -> Options0 + end, case open_doc_int(Db, Id, Options) of {ok, #doc{deleted = true} = Doc} -> case lists:member(deleted, Options) of @@ -792,8 +794,8 @@ security_error_type(#user_ctx{name = null}) -> security_error_type(#user_ctx{name = _}) -> forbidden. -is_per_user_ddoc(#doc{access=[]}) -> false; -is_per_user_ddoc(#doc{access=[<<"_users">>]}) -> false; +is_per_user_ddoc(#doc{access = []}) -> false; +is_per_user_ddoc(#doc{access = [<<"_users">>]}) -> false; is_per_user_ddoc(_) -> true. validate_access(Db, Doc) -> @@ -802,17 +804,20 @@ validate_access(Db, Doc) -> validate_access(Db, Doc, Options) -> validate_access1(has_access_enabled(Db), Db, Doc, Options). -validate_access1(false, _Db, _Doc, _Options) -> ok; -validate_access1(true, Db, #doc{meta=Meta}=Doc, Options) -> +validate_access1(false, _Db, _Doc, _Options) -> + ok; +validate_access1(true, Db, #doc{meta = Meta} = Doc, Options) -> case proplists:get_value(conflicts, Meta) of - undefined -> % no conflicts + % no conflicts + undefined -> case is_read_from_ddoc_cache(Options) andalso is_per_user_ddoc(Doc) of true -> throw({not_found, missing}); _False -> validate_access2(Db, Doc) end; - _Else -> % only admins can read conflicted docs in _access dbs - % TODO: expand: if leaves agree on _access, then a user should be able - % to proceed normally, only if they disagree should this become admin-only + % only admins can read conflicted docs in _access dbs + _Else -> + % TODO: expand: if leaves agree on _access, then a user should be able + % to proceed normally, only if they disagree should this become admin-only case is_admin(Db) of true -> ok; _Else2 -> throw({forbidden, <<"document is in conflict">>}) @@ -825,36 +830,35 @@ validate_access3(true) -> ok; % TODO: fix language validate_access3(_) -> throw({forbidden, <<"can't touch this">>}). -check_access(Db, #doc{access=Access}) -> +check_access(Db, #doc{access = Access}) -> check_access(Db, Access); check_access(Db, Access) -> %couch_log:notice("~n Db.user_ctx: ~p, Access: ~p ~n", [Db#db.user_ctx, Access]), #user_ctx{ - name=UserName, - roles=UserRoles + name = UserName, + roles = UserRoles } = Db#db.user_ctx, case Access of - [] -> - % if doc has no _access, userCtX must be admin - is_admin(Db); - Access -> - % if doc has _access, userCtx must be admin OR matching user or role - % _access = ["a", "b", ] - case is_admin(Db) of - true -> - true; - _ -> - case {check_name(UserName, Access), check_roles(UserRoles, Access)} of - {true, _} -> true; - {_, true} -> true; - _ -> false + [] -> + % if doc has no _access, userCtX must be admin + is_admin(Db); + Access -> + % if doc has _access, userCtx must be admin OR matching user or role + % _access = ["a", "b", ] + case is_admin(Db) of + true -> + true; + _ -> + case {check_name(UserName, Access), check_roles(UserRoles, Access)} of + {true, _} -> true; + {_, true} -> true; + _ -> false + end end - end end. check_name(null, _Access) -> true; -check_name(UserName, Access) -> - lists:member(UserName, Access). +check_name(UserName, Access) -> lists:member(UserName, Access). % nicked from couch_db:check_security % TODO: might need DRY @@ -1004,14 +1008,14 @@ group_alike_docs([Doc | Rest], [Bucket | RestBuckets]) -> end. validate_doc_update(#db{} = Db, #doc{id = <<"_design/", _/binary>>} = Doc, _GetDiskDocFun) -> - case couch_doc:has_access(Doc) of - true -> - validate_ddoc(Db, Doc); - _Else -> - case catch check_is_admin(Db) of - ok -> validate_ddoc(Db, Doc); - Error -> Error - end + case couch_doc:has_access(Doc) of + true -> + validate_ddoc(Db, Doc); + _Else -> + case catch check_is_admin(Db) of + ok -> validate_ddoc(Db, Doc); + Error -> Error + end end; validate_doc_update(#db{validate_doc_funs = undefined} = Db, Doc, Fun) -> ValidationFuns = load_validation_funs(Db), @@ -1412,24 +1416,28 @@ validate_update(Db, Doc) -> Error -> Error end. - validate_docs_access(Db, DocBuckets, DocErrors) -> - validate_docs_access1(Db, DocBuckets, {[], DocErrors}). + validate_docs_access1(Db, DocBuckets, {[], DocErrors}). validate_docs_access1(_Db, [], {DocBuckets0, DocErrors}) -> - DocBuckets1 = lists:reverse(lists:map(fun lists:reverse/1, DocBuckets0)), - DocBuckets = case DocBuckets1 of - [[]] -> []; - Else -> Else - end, + DocBuckets1 = lists:reverse(lists:map(fun lists:reverse/1, DocBuckets0)), + DocBuckets = + case DocBuckets1 of + [[]] -> []; + Else -> Else + end, {ok, DocBuckets, lists:reverse(DocErrors)}; -validate_docs_access1(Db, [DocBucket|RestBuckets], {DocAcc, ErrorAcc}) -> - {NewBuckets, NewErrors} = lists:foldl(fun(Doc, {Acc, ErrAcc}) -> - case catch validate_access(Db, Doc) of - ok -> {[Doc|Acc], ErrAcc}; - Error -> {Acc, [{doc_tag(Doc), Error}|ErrAcc]} - end - end, {[], ErrorAcc}, DocBucket), +validate_docs_access1(Db, [DocBucket | RestBuckets], {DocAcc, ErrorAcc}) -> + {NewBuckets, NewErrors} = lists:foldl( + fun(Doc, {Acc, ErrAcc}) -> + case catch validate_access(Db, Doc) of + ok -> {[Doc | Acc], ErrAcc}; + Error -> {Acc, [{doc_tag(Doc), Error} | ErrAcc]} + end + end, + {[], ErrorAcc}, + DocBucket + ), validate_docs_access1(Db, RestBuckets, {[NewBuckets | DocAcc], NewErrors}). update_docs(Db, Docs0, Options, ?REPLICATED_CHANGES) -> @@ -1462,28 +1470,34 @@ update_docs(Db, Docs0, Options, ?REPLICATED_CHANGES) -> [merge_conflicts | Options] ), case couch_db:has_access_enabled(Db) of - false -> - % we’re done here - {ok, DocErrors}; - _ -> - AccessViolations = lists:filter(fun({_Ref, Tag}) -> Tag =:= access end, Results), - case length(AccessViolations) of - 0 -> - % we’re done here - {ok, DocErrors}; - _ -> - % dig out FDIs from Docs matching our tags/refs - DocsDict = lists:foldl(fun(Doc, Dict) -> - Tag = doc_tag(Doc), - dict:store(Tag, Doc, Dict) - end, dict:new(), Docs), - AccessResults = lists:map(fun({Ref, Access}) -> - { dict:fetch(Ref, DocsDict), Access } - end, AccessViolations), - {ok, AccessResults} - end - end; - + false -> + % we’re done here + {ok, DocErrors}; + _ -> + AccessViolations = lists:filter(fun({_Ref, Tag}) -> Tag =:= access end, Results), + case length(AccessViolations) of + 0 -> + % we’re done here + {ok, DocErrors}; + _ -> + % dig out FDIs from Docs matching our tags/refs + DocsDict = lists:foldl( + fun(Doc, Dict) -> + Tag = doc_tag(Doc), + dict:store(Tag, Doc, Dict) + end, + dict:new(), + Docs + ), + AccessResults = lists:map( + fun({Ref, Access}) -> + {dict:fetch(Ref, DocsDict), Access} + end, + AccessViolations + ), + {ok, AccessResults} + end + end; update_docs(Db, Docs0, Options, ?INTERACTIVE_EDIT) -> Docs = tag_docs(Docs0), @@ -2008,7 +2022,10 @@ open_doc_revs_int(Db, IdRevs, Options) -> % we have the rev in our list but know nothing about it {{not_found, missing}, {Pos, Rev}}; #leaf{deleted = IsDeleted, ptr = SummaryPtr} -> - {ok, make_doc(Db, Id, IsDeleted, SummaryPtr, FoundRevPath, Access)} + {ok, + make_doc( + Db, Id, IsDeleted, SummaryPtr, FoundRevPath, Access + )} end end, FoundRevs @@ -2030,29 +2047,33 @@ open_doc_revs_int(Db, IdRevs, Options) -> open_doc_int(Db, <> = Id, Options) -> case couch_db_engine:open_local_docs(Db, [Id]) of [#doc{} = Doc] -> - case Doc#doc.body of - { Body } -> - Access = couch_util:get_value(<<"_access">>, Body), - apply_open_options(Db, {ok, Doc#doc{access = Access}}, Options); - _Else -> - apply_open_options(Db, {ok, Doc}, Options) - end; + case Doc#doc.body of + {Body} -> + Access = couch_util:get_value(<<"_access">>, Body), + apply_open_options(Db, {ok, Doc#doc{access = Access}}, Options); + _Else -> + apply_open_options(Db, {ok, Doc}, Options) + end; [not_found] -> {not_found, missing} end; open_doc_int(Db, #doc_info{id = Id, revs = [RevInfo | _], access = Access} = DocInfo, Options) -> #rev_info{deleted = IsDeleted, rev = {Pos, RevId}, body_sp = Bp} = RevInfo, Doc = make_doc(Db, Id, IsDeleted, Bp, {Pos, [RevId]}, Access), - apply_open_options(Db, + apply_open_options( + Db, {ok, Doc#doc{meta = doc_meta_info(DocInfo, [], Options)}}, Options ); -open_doc_int(Db, #full_doc_info{id = Id, rev_tree = RevTree, access = Access} = FullDocInfo, Options) -> +open_doc_int( + Db, #full_doc_info{id = Id, rev_tree = RevTree, access = Access} = FullDocInfo, Options +) -> #doc_info{revs = [#rev_info{deleted = IsDeleted, rev = Rev, body_sp = Bp} | _]} = DocInfo = couch_doc:to_doc_info(FullDocInfo), {[{_, RevPath}], []} = couch_key_tree:get(RevTree, [Rev]), Doc = make_doc(Db, Id, IsDeleted, Bp, RevPath, Access), - apply_open_options(Db, + apply_open_options( + Db, {ok, Doc#doc{meta = doc_meta_info(DocInfo, RevTree, Options)}}, Options ); diff --git a/src/couch/src/couch_db_updater.erl b/src/couch/src/couch_db_updater.erl index fb5a879ed44..db7515de184 100644 --- a/src/couch/src/couch_db_updater.erl +++ b/src/couch/src/couch_db_updater.erl @@ -25,8 +25,8 @@ -define(DEFAULT_MAX_PARTITION_SIZE, 16#280000000). -define(DEFAULT_SECURITY_OBJECT, [ - {<<"members">>,{[{<<"roles">>,[<<"_admin">>]}]}}, - {<<"admins">>, {[{<<"roles">>,[<<"_admin">>]}]}} + {<<"members">>, {[{<<"roles">>, [<<"_admin">>]}]}}, + {<<"admins">>, {[{<<"roles">>, [<<"_admin">>]}]}} ]). -record(merge_acc, { @@ -266,10 +266,11 @@ sort_and_tag_grouped_docs(Client, GroupedDocs) -> % duplicate documents if the incoming groups are not sorted, so as a sanity % check we sort them again here. See COUCHDB-2735. Cmp = fun - ([], []) -> false; % TODO: re-evaluate this addition, might be - % superflous now - ([#doc{id=A}|_], [#doc{id=B}|_]) -> A < B - end, + % TODO: re-evaluate this addition, might be + ([], []) -> false; + % superflous now + ([#doc{id = A} | _], [#doc{id = B} | _]) -> A < B + end, lists:map( fun(DocGroup) -> [{Client, maybe_tag_doc(D)} || D <- DocGroup] @@ -675,12 +676,12 @@ update_docs_int(Db, DocsList, LocalDocs, MergeConflicts, UserCtx) -> UpdateSeq = couch_db_engine:get_update_seq(Db), RevsLimit = couch_db_engine:get_revs_limit(Db), - Ids = [Id || [{_Client, #doc{id=Id}}|_] <- DocsList], + Ids = [Id || [{_Client, #doc{id = Id}} | _] <- DocsList], % TODO: maybe a perf hit, instead of zip3-ing existing Accesses into % our doc lists, maybe find 404 docs differently down in % validate_docs_access (revs is [], which we can then use % to skip validation as we know it is the first doc rev) - Accesses = [Access || [{_Client, #doc{access=Access}}|_] <- DocsList], + Accesses = [Access || [{_Client, #doc{access = Access}} | _] <- DocsList], % lookup up the old documents, if they exist. OldDocLookups = couch_db_engine:open_docs(Db, Ids), @@ -689,7 +690,7 @@ update_docs_int(Db, DocsList, LocalDocs, MergeConflicts, UserCtx) -> (_Id, #full_doc_info{} = FDI, _Access) -> FDI; (Id, not_found, Access) -> - #full_doc_info{id=Id,access=Access} + #full_doc_info{id = Id, access = Access} end, Ids, OldDocLookups, @@ -738,12 +739,12 @@ update_docs_int(Db, DocsList, LocalDocs, MergeConflicts, UserCtx) -> %couch_log:notice("~nDb: ~p, UserCtx: ~p~n", [Db, UserCtx]), - - { DocsListValidated, OldDocInfosValidated } = validate_docs_access(Db, UserCtx, DocsList, OldDocInfos), + {DocsListValidated, OldDocInfosValidated} = validate_docs_access( + Db, UserCtx, DocsList, OldDocInfos + ), %couch_log:notice("~nDocsListValidated: ~p, OldDocInfosValidated: ~p~n", [DocsListValidated, OldDocInfosValidated]), - {ok, AccOut} = merge_rev_trees(DocsListValidated, OldDocInfosValidated, AccIn), #merge_acc{ add_infos = NewFullDocInfos, @@ -772,7 +773,7 @@ update_docs_int(Db, DocsList, LocalDocs, MergeConflicts, UserCtx) -> % 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], + NonAccessIds = [Id || [{_Client, #doc{id = Id, access = []}} | _] <- DocsList], UpdatedDDocIds = lists:flatmap( fun (<<"_design/", _/binary>> = Id) -> [Id]; @@ -790,55 +791,68 @@ update_docs_int(Db, DocsList, LocalDocs, MergeConflicts, UserCtx) -> % true; % at this point, we already validated this Db is access enabled, so do the checks right away. -check_access(Db, UserCtx, Access) -> couch_db:check_access(Db#db{user_ctx=UserCtx}, Access). +check_access(Db, UserCtx, Access) -> couch_db:check_access(Db#db{user_ctx = UserCtx}, Access). validate_docs_access(Db, UserCtx, DocsList, OldDocInfos) -> case couch_db:has_access_enabled(Db) of true -> validate_docs_access_int(Db, UserCtx, DocsList, OldDocInfos); - _Else -> { DocsList, OldDocInfos } + _Else -> {DocsList, OldDocInfos} end. validate_docs_access_int(Db, UserCtx, DocsList, OldDocInfos) -> validate_docs_access(Db, UserCtx, DocsList, OldDocInfos, [], []). validate_docs_access(_Db, _UserCtx, [], [], DocsListValidated, OldDocInfosValidated) -> - { lists:reverse(DocsListValidated), lists:reverse(OldDocInfosValidated) }; -validate_docs_access(Db, UserCtx, [Docs | DocRest], [OldInfo | OldInfoRest], DocsListValidated, OldDocInfosValidated) -> + {lists:reverse(DocsListValidated), lists:reverse(OldDocInfosValidated)}; +validate_docs_access( + Db, UserCtx, [Docs | DocRest], [OldInfo | OldInfoRest], DocsListValidated, OldDocInfosValidated +) -> % loop over Docs as {Client, NewDoc} % validate Doc % if valid, then put back in Docs % if not, then send_result and skip %couch_log:notice("~nvalidate_docs_access() UserCtx: ~p, Docs: ~p, OldInfo: ~p~n", [UserCtx, Docs, OldInfo]), - NewDocs = lists:foldl(fun({ Client, Doc }, Acc) -> - %couch_log:notice("~nvalidate_docs_access lists:foldl() Doc: ~p Doc#doc.access: ~p~n", [Doc, Doc#doc.access]), + NewDocs = lists:foldl( + fun({Client, Doc}, Acc) -> + %couch_log:notice("~nvalidate_docs_access lists:foldl() Doc: ~p Doc#doc.access: ~p~n", [Doc, Doc#doc.access]), - % check if we are allowed to update the doc, skip when new doc - OldDocMatchesAccess = case OldInfo#full_doc_info.rev_tree of - [] -> true; - _ -> check_access(Db, UserCtx, OldInfo#full_doc_info.access) - end, + % check if we are allowed to update the doc, skip when new doc + OldDocMatchesAccess = + case OldInfo#full_doc_info.rev_tree of + [] -> true; + _ -> check_access(Db, UserCtx, OldInfo#full_doc_info.access) + end, - NewDocMatchesAccess = check_access(Db, UserCtx, Doc#doc.access), - %couch_log:notice("~nvalidate_docs_access lists:foldl() OldDocMatchesAccess: ~p, NewDocMatchesAccess: ~p, andalso: ~p~n", [OldDocMatchesAccess, NewDocMatchesAccess, OldDocMatchesAccess andalso NewDocMatchesAccess]), + NewDocMatchesAccess = check_access(Db, UserCtx, Doc#doc.access), + %couch_log:notice("~nvalidate_docs_access lists:foldl() OldDocMatchesAccess: ~p, NewDocMatchesAccess: ~p, andalso: ~p~n", [OldDocMatchesAccess, NewDocMatchesAccess, OldDocMatchesAccess andalso NewDocMatchesAccess]), - case OldDocMatchesAccess andalso NewDocMatchesAccess of - true -> % if valid, then send to DocsListValidated, OldDocsInfo + case OldDocMatchesAccess andalso NewDocMatchesAccess of + % if valid, then send to DocsListValidated, OldDocsInfo + true -> % and store the access context on the new doc - [{Client, Doc} | Acc]; - false -> % if invalid, then send_result tagged `access`(c.f. `conflict) - % and don’t add to DLV, nor ODI - send_result(Client, Doc, access), - Acc - end - end, [], Docs), - - { NewDocsListValidated, NewOldDocInfosValidated } = case length(NewDocs) of - 0 -> % we sent out all docs as invalid access, drop the old doc info associated with it - { [NewDocs | DocsListValidated], OldDocInfosValidated }; - _ -> - { [NewDocs | DocsListValidated], [OldInfo | OldDocInfosValidated] } - end, - validate_docs_access(Db, UserCtx, DocRest, OldInfoRest, NewDocsListValidated, NewOldDocInfosValidated). + [{Client, Doc} | Acc]; + % if invalid, then send_result tagged `access`(c.f. `conflict) + false -> + % and don’t add to DLV, nor ODI + send_result(Client, Doc, access), + Acc + end + end, + [], + Docs + ), + + {NewDocsListValidated, NewOldDocInfosValidated} = + case length(NewDocs) of + % we sent out all docs as invalid access, drop the old doc info associated with it + 0 -> + {[NewDocs | DocsListValidated], OldDocInfosValidated}; + _ -> + {[NewDocs | DocsListValidated], [OldInfo | OldDocInfosValidated]} + end, + validate_docs_access( + Db, UserCtx, DocRest, OldInfoRest, NewDocsListValidated, NewOldDocInfosValidated + ). apply_local_docs_access(Db, Docs) -> apply_local_docs_access1(couch_db:has_access_enabled(Db), Docs). @@ -846,10 +860,13 @@ apply_local_docs_access(Db, Docs) -> apply_local_docs_access1(false, Docs) -> Docs; apply_local_docs_access1(true, Docs) -> - lists:map(fun({Client, #doc{access = Access, body = {Body}} = Doc}) -> - Doc1 = Doc#doc{body = {[{<<"_access">>, Access} | Body]}}, - {Client, Doc1} - end, Docs). + lists:map( + fun({Client, #doc{access = Access, body = {Body}} = Doc}) -> + Doc1 = Doc#doc{body = {[{<<"_access">>, Access} | Body]}}, + {Client, Doc1} + end, + Docs + ). update_local_doc_revs(Docs) -> lists:foldl( @@ -1039,14 +1056,15 @@ get_meta_body_size(Meta) -> default_security_object(<<"shards/", _/binary>>) -> case config:get("couchdb", "default_security", "admin_only") of - "admin_only" -> ?DEFAULT_SECURITY_OBJECT; + "admin_only" -> + ?DEFAULT_SECURITY_OBJECT; Everyone when Everyone == "everyone"; Everyone == "admin_local" -> [] end; default_security_object(_DbName) -> case config:get("couchdb", "default_security", "admin_only") of Admin when Admin == "admin_only"; Admin == "admin_local" -> - ?DEFAULT_SECURITY_OBJECT; + ?DEFAULT_SECURITY_OBJECT; "everyone" -> [] end. diff --git a/src/couch/src/couch_doc.erl b/src/couch/src/couch_doc.erl index 70d593300a7..dec3301d485 100644 --- a/src/couch/src/couch_doc.erl +++ b/src/couch/src/couch_doc.erl @@ -430,7 +430,10 @@ to_doc_info_path(#full_doc_info{id = Id, rev_tree = Tree, update_seq = FDISeq, a ), [{_RevInfo, WinPath} | _] = SortedRevInfosAndPath, RevInfos = [RevInfo || {RevInfo, _Path} <- SortedRevInfosAndPath], - {#doc_info{id = Id, high_seq = max_seq(Tree, FDISeq), revs = RevInfos, access = Access}, WinPath}. + { + #doc_info{id = Id, high_seq = max_seq(Tree, FDISeq), revs = RevInfos, access = Access}, + WinPath + }. rev_info({#leaf{} = Leaf, {Pos, [RevId | _]}}) -> #rev_info{ @@ -472,7 +475,7 @@ is_deleted(Tree) -> get_access({Props}) -> get_access(couch_doc:from_json_obj({Props})); -get_access(#doc{access=Access}) -> +get_access(#doc{access = Access}) -> Access. has_access(Doc) -> diff --git a/src/couch/src/couch_util.erl b/src/couch/src/couch_util.erl index d0067b5e8cd..148f46f4ed8 100644 --- a/src/couch/src/couch_util.erl +++ b/src/couch/src/couch_util.erl @@ -838,8 +838,7 @@ validate_design_access(Db, DDoc) -> validate_design_access1(DDoc, couch_db:has_access_enabled(Db)). validate_design_access1(_DDoc, false) -> ok; -validate_design_access1(DDoc, true) -> - is_users_ddoc(DDoc). +validate_design_access1(DDoc, true) -> is_users_ddoc(DDoc). -is_users_ddoc(#doc{access=[<<"_users">>]}) -> ok; +is_users_ddoc(#doc{access = [<<"_users">>]}) -> ok; is_users_ddoc(_) -> throw({forbidden, <<"per-user ddoc access">>}). diff --git a/src/couch/test/eunit/couchdb_access_tests.erl b/src/couch/test/eunit/couchdb_access_tests.erl index 33fb576ff61..126e43fb262 100644 --- a/src/couch/test/eunit/couchdb_access_tests.erl +++ b/src/couch/test/eunit/couchdb_access_tests.erl @@ -18,10 +18,12 @@ -define(ADMIN_REQ_HEADERS, [?CONTENT_JSON, {basic_auth, {"a", "a"}}]). -define(USERX_REQ_HEADERS, [?CONTENT_JSON, {basic_auth, {"x", "x"}}]). -define(USERY_REQ_HEADERS, [?CONTENT_JSON, {basic_auth, {"y", "y"}}]). --define(SECURITY_OBJECT, {[ - {<<"members">>,{[{<<"roles">>,[<<"_admin">>, <<"_users">>]}]}}, - {<<"admins">>, {[{<<"roles">>,[<<"_admin">>]}]}} -]}). +-define(SECURITY_OBJECT, + {[ + {<<"members">>, {[{<<"roles">>, [<<"_admin">>, <<"_users">>]}]}}, + {<<"admins">>, {[{<<"roles">>, [<<"_admin">>]}]}} + ]} +). url() -> Addr = config:get("httpd", "bind_address", "127.0.0.1"), @@ -31,7 +33,9 @@ before_each(_) -> R = test_request:put(url() ++ "/db?q=1&n=1&access=true", ?ADMIN_REQ_HEADERS, ""), %?debugFmt("~nRequest: ~p~n", [R]), {ok, 201, _, _} = R, - {ok, _, _, _} = test_request:put(url() ++ "/db/_security", ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), + {ok, _, _, _} = test_request:put( + url() ++ "/db/_security", ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT) + ), url(). after_each(_, Url) -> @@ -43,10 +47,10 @@ after_each(_, Url) -> before_all() -> Couch = test_util:start_couch([chttpd, couch_replicator]), Hashed = couch_passwords:hash_admin_password("a"), - ok = config:set("admins", "a", binary_to_list(Hashed), _Persist=false), - ok = config:set("couchdb", "uuid", "21ac467c1bc05e9d9e9d2d850bb1108f", _Persist=false), - ok = config:set("log", "level", "debug", _Persist=false), - ok = config:set("per_doc_access", "enabled", "true", _Persist=false), + ok = config:set("admins", "a", binary_to_list(Hashed), _Persist = false), + ok = config:set("couchdb", "uuid", "21ac467c1bc05e9d9e9d2d850bb1108f", _Persist = false), + ok = config:set("log", "level", "debug", _Persist = false), + ok = config:set("per_doc_access", "enabled", "true", _Persist = false), % cleanup and setup {ok, _, _, _} = test_request:delete(url() ++ "/db", ?ADMIN_REQ_HEADERS), @@ -73,7 +77,6 @@ after_all(_) -> access_test_() -> Tests = [ - % Server config fun should_not_let_create_access_db_if_disabled/2, @@ -109,7 +112,6 @@ access_test_() -> fun should_let_admin_fetch_all_docs/2, fun should_let_user_fetch_their_own_all_docs/2, - % _changes fun should_let_admin_fetch_changes/2, fun should_let_user_fetch_their_own_changes/2, @@ -134,7 +136,6 @@ access_test_() -> % _revs_diff for docs you don’t have access to fun should_not_allow_user_to_revs_diff_other_docs/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 @@ -145,7 +146,8 @@ access_test_() -> "Access tests", { setup, - fun before_all/0, fun after_all/1, + fun before_all/0, + fun after_all/1, [ make_test_cases(clustered, Tests) ] @@ -159,7 +161,7 @@ make_test_cases(Mod, Funs) -> }. % Doc creation - % http://127.0.0.1:64903/db/a?revs=true&open_revs=%5B%221-23202479633c2b380f79507a776743d5%22%5D&latest=true +% http://127.0.0.1:64903/db/a?revs=true&open_revs=%5B%221-23202479633c2b380f79507a776743d5%22%5D&latest=true % should_do_the_thing(_PortType, Url) -> % ?_test(begin @@ -172,9 +174,9 @@ make_test_cases(Mod, Funs) -> % should_not_let_create_access_db_if_disabled(_PortType, Url) -> - ok = config:set("per_doc_access", "enabled", "false", _Persist=false), + ok = config:set("per_doc_access", "enabled", "false", _Persist = false), {ok, Code, _, _} = test_request:put(url() ++ "/db?q=1&n=1&access=true", ?ADMIN_REQ_HEADERS, ""), - ok = config:set("per_doc_access", "enabled", "true", _Persist=false), + ok = config:set("per_doc_access", "enabled", "true", _Persist = false), ?_assertEqual(400, Code). should_not_let_anonymous_user_create_doc(_PortType, Url) -> @@ -193,292 +195,489 @@ should_not_let_anonymous_user_create_doc(_PortType, Url) -> ?_assertEqual(401, Code). should_let_admin_create_doc_with_access(_PortType, Url) -> - {ok, Code, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), + {ok, Code, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), ?_assertEqual(201, Code). should_let_admin_create_doc_without_access(_PortType, Url) -> - {ok, Code, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1}"), + {ok, Code, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1}" + ), ?_assertEqual(201, Code). should_let_user_create_doc_for_themselves(_PortType, Url) -> - {ok, Code, _, _} = test_request:put(Url ++ "/db/b", - ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), + {ok, Code, _, _} = test_request:put( + Url ++ "/db/b", + ?USERX_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), ?_assertEqual(201, Code). should_not_let_user_create_doc_for_someone_else(_PortType, Url) -> - {ok, Code, _, _} = test_request:put(Url ++ "/db/c", - ?USERY_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), + {ok, Code, _, _} = test_request:put( + Url ++ "/db/c", + ?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\"]}"), + {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) -> ?_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), + 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\"]}"), + {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), + {ok, Code2, _, _} = test_request:get( + Url ++ "/db/_design/dx/_view/foo", + ?USERX_REQ_HEADERS + ), ?assertEqual(404, Code2), - {ok, Code3, _, _} = test_request:get(Url ++ "/db/_design/dx/_show/boo/b", - ?USERX_REQ_HEADERS), + {ok, Code3, _, _} = test_request:get( + Url ++ "/db/_design/dx/_show/boo/b", + ?USERX_REQ_HEADERS + ), ?assertEqual(404, Code3), - {ok, Code4, _, _} = test_request:get(Url ++ "/db/_design/dx/_list/hoo/foo", - ?USERX_REQ_HEADERS), + {ok, Code4, _, _} = test_request:get( + Url ++ "/db/_design/dx/_list/hoo/foo", + ?USERX_REQ_HEADERS + ), ?assertEqual(404, Code4), - {ok, Code5, _, _} = test_request:post(Url ++ "/db/_design/dx/_update/goo", - ?USERX_REQ_HEADERS, ""), + {ok, Code5, _, _} = test_request:post( + Url ++ "/db/_design/dx/_update/goo", + ?USERX_REQ_HEADERS, + "" + ), ?assertEqual(404, Code5), - {ok, Code6, _, _} = test_request:get(Url ++ "/db/_changes?filter=dx/loo", - ?USERX_REQ_HEADERS), + {ok, Code6, _, _} = test_request:get( + Url ++ "/db/_changes?filter=dx/loo", + ?USERX_REQ_HEADERS + ), ?assertEqual(404, Code6), - {ok, Code7, _, _} = test_request:get(Url ++ "/db/_changes?filter=_view&view=dx/foo", - ?USERX_REQ_HEADERS), + {ok, Code7, _, _} = test_request:get( + Url ++ "/db/_changes?filter=_view&view=dx/foo", + ?USERX_REQ_HEADERS + ), ?assertEqual(404, Code7) end). % Doc updates users_with_access_can_update_doc(_PortType, Url) -> - {ok, _, _, Body} = test_request:put(Url ++ "/db/b", - ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), + {ok, _, _, Body} = test_request:put( + Url ++ "/db/b", + ?USERX_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), {Json} = jiffy:decode(Body), Rev = couch_util:get_value(<<"rev">>, Json), - {ok, Code, _, _} = test_request:put(Url ++ "/db/b", + {ok, Code, _, _} = test_request:put( + Url ++ "/db/b", ?USERX_REQ_HEADERS, - "{\"a\":2,\"_access\":[\"x\"],\"_rev\":\"" ++ binary_to_list(Rev) ++ "\"}"), + "{\"a\":2,\"_access\":[\"x\"],\"_rev\":\"" ++ binary_to_list(Rev) ++ "\"}" + ), ?_assertEqual(201, Code). users_without_access_can_not_update_doc(_PortType, Url) -> - {ok, _, _, Body} = test_request:put(Url ++ "/db/b", - ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), + {ok, _, _, Body} = test_request:put( + Url ++ "/db/b", + ?USERX_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), {Json} = jiffy:decode(Body), Rev = couch_util:get_value(<<"rev">>, Json), - {ok, Code, _, _} = test_request:put(Url ++ "/db/b", + {ok, Code, _, _} = test_request:put( + Url ++ "/db/b", ?USERY_REQ_HEADERS, - "{\"a\":2,\"_access\":[\"y\"],\"_rev\":\"" ++ binary_to_list(Rev) ++ "\"}"), + "{\"a\":2,\"_access\":[\"y\"],\"_rev\":\"" ++ binary_to_list(Rev) ++ "\"}" + ), ?_assertEqual(403, Code). users_with_access_can_not_change_access(_PortType, Url) -> - {ok, _, _, Body} = test_request:put(Url ++ "/db/b", - ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), + {ok, _, _, Body} = test_request:put( + Url ++ "/db/b", + ?USERX_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), {Json} = jiffy:decode(Body), Rev = couch_util:get_value(<<"rev">>, Json), - {ok, Code, _, _} = test_request:put(Url ++ "/db/b", + {ok, Code, _, _} = test_request:put( + Url ++ "/db/b", ?USERX_REQ_HEADERS, - "{\"a\":2,\"_access\":[\"y\"],\"_rev\":\"" ++ binary_to_list(Rev) ++ "\"}"), + "{\"a\":2,\"_access\":[\"y\"],\"_rev\":\"" ++ binary_to_list(Rev) ++ "\"}" + ), ?_assertEqual(403, Code). users_with_access_can_not_remove_access(_PortType, Url) -> - {ok, _, _, Body} = test_request:put(Url ++ "/db/b", - ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), + {ok, _, _, Body} = test_request:put( + Url ++ "/db/b", + ?USERX_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), {Json} = jiffy:decode(Body), Rev = couch_util:get_value(<<"rev">>, Json), - {ok, Code, _, _} = test_request:put(Url ++ "/db/b", + {ok, Code, _, _} = test_request:put( + Url ++ "/db/b", ?USERX_REQ_HEADERS, - "{\"a\":2,\"_rev\":\"" ++ binary_to_list(Rev) ++ "\"}"), + "{\"a\":2,\"_rev\":\"" ++ binary_to_list(Rev) ++ "\"}" + ), ?_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\"]}"), - {ok, Code, _, _} = test_request:get(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS), + {ok, 201, _, _} = test_request:put( + Url ++ "/db/a", + ?USERX_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, Code, _, _} = test_request:get( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS + ), ?_assertEqual(200, Code). user_with_access_can_read_doc(_PortType, Url) -> - {ok, 201, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, Code, _, _} = test_request:get(Url ++ "/db/a", - ?USERX_REQ_HEADERS), + {ok, 201, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, Code, _, _} = test_request:get( + Url ++ "/db/a", + ?USERX_REQ_HEADERS + ), ?_assertEqual(200, Code). user_with_access_can_not_read_conflicted_doc(_PortType, Url) -> - {ok, 201, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"_id\":\"f1\",\"a\":1,\"_access\":[\"x\"]}"), - {ok, 201, _, _} = test_request:put(Url ++ "/db/a?new_edits=false", - ?ADMIN_REQ_HEADERS, "{\"_id\":\"f1\",\"_rev\":\"7-XYZ\",\"a\":1,\"_access\":[\"x\"]}"), - {ok, Code, _, _} = test_request:get(Url ++ "/db/a", - ?USERX_REQ_HEADERS), + {ok, 201, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"_id\":\"f1\",\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, 201, _, _} = test_request:put( + Url ++ "/db/a?new_edits=false", + ?ADMIN_REQ_HEADERS, + "{\"_id\":\"f1\",\"_rev\":\"7-XYZ\",\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, Code, _, _} = test_request:get( + Url ++ "/db/a", + ?USERX_REQ_HEADERS + ), ?_assertEqual(403, Code). admin_with_access_can_read_conflicted_doc(_PortType, Url) -> - {ok, 201, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"_id\":\"a\",\"a\":1,\"_access\":[\"x\"]}"), - {ok, 201, _, _} = test_request:put(Url ++ "/db/a?new_edits=false", - ?ADMIN_REQ_HEADERS, "{\"_id\":\"a\",\"_rev\":\"7-XYZ\",\"a\":1,\"_access\":[\"x\"]}"), - {ok, Code, _, _} = test_request:get(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS), + {ok, 201, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"_id\":\"a\",\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, 201, _, _} = test_request:put( + Url ++ "/db/a?new_edits=false", + ?ADMIN_REQ_HEADERS, + "{\"_id\":\"a\",\"_rev\":\"7-XYZ\",\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, Code, _, _} = test_request:get( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS + ), ?_assertEqual(200, Code). user_without_access_can_not_read_doc(_PortType, Url) -> - {ok, 201, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, Code, _, _} = test_request:get(Url ++ "/db/a", - ?USERY_REQ_HEADERS), + {ok, 201, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, Code, _, _} = test_request:get( + Url ++ "/db/a", + ?USERY_REQ_HEADERS + ), ?_assertEqual(403, Code). user_can_not_read_doc_without_access(_PortType, Url) -> - {ok, 201, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1}"), - {ok, Code, _, _} = test_request:get(Url ++ "/db/a", - ?USERX_REQ_HEADERS), + {ok, 201, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1}" + ), + {ok, Code, _, _} = test_request:get( + Url ++ "/db/a", + ?USERX_REQ_HEADERS + ), ?_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\"]}"), - {ok, Code, _, _} = test_request:delete(Url ++ "/db/a?rev=1-23202479633c2b380f79507a776743d5", - ?ADMIN_REQ_HEADERS), + {ok, 201, _, _} = test_request:put( + Url ++ "/db/a", + ?USERX_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, Code, _, _} = test_request:delete( + Url ++ "/db/a?rev=1-23202479633c2b380f79507a776743d5", + ?ADMIN_REQ_HEADERS + ), ?_assertEqual(200, Code). should_let_user_delete_doc_for_themselves(_PortType, Url) -> - {ok, 201, _, _} = test_request:put(Url ++ "/db/a", - ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:get(Url ++ "/db/a", - ?USERX_REQ_HEADERS), - {ok, Code, _, _} = test_request:delete(Url ++ "/db/a?rev=1-23202479633c2b380f79507a776743d5", - ?USERX_REQ_HEADERS), + {ok, 201, _, _} = test_request:put( + Url ++ "/db/a", + ?USERX_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:get( + Url ++ "/db/a", + ?USERX_REQ_HEADERS + ), + {ok, Code, _, _} = test_request:delete( + Url ++ "/db/a?rev=1-23202479633c2b380f79507a776743d5", + ?USERX_REQ_HEADERS + ), ?_assertEqual(200, Code). should_not_let_user_delete_doc_for_someone_else(_PortType, Url) -> - {ok, 201, _, _} = test_request:put(Url ++ "/db/a", - ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, Code, _, _} = test_request:delete(Url ++ "/db/a?rev=1-23202479633c2b380f79507a776743d5", - ?USERY_REQ_HEADERS), + {ok, 201, _, _} = test_request:put( + Url ++ "/db/a", + ?USERX_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, Code, _, _} = test_request:delete( + Url ++ "/db/a?rev=1-23202479633c2b380f79507a776743d5", + ?USERY_REQ_HEADERS + ), ?_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\"]}"), - {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/_all_docs?include_docs=true", - ?ADMIN_REQ_HEADERS), + {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/_all_docs?include_docs=true", + ?ADMIN_REQ_HEADERS + ), {Json} = jiffy:decode(Body), ?_assertEqual(4, proplists:get_value(<<"total_rows">>, Json)). should_let_user_fetch_their_own_all_docs(_PortType, Url) -> ?_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), + {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( + [ + {[ + {<<"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), + {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) + ?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). - % _changes should_let_admin_fetch_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", - ?ADMIN_REQ_HEADERS), + {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", + ?ADMIN_REQ_HEADERS + ), {Json} = jiffy:decode(Body), AmountOfDocs = length(proplists:get_value(<<"results">>, Json)), ?_assertEqual(4, AmountOfDocs). should_let_user_fetch_their_own_changes(_PortType, Url) -> ?_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), + {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), + ?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). @@ -487,38 +686,58 @@ should_let_user_fetch_their_own_changes(_PortType, Url) -> 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", - ?ADMIN_REQ_HEADERS, DDoc), + {ok, Code, _, _} = test_request:put( + Url ++ "/db/_design/a", + ?ADMIN_REQ_HEADERS, + DDoc + ), ?assertEqual(201, Code), - {ok, Code1, _, _} = test_request:get(Url ++ "/db/_design/a/_view/foo", - ?ADMIN_REQ_HEADERS), + {ok, Code1, _, _} = test_request:get( + Url ++ "/db/_design/a/_view/foo", + ?ADMIN_REQ_HEADERS + ), ?_assertEqual(404, Code1). should_not_allow_user_access_ddoc_view_request(_PortType, Url) -> DDoc = "{\"a\":1,\"_access\":[\"x\"],\"views\":{\"foo\":{\"map\":\"function() {}\"}}}", - {ok, Code, _, _} = test_request:put(Url ++ "/db/_design/a", - ?ADMIN_REQ_HEADERS, DDoc), + {ok, Code, _, _} = test_request:put( + Url ++ "/db/_design/a", + ?ADMIN_REQ_HEADERS, + DDoc + ), ?assertEqual(201, Code), - {ok, Code1, _, _} = test_request:get(Url ++ "/db/_design/a/_view/foo", - ?USERX_REQ_HEADERS), + {ok, Code1, _, _} = test_request:get( + Url ++ "/db/_design/a/_view/foo", + ?USERX_REQ_HEADERS + ), ?_assertEqual(404, Code1). should_allow_admin_users_access_ddoc_view_request(_PortType, Url) -> DDoc = "{\"a\":1,\"_access\":[\"_users\"],\"views\":{\"foo\":{\"map\":\"function() {}\"}}}", - {ok, Code, _, _} = test_request:put(Url ++ "/db/_design/a", - ?ADMIN_REQ_HEADERS, DDoc), + {ok, Code, _, _} = test_request:put( + Url ++ "/db/_design/a", + ?ADMIN_REQ_HEADERS, + DDoc + ), ?assertEqual(201, Code), - {ok, Code1, _, _} = test_request:get(Url ++ "/db/_design/a/_view/foo", - ?ADMIN_REQ_HEADERS), + {ok, Code1, _, _} = test_request:get( + Url ++ "/db/_design/a/_view/foo", + ?ADMIN_REQ_HEADERS + ), ?_assertEqual(200, Code1). should_allow_user_users_access_ddoc_view_request(_PortType, Url) -> DDoc = "{\"a\":1,\"_access\":[\"_users\"],\"views\":{\"foo\":{\"map\":\"function() {}\"}}}", - {ok, Code, _, _} = test_request:put(Url ++ "/db/_design/a", - ?ADMIN_REQ_HEADERS, DDoc), + {ok, Code, _, _} = test_request:put( + Url ++ "/db/_design/a", + ?ADMIN_REQ_HEADERS, + DDoc + ), ?assertEqual(201, Code), - {ok, Code1, _, _} = test_request:get(Url ++ "/db/_design/a/_view/foo", - ?USERX_REQ_HEADERS), + {ok, Code1, _, _} = test_request:get( + Url ++ "/db/_design/a/_view/foo", + ?USERX_REQ_HEADERS + ), ?_assertEqual(200, Code1). % replication @@ -526,28 +745,47 @@ should_allow_user_users_access_ddoc_view_request(_PortType, Url) -> should_allow_admin_to_replicate_from_access_to_access(_PortType, Url) -> ?_test(begin % create target db - {ok, 201, _, _} = test_request:put(url() ++ "/db2?q=1&n=1&access=true", - ?ADMIN_REQ_HEADERS, ""), + {ok, 201, _, _} = test_request:put( + url() ++ "/db2?q=1&n=1&access=true", + ?ADMIN_REQ_HEADERS, + "" + ), % set target db security - {ok, _, _, _} = test_request:put(url() ++ "/db2/_security", - ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), + {ok, _, _, _} = test_request:put( + url() ++ "/db2/_security", + ?ADMIN_REQ_HEADERS, + jiffy:encode(?SECURITY_OBJECT) + ), % create source docs - {ok, _, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db/b", - ?ADMIN_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db/c", - ?ADMIN_REQ_HEADERS, "{\"c\":3,\"_access\":[\"x\"]}"), + {ok, _, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db/b", + ?ADMIN_REQ_HEADERS, + "{\"b\":2,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db/c", + ?ADMIN_REQ_HEADERS, + "{\"c\":3,\"_access\":[\"x\"]}" + ), % replicate AdminUrl = string:replace(Url, "http://", "http://a:a@"), - EJRequestBody = {[ - {<<"source">>, list_to_binary(AdminUrl ++ "/db")}, - {<<"target">>, list_to_binary(AdminUrl ++ "/db2")} - ]}, - {ok, ResponseCode, _, ResponseBody} = test_request:post(Url ++ "/_replicate", - ?ADMIN_REQ_HEADERS, jiffy:encode(EJRequestBody)), + EJRequestBody = + {[ + {<<"source">>, list_to_binary(AdminUrl ++ "/db")}, + {<<"target">>, list_to_binary(AdminUrl ++ "/db2")} + ]}, + {ok, ResponseCode, _, ResponseBody} = test_request:post( + Url ++ "/_replicate", + ?ADMIN_REQ_HEADERS, + jiffy:encode(EJRequestBody) + ), % assert replication status {EJResponseBody} = jiffy:decode(ResponseBody), @@ -560,16 +798,18 @@ should_allow_admin_to_replicate_from_access_to_access(_PortType, Url) -> DocsReard = couch_util:get_value(<<"docs_read">>, History), DocsWritten = couch_util:get_value(<<"docs_written">>, History), DocWriteFailures = couch_util:get_value(<<"doc_write_failures">>, History), - + ?assertEqual(3, MissingChecked), ?assertEqual(3, MissingFound), ?assertEqual(3, DocsReard), ?assertEqual(3, DocsWritten), ?assertEqual(0, DocWriteFailures), - + % assert docs in target db - {ok, 200, _, ADBody} = test_request:get(Url ++ "/db2/_all_docs?include_docs=true", - ?ADMIN_REQ_HEADERS), + {ok, 200, _, ADBody} = test_request:get( + Url ++ "/db2/_all_docs?include_docs=true", + ?ADMIN_REQ_HEADERS + ), {Json} = jiffy:decode(ADBody), ?assertEqual(3, proplists:get_value(<<"total_rows">>, Json)) end). @@ -577,28 +817,47 @@ should_allow_admin_to_replicate_from_access_to_access(_PortType, Url) -> should_allow_admin_to_replicate_from_no_access_to_access(_PortType, Url) -> ?_test(begin % create target db - {ok, 201, _, _} = test_request:put(url() ++ "/db2?q=1&n=1", - ?ADMIN_REQ_HEADERS, ""), + {ok, 201, _, _} = test_request:put( + url() ++ "/db2?q=1&n=1", + ?ADMIN_REQ_HEADERS, + "" + ), % set target db security - {ok, _, _, _} = test_request:put(url() ++ "/db2/_security", - ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), + {ok, _, _, _} = test_request:put( + url() ++ "/db2/_security", + ?ADMIN_REQ_HEADERS, + jiffy:encode(?SECURITY_OBJECT) + ), % create source docs - {ok, _, _, _} = test_request:put(Url ++ "/db2/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db2/b", - ?ADMIN_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db2/c", - ?ADMIN_REQ_HEADERS, "{\"c\":3,\"_access\":[\"x\"]}"), + {ok, _, _, _} = test_request:put( + Url ++ "/db2/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db2/b", + ?ADMIN_REQ_HEADERS, + "{\"b\":2,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db2/c", + ?ADMIN_REQ_HEADERS, + "{\"c\":3,\"_access\":[\"x\"]}" + ), % replicate AdminUrl = string:replace(Url, "http://", "http://a:a@"), - EJRequestBody = {[ - {<<"source">>, list_to_binary(AdminUrl ++ "/db2")}, - {<<"target">>, list_to_binary(AdminUrl ++ "/db")} - ]}, - {ok, ResponseCode, _, ResponseBody} = test_request:post(Url ++ "/_replicate", - ?ADMIN_REQ_HEADERS, jiffy:encode(EJRequestBody)), + EJRequestBody = + {[ + {<<"source">>, list_to_binary(AdminUrl ++ "/db2")}, + {<<"target">>, list_to_binary(AdminUrl ++ "/db")} + ]}, + {ok, ResponseCode, _, ResponseBody} = test_request:post( + Url ++ "/_replicate", + ?ADMIN_REQ_HEADERS, + jiffy:encode(EJRequestBody) + ), % assert replication status {EJResponseBody} = jiffy:decode(ResponseBody), @@ -611,16 +870,18 @@ should_allow_admin_to_replicate_from_no_access_to_access(_PortType, Url) -> DocsReard = couch_util:get_value(<<"docs_read">>, History), DocsWritten = couch_util:get_value(<<"docs_written">>, History), DocWriteFailures = couch_util:get_value(<<"doc_write_failures">>, History), - + ?assertEqual(3, MissingChecked), ?assertEqual(3, MissingFound), ?assertEqual(3, DocsReard), ?assertEqual(3, DocsWritten), ?assertEqual(0, DocWriteFailures), - + % assert docs in target db - {ok, 200, _, ADBody} = test_request:get(Url ++ "/db/_all_docs?include_docs=true", - ?ADMIN_REQ_HEADERS), + {ok, 200, _, ADBody} = test_request:get( + Url ++ "/db/_all_docs?include_docs=true", + ?ADMIN_REQ_HEADERS + ), {Json} = jiffy:decode(ADBody), ?assertEqual(3, proplists:get_value(<<"total_rows">>, Json)) end). @@ -628,28 +889,47 @@ should_allow_admin_to_replicate_from_no_access_to_access(_PortType, Url) -> should_allow_admin_to_replicate_from_access_to_no_access(_PortType, Url) -> ?_test(begin % create target db - {ok, 201, _, _} = test_request:put(url() ++ "/db2?q=1&n=1", - ?ADMIN_REQ_HEADERS, ""), + {ok, 201, _, _} = test_request:put( + url() ++ "/db2?q=1&n=1", + ?ADMIN_REQ_HEADERS, + "" + ), % set target db security - {ok, _, _, _} = test_request:put(url() ++ "/db2/_security", - ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), + {ok, _, _, _} = test_request:put( + url() ++ "/db2/_security", + ?ADMIN_REQ_HEADERS, + jiffy:encode(?SECURITY_OBJECT) + ), % create source docs - {ok, _, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db/b", - ?ADMIN_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db/c", - ?ADMIN_REQ_HEADERS, "{\"c\":3,\"_access\":[\"x\"]}"), + {ok, _, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db/b", + ?ADMIN_REQ_HEADERS, + "{\"b\":2,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db/c", + ?ADMIN_REQ_HEADERS, + "{\"c\":3,\"_access\":[\"x\"]}" + ), % replicate AdminUrl = string:replace(Url, "http://", "http://a:a@"), - EJRequestBody = {[ - {<<"source">>, list_to_binary(AdminUrl ++ "/db")}, - {<<"target">>, list_to_binary(AdminUrl ++ "/db2")} - ]}, - {ok, ResponseCode, _, ResponseBody} = test_request:post(Url ++ "/_replicate", - ?ADMIN_REQ_HEADERS, jiffy:encode(EJRequestBody)), + EJRequestBody = + {[ + {<<"source">>, list_to_binary(AdminUrl ++ "/db")}, + {<<"target">>, list_to_binary(AdminUrl ++ "/db2")} + ]}, + {ok, ResponseCode, _, ResponseBody} = test_request:post( + Url ++ "/_replicate", + ?ADMIN_REQ_HEADERS, + jiffy:encode(EJRequestBody) + ), % assert replication status {EJResponseBody} = jiffy:decode(ResponseBody), @@ -662,16 +942,18 @@ should_allow_admin_to_replicate_from_access_to_no_access(_PortType, Url) -> DocsReard = couch_util:get_value(<<"docs_read">>, History), DocsWritten = couch_util:get_value(<<"docs_written">>, History), DocWriteFailures = couch_util:get_value(<<"doc_write_failures">>, History), - + ?assertEqual(3, MissingChecked), ?assertEqual(3, MissingFound), ?assertEqual(3, DocsReard), ?assertEqual(3, DocsWritten), ?assertEqual(0, DocWriteFailures), - + % assert docs in target db - {ok, 200, _, ADBody} = test_request:get(Url ++ "/db2/_all_docs?include_docs=true", - ?ADMIN_REQ_HEADERS), + {ok, 200, _, ADBody} = test_request:get( + Url ++ "/db2/_all_docs?include_docs=true", + ?ADMIN_REQ_HEADERS + ), {Json} = jiffy:decode(ADBody), ?assertEqual(3, proplists:get_value(<<"total_rows">>, Json)) end). @@ -679,34 +961,59 @@ should_allow_admin_to_replicate_from_access_to_no_access(_PortType, Url) -> should_allow_admin_to_replicate_from_no_access_to_no_access(_PortType, Url) -> ?_test(begin % create source and target dbs - {ok, 201, _, _} = test_request:put(url() ++ "/db2?q=1&n=1", - ?ADMIN_REQ_HEADERS, ""), + {ok, 201, _, _} = test_request:put( + url() ++ "/db2?q=1&n=1", + ?ADMIN_REQ_HEADERS, + "" + ), % set target db security - {ok, _, _, _} = test_request:put(url() ++ "/db2/_security", - ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), - - {ok, 201, _, _} = test_request:put(url() ++ "/db3?q=1&n=1", - ?ADMIN_REQ_HEADERS, ""), + {ok, _, _, _} = test_request:put( + url() ++ "/db2/_security", + ?ADMIN_REQ_HEADERS, + jiffy:encode(?SECURITY_OBJECT) + ), + + {ok, 201, _, _} = test_request:put( + url() ++ "/db3?q=1&n=1", + ?ADMIN_REQ_HEADERS, + "" + ), % set target db security - {ok, _, _, _} = test_request:put(url() ++ "/db3/_security", - ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), + {ok, _, _, _} = test_request:put( + url() ++ "/db3/_security", + ?ADMIN_REQ_HEADERS, + jiffy:encode(?SECURITY_OBJECT) + ), % create source docs - {ok, _, _, _} = test_request:put(Url ++ "/db2/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db2/b", - ?ADMIN_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db2/c", - ?ADMIN_REQ_HEADERS, "{\"c\":3,\"_access\":[\"x\"]}"), + {ok, _, _, _} = test_request:put( + Url ++ "/db2/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db2/b", + ?ADMIN_REQ_HEADERS, + "{\"b\":2,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db2/c", + ?ADMIN_REQ_HEADERS, + "{\"c\":3,\"_access\":[\"x\"]}" + ), % replicate AdminUrl = string:replace(Url, "http://", "http://a:a@"), - EJRequestBody = {[ - {<<"source">>, list_to_binary(AdminUrl ++ "/db2")}, - {<<"target">>, list_to_binary(AdminUrl ++ "/db3")} - ]}, - {ok, ResponseCode, _, ResponseBody} = test_request:post(Url ++ "/_replicate", - ?ADMIN_REQ_HEADERS, jiffy:encode(EJRequestBody)), + EJRequestBody = + {[ + {<<"source">>, list_to_binary(AdminUrl ++ "/db2")}, + {<<"target">>, list_to_binary(AdminUrl ++ "/db3")} + ]}, + {ok, ResponseCode, _, ResponseBody} = test_request:post( + Url ++ "/_replicate", + ?ADMIN_REQ_HEADERS, + jiffy:encode(EJRequestBody) + ), % assert replication status {EJResponseBody} = jiffy:decode(ResponseBody), @@ -719,16 +1026,18 @@ should_allow_admin_to_replicate_from_no_access_to_no_access(_PortType, Url) -> DocsReard = couch_util:get_value(<<"docs_read">>, History), DocsWritten = couch_util:get_value(<<"docs_written">>, History), DocWriteFailures = couch_util:get_value(<<"doc_write_failures">>, History), - + ?assertEqual(3, MissingChecked), ?assertEqual(3, MissingFound), ?assertEqual(3, DocsReard), ?assertEqual(3, DocsWritten), ?assertEqual(0, DocWriteFailures), - + % assert docs in target db - {ok, 200, _, ADBody} = test_request:get(Url ++ "/db3/_all_docs?include_docs=true", - ?ADMIN_REQ_HEADERS), + {ok, 200, _, ADBody} = test_request:get( + Url ++ "/db3/_all_docs?include_docs=true", + ?ADMIN_REQ_HEADERS + ), {Json} = jiffy:decode(ADBody), ?assertEqual(3, proplists:get_value(<<"total_rows">>, Json)) end). @@ -736,28 +1045,47 @@ should_allow_admin_to_replicate_from_no_access_to_no_access(_PortType, Url) -> should_allow_user_to_replicate_from_access_to_access(_PortType, Url) -> ?_test(begin % create source and target dbs - {ok, 201, _, _} = test_request:put(url() ++ "/db2?q=1&n=1&access=true", - ?ADMIN_REQ_HEADERS, ""), + {ok, 201, _, _} = test_request:put( + url() ++ "/db2?q=1&n=1&access=true", + ?ADMIN_REQ_HEADERS, + "" + ), % set target db security - {ok, _, _, _} = test_request:put(url() ++ "/db2/_security", - ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), + {ok, _, _, _} = test_request:put( + url() ++ "/db2/_security", + ?ADMIN_REQ_HEADERS, + jiffy:encode(?SECURITY_OBJECT) + ), % create source docs - {ok, _, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db/b", - ?ADMIN_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db/c", - ?ADMIN_REQ_HEADERS, "{\"c\":3,\"_access\":[\"y\"]}"), + {ok, _, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db/b", + ?ADMIN_REQ_HEADERS, + "{\"b\":2,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db/c", + ?ADMIN_REQ_HEADERS, + "{\"c\":3,\"_access\":[\"y\"]}" + ), % replicate UserXUrl = string:replace(Url, "http://", "http://x:x@"), - EJRequestBody = {[ - {<<"source">>, list_to_binary(UserXUrl ++ "/db")}, - {<<"target">>, list_to_binary(UserXUrl ++ "/db2")} - ]}, - {ok, ResponseCode, _, ResponseBody} = test_request:post(Url ++ "/_replicate", - ?USERX_REQ_HEADERS, jiffy:encode(EJRequestBody)), + EJRequestBody = + {[ + {<<"source">>, list_to_binary(UserXUrl ++ "/db")}, + {<<"target">>, list_to_binary(UserXUrl ++ "/db2")} + ]}, + {ok, ResponseCode, _, ResponseBody} = test_request:post( + Url ++ "/_replicate", + ?USERX_REQ_HEADERS, + jiffy:encode(EJRequestBody) + ), % ?debugFmt("~nResponseBody: ~p~n", [ResponseBody]), % assert replication status @@ -772,28 +1100,34 @@ should_allow_user_to_replicate_from_access_to_access(_PortType, Url) -> DocsReard = couch_util:get_value(<<"docs_read">>, History), DocsWritten = couch_util:get_value(<<"docs_written">>, History), DocWriteFailures = couch_util:get_value(<<"doc_write_failures">>, History), - + ?assertEqual(2, MissingChecked), ?assertEqual(2, MissingFound), ?assertEqual(2, DocsReard), ?assertEqual(2, DocsWritten), ?assertEqual(0, DocWriteFailures), - + % assert access in local doc ReplicationId = couch_util:get_value(<<"replication_id">>, EJResponseBody), - {ok, 200, _, CheckPoint} = test_request:get(Url ++ "/db/_local/" ++ ReplicationId, - ?USERX_REQ_HEADERS), + {ok, 200, _, CheckPoint} = test_request:get( + Url ++ "/db/_local/" ++ ReplicationId, + ?USERX_REQ_HEADERS + ), {EJCheckPoint} = jiffy:decode(CheckPoint), Access = couch_util:get_value(<<"_access">>, EJCheckPoint), ?assertEqual([<<"x">>], Access), % make sure others can’t read our local docs - {ok, 403, _, _} = test_request:get(Url ++ "/db/_local/" ++ ReplicationId, - ?USERY_REQ_HEADERS), + {ok, 403, _, _} = test_request:get( + Url ++ "/db/_local/" ++ ReplicationId, + ?USERY_REQ_HEADERS + ), % assert docs in target db - {ok, 200, _, ADBody} = test_request:get(Url ++ "/db2/_all_docs?include_docs=true", - ?ADMIN_REQ_HEADERS), + {ok, 200, _, ADBody} = test_request:get( + Url ++ "/db2/_all_docs?include_docs=true", + ?ADMIN_REQ_HEADERS + ), {Json} = jiffy:decode(ADBody), ?assertEqual(2, proplists:get_value(<<"total_rows">>, Json)) end). @@ -801,28 +1135,47 @@ should_allow_user_to_replicate_from_access_to_access(_PortType, Url) -> should_allow_user_to_replicate_from_access_to_no_access(_PortType, Url) -> ?_test(begin % create source and target dbs - {ok, 201, _, _} = test_request:put(url() ++ "/db2?q=1&n=1", - ?ADMIN_REQ_HEADERS, ""), + {ok, 201, _, _} = test_request:put( + url() ++ "/db2?q=1&n=1", + ?ADMIN_REQ_HEADERS, + "" + ), % set target db security - {ok, _, _, _} = test_request:put(url() ++ "/db2/_security", - ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), + {ok, _, _, _} = test_request:put( + url() ++ "/db2/_security", + ?ADMIN_REQ_HEADERS, + jiffy:encode(?SECURITY_OBJECT) + ), % create source docs - {ok, _, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db/b", - ?ADMIN_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db/c", - ?ADMIN_REQ_HEADERS, "{\"c\":3,\"_access\":[\"y\"]}"), + {ok, _, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db/b", + ?ADMIN_REQ_HEADERS, + "{\"b\":2,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db/c", + ?ADMIN_REQ_HEADERS, + "{\"c\":3,\"_access\":[\"y\"]}" + ), % replicate UserXUrl = string:replace(Url, "http://", "http://x:x@"), - EJRequestBody = {[ - {<<"source">>, list_to_binary(UserXUrl ++ "/db")}, - {<<"target">>, list_to_binary(UserXUrl ++ "/db2")} - ]}, - {ok, ResponseCode, _, ResponseBody} = test_request:post(Url ++ "/_replicate", - ?USERX_REQ_HEADERS, jiffy:encode(EJRequestBody)), + EJRequestBody = + {[ + {<<"source">>, list_to_binary(UserXUrl ++ "/db")}, + {<<"target">>, list_to_binary(UserXUrl ++ "/db2")} + ]}, + {ok, ResponseCode, _, ResponseBody} = test_request:post( + Url ++ "/_replicate", + ?USERX_REQ_HEADERS, + jiffy:encode(EJRequestBody) + ), % assert replication status {EJResponseBody} = jiffy:decode(ResponseBody), @@ -835,16 +1188,18 @@ should_allow_user_to_replicate_from_access_to_no_access(_PortType, Url) -> DocsReard = couch_util:get_value(<<"docs_read">>, History), DocsWritten = couch_util:get_value(<<"docs_written">>, History), DocWriteFailures = couch_util:get_value(<<"doc_write_failures">>, History), - + ?assertEqual(2, MissingChecked), ?assertEqual(2, MissingFound), ?assertEqual(2, DocsReard), ?assertEqual(2, DocsWritten), ?assertEqual(0, DocWriteFailures), - + % assert docs in target db - {ok, 200, _, ADBody} = test_request:get(Url ++ "/db2/_all_docs?include_docs=true", - ?ADMIN_REQ_HEADERS), + {ok, 200, _, ADBody} = test_request:get( + Url ++ "/db2/_all_docs?include_docs=true", + ?ADMIN_REQ_HEADERS + ), {Json} = jiffy:decode(ADBody), ?assertEqual(2, proplists:get_value(<<"total_rows">>, Json)) end). @@ -852,11 +1207,17 @@ should_allow_user_to_replicate_from_access_to_no_access(_PortType, Url) -> should_allow_user_to_replicate_from_no_access_to_access(_PortType, Url) -> ?_test(begin % create source and target dbs - {ok, 201, _, _} = test_request:put(url() ++ "/db2?q=1&n=1", - ?ADMIN_REQ_HEADERS, ""), + {ok, 201, _, _} = test_request:put( + url() ++ "/db2?q=1&n=1", + ?ADMIN_REQ_HEADERS, + "" + ), % set target db security - {ok, _, _, _} = test_request:put(url() ++ "/db2/_security", - ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), + {ok, _, _, _} = test_request:put( + url() ++ "/db2/_security", + ?ADMIN_REQ_HEADERS, + jiffy:encode(?SECURITY_OBJECT) + ), % leave for easier debugging % VduFun = <<"function(newdoc, olddoc, userctx) {if(newdoc._id == \"b\") throw({'forbidden':'fail'})}">>, @@ -867,22 +1228,34 @@ should_allow_user_to_replicate_from_no_access_to_access(_PortType, Url) -> % {ok, _, _, _} = test_request:put(Url ++ "/db/_design/vdu", % ?ADMIN_REQ_HEADERS, jiffy:encode(DDoc)), % create source docs - {ok, _, _, _} = test_request:put(Url ++ "/db2/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db2/b", - ?ADMIN_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db2/c", - ?ADMIN_REQ_HEADERS, "{\"c\":3,\"_access\":[\"y\"]}"), - + {ok, _, _, _} = test_request:put( + Url ++ "/db2/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db2/b", + ?ADMIN_REQ_HEADERS, + "{\"b\":2,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db2/c", + ?ADMIN_REQ_HEADERS, + "{\"c\":3,\"_access\":[\"y\"]}" + ), % replicate UserXUrl = string:replace(Url, "http://", "http://x:x@"), - EJRequestBody = {[ - {<<"source">>, list_to_binary(UserXUrl ++ "/db2")}, - {<<"target">>, list_to_binary(UserXUrl ++ "/db")} - ]}, - {ok, ResponseCode, _, ResponseBody} = test_request:post(Url ++ "/_replicate", - ?USERX_REQ_HEADERS, jiffy:encode(EJRequestBody)), + EJRequestBody = + {[ + {<<"source">>, list_to_binary(UserXUrl ++ "/db2")}, + {<<"target">>, list_to_binary(UserXUrl ++ "/db")} + ]}, + {ok, ResponseCode, _, ResponseBody} = test_request:post( + Url ++ "/_replicate", + ?USERX_REQ_HEADERS, + jiffy:encode(EJRequestBody) + ), % assert replication status {EJResponseBody} = jiffy:decode(ResponseBody), @@ -895,16 +1268,18 @@ should_allow_user_to_replicate_from_no_access_to_access(_PortType, Url) -> DocsReard = couch_util:get_value(<<"docs_read">>, History), DocsWritten = couch_util:get_value(<<"docs_written">>, History), DocWriteFailures = couch_util:get_value(<<"doc_write_failures">>, History), - + ?assertEqual(3, MissingChecked), ?assertEqual(3, MissingFound), ?assertEqual(3, DocsReard), ?assertEqual(2, DocsWritten), ?assertEqual(1, DocWriteFailures), - + % assert docs in target db - {ok, 200, _, ADBody} = test_request:get(Url ++ "/db/_all_docs?include_docs=true", - ?ADMIN_REQ_HEADERS), + {ok, 200, _, ADBody} = test_request:get( + Url ++ "/db/_all_docs?include_docs=true", + ?ADMIN_REQ_HEADERS + ), {Json} = jiffy:decode(ADBody), ?assertEqual(2, proplists:get_value(<<"total_rows">>, Json)) end). @@ -912,33 +1287,58 @@ should_allow_user_to_replicate_from_no_access_to_access(_PortType, Url) -> should_allow_user_to_replicate_from_no_access_to_no_access(_PortType, Url) -> ?_test(begin % create source and target dbs - {ok, 201, _, _} = test_request:put(url() ++ "/db2?q=1&n=1", - ?ADMIN_REQ_HEADERS, ""), + {ok, 201, _, _} = test_request:put( + url() ++ "/db2?q=1&n=1", + ?ADMIN_REQ_HEADERS, + "" + ), % set target db security - {ok, _, _, _} = test_request:put(url() ++ "/db2/_security", - ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), - - {ok, 201, _, _} = test_request:put(url() ++ "/db3?q=1&n=1", - ?ADMIN_REQ_HEADERS, ""), + {ok, _, _, _} = test_request:put( + url() ++ "/db2/_security", + ?ADMIN_REQ_HEADERS, + jiffy:encode(?SECURITY_OBJECT) + ), + + {ok, 201, _, _} = test_request:put( + url() ++ "/db3?q=1&n=1", + ?ADMIN_REQ_HEADERS, + "" + ), % set target db security - {ok, _, _, _} = test_request:put(url() ++ "/db3/_security", - ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), + {ok, _, _, _} = test_request:put( + url() ++ "/db3/_security", + ?ADMIN_REQ_HEADERS, + jiffy:encode(?SECURITY_OBJECT) + ), % create source docs - {ok, _, _, _} = test_request:put(Url ++ "/db2/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db2/b", - ?ADMIN_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db2/c", - ?ADMIN_REQ_HEADERS, "{\"c\":3,\"_access\":[\"y\"]}"), + {ok, _, _, _} = test_request:put( + Url ++ "/db2/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db2/b", + ?ADMIN_REQ_HEADERS, + "{\"b\":2,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db2/c", + ?ADMIN_REQ_HEADERS, + "{\"c\":3,\"_access\":[\"y\"]}" + ), % replicate UserXUrl = string:replace(Url, "http://", "http://x:x@"), - EJRequestBody = {[ - {<<"source">>, list_to_binary(UserXUrl ++ "/db2")}, - {<<"target">>, list_to_binary(UserXUrl ++ "/db3")} - ]}, - {ok, ResponseCode, _, ResponseBody} = test_request:post(Url ++ "/_replicate", - ?USERX_REQ_HEADERS, jiffy:encode(EJRequestBody)), + EJRequestBody = + {[ + {<<"source">>, list_to_binary(UserXUrl ++ "/db2")}, + {<<"target">>, list_to_binary(UserXUrl ++ "/db3")} + ]}, + {ok, ResponseCode, _, ResponseBody} = test_request:post( + Url ++ "/_replicate", + ?USERX_REQ_HEADERS, + jiffy:encode(EJRequestBody) + ), % assert replication status {EJResponseBody} = jiffy:decode(ResponseBody), @@ -951,72 +1351,97 @@ should_allow_user_to_replicate_from_no_access_to_no_access(_PortType, Url) -> DocsReard = couch_util:get_value(<<"docs_read">>, History), DocsWritten = couch_util:get_value(<<"docs_written">>, History), DocWriteFailures = couch_util:get_value(<<"doc_write_failures">>, History), - + ?assertEqual(3, MissingChecked), ?assertEqual(3, MissingFound), ?assertEqual(3, DocsReard), ?assertEqual(3, DocsWritten), ?assertEqual(0, DocWriteFailures), - + % assert docs in target db - {ok, 200, _, ADBody} = test_request:get(Url ++ "/db3/_all_docs?include_docs=true", - ?ADMIN_REQ_HEADERS), + {ok, 200, _, ADBody} = test_request:get( + Url ++ "/db3/_all_docs?include_docs=true", + ?ADMIN_REQ_HEADERS + ), {Json} = jiffy:decode(ADBody), ?assertEqual(3, proplists:get_value(<<"total_rows">>, Json)) end). % revs_diff should_not_allow_user_to_revs_diff_other_docs(_PortType, Url) -> - ?_test(begin - % create test docs - {ok, _, _, _} = test_request:put(Url ++ "/db/a", - ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), - {ok, _, _, _} = test_request:put(Url ++ "/db/b", - ?ADMIN_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), - {ok, _, _, V} = test_request:put(Url ++ "/db/c", - ?ADMIN_REQ_HEADERS, "{\"c\":3,\"_access\":[\"y\"]}"), - - % nothing missing - RevsDiff = {[ - {<<"a">>, [ - <<"1-23202479633c2b380f79507a776743d5">> - ]} - ]}, - {ok, GoodCode, _, GoodBody} = test_request:post(Url ++ "/db/_revs_diff", - ?USERX_REQ_HEADERS, jiffy:encode(RevsDiff)), - EJGoodBody = jiffy:decode(GoodBody), - ?assertEqual(200, GoodCode), - ?assertEqual({[]}, EJGoodBody), - - % something missing - MissingRevsDiff = {[ - {<<"a">>, [ - <<"1-missing">> - ]} - ]}, - {ok, MissingCode, _, MissingBody} = test_request:post(Url ++ "/db/_revs_diff", - ?USERX_REQ_HEADERS, jiffy:encode(MissingRevsDiff)), - EJMissingBody = jiffy:decode(MissingBody), - ?assertEqual(200, MissingCode), - MissingExpect = {[ - {<<"a">>, {[ - {<<"missing">>, [<<"1-missing">>]} - ]}} - ]}, - ?assertEqual(MissingExpect, EJMissingBody), - - % other doc - OtherRevsDiff = {[ - {<<"c">>, [ - <<"1-92aef5b0e4a3f4db0aba1320869bc95d">> - ]} - ]}, - {ok, OtherCode, _, OtherBody} = test_request:post(Url ++ "/db/_revs_diff", - ?USERX_REQ_HEADERS, jiffy:encode(OtherRevsDiff)), - EJOtherBody = jiffy:decode(OtherBody), - ?assertEqual(200, OtherCode), - ?assertEqual({[]}, EJOtherBody) - end). + ?_test(begin + % create test docs + {ok, _, _, _} = test_request:put( + Url ++ "/db/a", + ?ADMIN_REQ_HEADERS, + "{\"a\":1,\"_access\":[\"x\"]}" + ), + {ok, _, _, _} = test_request:put( + Url ++ "/db/b", + ?ADMIN_REQ_HEADERS, + "{\"b\":2,\"_access\":[\"x\"]}" + ), + {ok, _, _, V} = test_request:put( + Url ++ "/db/c", + ?ADMIN_REQ_HEADERS, + "{\"c\":3,\"_access\":[\"y\"]}" + ), + + % nothing missing + RevsDiff = + {[ + {<<"a">>, [ + <<"1-23202479633c2b380f79507a776743d5">> + ]} + ]}, + {ok, GoodCode, _, GoodBody} = test_request:post( + Url ++ "/db/_revs_diff", + ?USERX_REQ_HEADERS, + jiffy:encode(RevsDiff) + ), + EJGoodBody = jiffy:decode(GoodBody), + ?assertEqual(200, GoodCode), + ?assertEqual({[]}, EJGoodBody), + + % something missing + MissingRevsDiff = + {[ + {<<"a">>, [ + <<"1-missing">> + ]} + ]}, + {ok, MissingCode, _, MissingBody} = test_request:post( + Url ++ "/db/_revs_diff", + ?USERX_REQ_HEADERS, + jiffy:encode(MissingRevsDiff) + ), + EJMissingBody = jiffy:decode(MissingBody), + ?assertEqual(200, MissingCode), + MissingExpect = + {[ + {<<"a">>, + {[ + {<<"missing">>, [<<"1-missing">>]} + ]}} + ]}, + ?assertEqual(MissingExpect, EJMissingBody), + + % other doc + OtherRevsDiff = + {[ + {<<"c">>, [ + <<"1-92aef5b0e4a3f4db0aba1320869bc95d">> + ]} + ]}, + {ok, OtherCode, _, OtherBody} = test_request:post( + Url ++ "/db/_revs_diff", + ?USERX_REQ_HEADERS, + jiffy:encode(OtherRevsDiff) + ), + EJOtherBody = jiffy:decode(OtherBody), + ?assertEqual(200, OtherCode), + ?assertEqual({[]}, EJOtherBody) + end). %% ------------------------------------------------------------------ %% Internal Function Definitions %% ------------------------------------------------------------------ diff --git a/src/couch/test/eunit/couchdb_update_conflicts_tests.erl b/src/couch/test/eunit/couchdb_update_conflicts_tests.erl index 953ddd7033b..d75b335b149 100644 --- a/src/couch/test/eunit/couchdb_update_conflicts_tests.erl +++ b/src/couch/test/eunit/couchdb_update_conflicts_tests.erl @@ -18,7 +18,8 @@ -define(i2l(I), integer_to_list(I)). -define(DOC_ID, <<"foobar">>). -define(LOCAL_DOC_ID, <<"_local/foobar">>). --define(NUM_CLIENTS, [100, 500 ]). % TODO: enable 1000, 2000, 5000, 10000]). +% TODO: enable 1000, 2000, 5000, 10000]). +-define(NUM_CLIENTS, [100, 500]). -define(TIMEOUT, 200000). start() -> diff --git a/src/couch_index/src/couch_index_updater.erl b/src/couch_index/src/couch_index_updater.erl index 66d76062200..ab84b095555 100644 --- a/src/couch_index/src/couch_index_updater.erl +++ b/src/couch_index/src/couch_index_updater.erl @@ -124,7 +124,7 @@ update(Idx, Mod, IdxState) -> DocOpts = case lists:member(local_seq, UpdateOpts) of true -> [conflicts, deleted_conflicts, local_seq, deleted]; - _ -> [conflicts, deleted_conflicts,local_seq, deleted] + _ -> [conflicts, deleted_conflicts, local_seq, deleted] end, couch_util:with_db(DbName, fun(Db) -> @@ -142,9 +142,9 @@ update(Idx, Mod, IdxState) -> end, GetInfo = fun - (#full_doc_info{id=Id, update_seq=Seq, deleted=Del,access=Access}=FDI) -> + (#full_doc_info{id = Id, update_seq = Seq, deleted = Del, access = Access} = FDI) -> {Id, Seq, Del, couch_doc:to_doc_info(FDI), Access}; - (#doc_info{id=Id, high_seq=Seq, revs=[RI|_],access=Access}=DI) -> + (#doc_info{id = Id, high_seq = Seq, revs = [RI | _], access = Access} = DI) -> {Id, Seq, RI#rev_info.deleted, DI, Access} end, @@ -155,19 +155,20 @@ update(Idx, Mod, IdxState) -> {false, <<"_design/", _/binary>>} -> {nil, Seq}; _ -> - case IndexName of % TODO: move into outer case statement + % TODO: move into outer case statement + case IndexName of <<"_design/_access">> -> {ok, Doc} = couch_db:open_doc_int(Db, DocInfo, DocOpts), % TODO: hande conflicted docs in _access index % probably remove - [RevInfo|_] = DocInfo#doc_info.revs, + [RevInfo | _] = DocInfo#doc_info.revs, Doc1 = Doc#doc{ meta = [{body_sp, RevInfo#rev_info.body_sp}], access = Access }, {Doc1, Seq}; _ when Deleted -> - {#doc{id=DocId, deleted=true}, Seq}; + {#doc{id = DocId, deleted = true}, Seq}; _ -> {ok, Doc} = couch_db:open_doc_int(Db, DocInfo, DocOpts), {Doc, Seq} diff --git a/src/couch_index/src/couch_index_util.erl b/src/couch_index/src/couch_index_util.erl index 47133db0f14..beb0f556910 100644 --- a/src/couch_index/src/couch_index_util.erl +++ b/src/couch_index/src/couch_index_util.erl @@ -31,7 +31,7 @@ index_file(Module, DbName, FileName) -> load_doc(Db, #doc_info{} = DI, Opts) -> Deleted = lists:member(deleted, Opts), - % MyDoc = , + % MyDoc = , %{ok, MyDoc2} = MyDoc, %couch_log:error("~ncouch_index_util:load_doc(): Doc: ~p, Deleted ~p~n", [MyDoc2, MyDoc2#doc.deleted]), case catch (couch_db:open_doc(Db, DI, Opts)) of diff --git a/src/couch_mrview/src/couch_mrview.erl b/src/couch_mrview/src/couch_mrview.erl index 79b2b8bec90..2cb74fac4ca 100644 --- a/src/couch_mrview/src/couch_mrview.erl +++ b/src/couch_mrview/src/couch_mrview.erl @@ -266,22 +266,27 @@ query_all_docs(Db, Args0, Callback, Acc) -> access_ddoc() -> #doc{ id = <<"_design/_access">>, - body = {[ - {<<"language">>,<<"_access">>}, - {<<"options">>, {[ - {<<"include_design">>, true} - ]}}, - {<<"views">>, {[ - {<<"_access_by_id">>, {[ - {<<"map">>, <<"_access/by-id-map">>}, - {<<"reduce">>, <<"_count">>} - ]}}, - {<<"_access_by_seq">>, {[ - {<<"map">>, <<"_access/by-seq-map">>}, - {<<"reduce">>, <<"_count">>} - ]}} - ]}} - ]} + body = + {[ + {<<"language">>, <<"_access">>}, + {<<"options">>, + {[ + {<<"include_design">>, true} + ]}}, + {<<"views">>, + {[ + {<<"_access_by_id">>, + {[ + {<<"map">>, <<"_access/by-id-map">>}, + {<<"reduce">>, <<"_count">>} + ]}}, + {<<"_access_by_seq">>, + {[ + {<<"map">>, <<"_access/by-seq-map">>}, + {<<"reduce">>, <<"_count">>} + ]}} + ]}} + ]} }. query_changes_access(Db, StartSeq, Fun, Options, Acc) -> DDoc = access_ddoc(), @@ -289,15 +294,16 @@ query_changes_access(Db, StartSeq, Fun, Options, Acc) -> UserName = UserCtx#user_ctx.name, %% % TODO: add roles Args1 = prefix_startkey_endkey(UserName, #mrargs{}, fwd), - Args2 = Args1#mrargs{deleted=true}, - Args = Args2#mrargs{reduce=false}, + Args2 = Args1#mrargs{deleted = true}, + Args = Args2#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) Callback = fun - ({meta, _}, Acc0) -> - {ok, Acc0}; % ignore for now - ({row, Props}, Acc0) -> + ({meta, _}, Acc0) -> + % ignore for now + {ok, Acc0}; + ({row, Props}, Acc0) -> % turn row into FDI Value = couch_util:get_value(value, Props), [Owner, Seq] = couch_util:get_value(key, Props), @@ -307,7 +313,16 @@ query_changes_access(Db, StartSeq, Fun, Options, Acc) -> [Pos, RevId] = string:split(?b2l(Rev), "-"), FDI = #full_doc_info{ id = proplists:get_value(id, Props), - rev_tree = [{list_to_integer(Pos), {?l2b(RevId), #leaf{deleted=Deleted, ptr=BodySp, seq=Seq, sizes=#size_info{}}, []}}], + rev_tree = [ + { + list_to_integer(Pos), + { + ?l2b(RevId), + #leaf{deleted = Deleted, ptr = BodySp, seq = Seq, sizes = #size_info{}}, + [] + } + } + ], deleted = Deleted, update_seq = 0, sizes = #size_info{}, @@ -315,8 +330,9 @@ query_changes_access(Db, StartSeq, Fun, Options, Acc) -> }, Fun(FDI, Acc0); (_Else, Acc0) -> - {ok, Acc0} % ignore for now - end, + % ignore for now + {ok, Acc0} + end, VName = <<"_access_by_seq">>, query_view(Db, DDoc, VName, Args, Callback, Acc). @@ -327,7 +343,7 @@ query_all_docs_access(Db, Args0, Callback0, Acc) -> UserCtx = couch_db:get_user_ctx(Db), UserName = UserCtx#user_ctx.name, Args1 = prefix_startkey_endkey(UserName, Args0, Args0#mrargs.direction), - Args = Args1#mrargs{reduce=false, extra=Args1#mrargs.extra ++ [{all_docs_access, true}]}, + Args = Args1#mrargs{reduce = false, extra = Args1#mrargs.extra ++ [{all_docs_access, true}]}, Callback = fun ({row, Props}, Acc0) -> % filter out the user-prefix from the key, so _all_docs looks normal @@ -339,34 +355,37 @@ query_all_docs_access(Db, Args0, Callback0, Acc) -> Callback0({row, Row}, Acc0); (Row, Acc0) -> Callback0(Row, Acc0) - end, + end, VName = <<"_access_by_id">>, query_view(Db, DDoc, VName, Args, Callback, Acc). prefix_startkey_endkey(UserName, Args, fwd) -> - #mrargs{start_key=StartKey, end_key=EndKey} = Args, - Args#mrargs { - start_key = case StartKey of - undefined -> [UserName]; - StartKey -> [UserName, StartKey] - end, - end_key = case EndKey of - undefined -> [UserName, {}]; - EndKey -> [UserName, EndKey, {}] - end + #mrargs{start_key = StartKey, end_key = EndKey} = Args, + Args#mrargs{ + start_key = + case StartKey of + undefined -> [UserName]; + StartKey -> [UserName, StartKey] + end, + end_key = + case EndKey of + undefined -> [UserName, {}]; + EndKey -> [UserName, EndKey, {}] + end }; - prefix_startkey_endkey(UserName, Args, rev) -> - #mrargs{start_key=StartKey, end_key=EndKey} = Args, - Args#mrargs { - end_key = case StartKey of - undefined -> [UserName]; - StartKey -> [UserName, StartKey] - end, - start_key = case EndKey of - undefined -> [UserName, {}]; - EndKey -> [UserName, EndKey, {}] - end + #mrargs{start_key = StartKey, end_key = EndKey} = Args, + Args#mrargs{ + end_key = + case StartKey of + undefined -> [UserName]; + StartKey -> [UserName, StartKey] + end, + start_key = + case EndKey of + undefined -> [UserName, {}]; + EndKey -> [UserName, EndKey, {}] + end }. query_all_docs_admin(Db, Args0, Callback, Acc) -> Sig = couch_util:with_db(Db, fun(WDb) -> diff --git a/src/couch_mrview/src/couch_mrview_updater.erl b/src/couch_mrview/src/couch_mrview_updater.erl index 5d58ab05d74..83d21c0364e 100644 --- a/src/couch_mrview/src/couch_mrview_updater.erl +++ b/src/couch_mrview/src/couch_mrview_updater.erl @@ -176,36 +176,38 @@ map_docs(Parent, #mrst{db_name = DbName, idx_name = IdxName} = State0) -> DocFun = fun ({nil, Seq, _}, {SeqAcc, Results}) -> {erlang:max(Seq, SeqAcc), Results}; - ({Id, Seq, Rev, #doc{deleted=true, body=Body, meta=Meta}}, {SeqAcc, Results}) -> - % _access needs deleted docs - case IdxName of - <<"_design/_access">> -> - % splice in seq - {Start, Rev1} = Rev, - Doc = #doc{ - id = Id, - revs = {Start, [Rev1]}, - body = {make_deleted_body(Body, Meta, Seq)}, %% todo: only keep _access and add _seq - deleted = true - }, - {ok, Res} = couch_query_servers:map_doc_raw(QServer, Doc), - {erlang:max(Seq, SeqAcc), [{Id, Seq, Rev, Res} | Results]}; - _Else -> - {erlang:max(Seq, SeqAcc), [{Id, Seq, Rev, []} | Results]} - end; - ({Id, Seq, Doc}, {SeqAcc, Results}) -> - couch_stats:increment_counter([couchdb, mrview, map_doc]), - % IdxName: ~p, Doc: ~p~n~n", [IdxName, Doc]), - Doc0 = case IdxName of + ({Id, Seq, Rev, #doc{deleted = true, body = Body, meta = Meta}}, {SeqAcc, Results}) -> + % _access needs deleted docs + case IdxName of <<"_design/_access">> -> % splice in seq - {Props} = Doc#doc.body, - BodySp = couch_util:get_value(body_sp, Doc#doc.meta), - Doc#doc{ - body = {Props++[{<<"_seq">>, Seq}, {<<"_body_sp">>, BodySp}]} - }; + {Start, Rev1} = Rev, + Doc = #doc{ + id = Id, + revs = {Start, [Rev1]}, + %% todo: only keep _access and add _seq + body = {make_deleted_body(Body, Meta, Seq)}, + deleted = true + }, + {ok, Res} = couch_query_servers:map_doc_raw(QServer, Doc), + {erlang:max(Seq, SeqAcc), [{Id, Seq, Rev, Res} | Results]}; _Else -> - Doc + {erlang:max(Seq, SeqAcc), [{Id, Seq, Rev, []} | Results]} + end; + ({Id, Seq, Doc}, {SeqAcc, Results}) -> + couch_stats:increment_counter([couchdb, mrview, map_doc]), + % IdxName: ~p, Doc: ~p~n~n", [IdxName, Doc]), + Doc0 = + case IdxName of + <<"_design/_access">> -> + % splice in seq + {Props} = Doc#doc.body, + BodySp = couch_util:get_value(body_sp, Doc#doc.meta), + Doc#doc{ + body = {Props ++ [{<<"_seq">>, Seq}, {<<"_body_sp">>, BodySp}]} + }; + _Else -> + Doc end, {ok, Res} = couch_query_servers:map_doc_raw(QServer, Doc0), {erlang:max(Seq, SeqAcc), [{Id, Res} | Results]} diff --git a/src/couch_mrview/src/couch_mrview_util.erl b/src/couch_mrview/src/couch_mrview_util.erl index cb90199a28c..d0c3087b239 100644 --- a/src/couch_mrview/src/couch_mrview_util.erl +++ b/src/couch_mrview/src/couch_mrview_util.erl @@ -385,7 +385,7 @@ reduce_to_count(Reductions) -> FinalReduction = couch_btree:final_reduce(CountReduceFun, Reductions), get_count(FinalReduction). -get_access_row_count(#mrview{btree=Bt}, UserName) -> +get_access_row_count(#mrview{btree = Bt}, UserName) -> couch_btree:full_reduce_with_options(Bt, [ {start_key, UserName} ]). diff --git a/src/couch_replicator/src/couch_replicator.erl b/src/couch_replicator/src/couch_replicator.erl index b1b67fa7e4a..eb64a2afd76 100644 --- a/src/couch_replicator/src/couch_replicator.erl +++ b/src/couch_replicator/src/couch_replicator.erl @@ -79,13 +79,16 @@ replicate(PostBody, Ctx) -> false -> check_authorization(RepId, UserCtx), {ok, Listener} = rep_result_listener(RepId), - Result = case do_replication_loop(Rep) of % TODO: review why we need this - {ok, {ResultJson}} -> - {PublicRepId, _} = couch_replicator_ids:replication_id(Rep), % TODO: check with options - {ok, {[{<<"replication_id">>, ?l2b(PublicRepId)} | ResultJson]}}; - Else -> - Else - end, + % TODO: review why we need this + Result = + case do_replication_loop(Rep) of + {ok, {ResultJson}} -> + % TODO: check with options + {PublicRepId, _} = couch_replicator_ids:replication_id(Rep), + {ok, {[{<<"replication_id">>, ?l2b(PublicRepId)} | ResultJson]}}; + Else -> + Else + end, couch_replicator_notifier:stop(Listener), Result end. diff --git a/src/couch_replicator/src/couch_replicator_scheduler_job.erl b/src/couch_replicator/src/couch_replicator_scheduler_job.erl index c1f7cdce468..70e713901bb 100644 --- a/src/couch_replicator/src/couch_replicator_scheduler_job.erl +++ b/src/couch_replicator/src/couch_replicator_scheduler_job.erl @@ -830,9 +830,11 @@ do_checkpoint(State) -> try {SrcRevPos, SrcRevId} = update_checkpoint( - Source, SourceLog#doc{body = NewRepHistory}, SrcAccess, UserCtx, source), + Source, SourceLog#doc{body = NewRepHistory}, SrcAccess, UserCtx, source + ), {TgtRevPos, TgtRevId} = update_checkpoint( - Target, TargetLog#doc{body = NewRepHistory}, TgtAccess, UserCtx, target), + Target, TargetLog#doc{body = NewRepHistory}, TgtAccess, UserCtx, target + ), NewState = State#rep_state{ checkpoint_history = NewRepHistory, committed_seq = NewTsSeq, @@ -878,10 +880,11 @@ update_checkpoint(Db, Doc, Access, UserCtx, DbType) -> update_checkpoint(Db, #doc{id = LogId} = Doc0, Access, UserCtx) -> % if db has _access, then: % get userCtx from replication and splice into doc _access - Doc = case Access of - true -> Doc0#doc{access = [UserCtx#user_ctx.name]}; - _False -> Doc0 - end, + Doc = + case Access of + true -> Doc0#doc{access = [UserCtx#user_ctx.name]}; + _False -> Doc0 + end, try case couch_replicator_api_wrap:update_doc(Db, Doc, [delay_commit]) of diff --git a/src/fabric/src/fabric_doc_update.erl b/src/fabric/src/fabric_doc_update.erl index 8a89685da15..f08fd2a1bbd 100644 --- a/src/fabric/src/fabric_doc_update.erl +++ b/src/fabric/src/fabric_doc_update.erl @@ -413,7 +413,7 @@ doc_update1() -> ?assertEqual( % TODO: find out why we had to swap this - {error, [{Doc2,{error,internal_server_error}},{Doc1,{accepted,"A"}}]}, + {error, [{Doc2, {error, internal_server_error}}, {Doc1, {accepted, "A"}}]}, ReplyW5 ). @@ -444,7 +444,7 @@ doc_update2() -> handle_message({rexi_EXIT, 1}, lists:nth(3, Shards), Acc2), ?assertEqual( - {accepted, [{Doc2,{accepted,Doc2}}, {Doc1,{accepted,Doc1}}]}, + {accepted, [{Doc2, {accepted, Doc2}}, {Doc1, {accepted, Doc1}}]}, Reply ). @@ -473,7 +473,7 @@ doc_update3() -> {stop, Reply} = handle_message({ok, [{ok, Doc1}, {ok, Doc2}]}, lists:nth(3, Shards), Acc2), - ?assertEqual({ok, [{Doc2, {ok,Doc2}},{Doc1, {ok, Doc1}}]},Reply). + ?assertEqual({ok, [{Doc2, {ok, Doc2}}, {Doc1, {ok, Doc1}}]}, Reply). handle_all_dbs_active() -> Doc1 = #doc{revs = {1, [<<"foo">>]}},