| % Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| % use this file except in compliance with the License. You may obtain a copy of |
| % the License at |
| % |
| % http://www.apache.org/licenses/LICENSE-2.0 |
| % |
| % Unless required by applicable law or agreed to in writing, software |
| % distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| % WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| % License for the specific language governing permissions and limitations under |
| % the License. |
| |
| -module(couchdb_access_tests). |
| |
| -include_lib("couch/include/couch_eunit.hrl"). |
| |
| -define(CONTENT_JSON, {"Content-Type", "application/json"}). |
| -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">>]}]}} |
| ]}). |
| |
| url() -> |
| Addr = config:get("httpd", "bind_address", "127.0.0.1"), |
| lists:concat(["http://", Addr, ":", port()]). |
| |
| before_each(_) -> |
| {ok, 201, _, _} = test_request:put(url() ++ "/db?q=1&n=1&access=true", ?ADMIN_REQ_HEADERS, ""), |
| {ok, _, _, _} = test_request:put(url() ++ "/db/_security", ?ADMIN_REQ_HEADERS, jiffy:encode(?SECURITY_OBJECT)), |
| url(). |
| |
| after_each(_, Url) -> |
| {ok, 200, _, _} = test_request:delete(Url ++ "/db", ?ADMIN_REQ_HEADERS), |
| {_, _, _, _} = test_request:delete(Url ++ "/db2", ?ADMIN_REQ_HEADERS), |
| {_, _, _, _} = test_request:delete(Url ++ "/db3", ?ADMIN_REQ_HEADERS), |
| ok. |
| |
| 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), |
| |
| % cleanup and setup |
| {ok, _, _, _} = test_request:delete(url() ++ "/db", ?ADMIN_REQ_HEADERS), |
| % {ok, _, _, _} = test_request:put(url() ++ "/db?q=1&n=1&access=true", ?ADMIN_REQ_HEADERS, ""), |
| |
| % create users |
| UserDbUrl = url() ++ "/_users?q=1&n=1", |
| {ok, _, _, _} = test_request:delete(UserDbUrl, ?ADMIN_REQ_HEADERS, ""), |
| {ok, 201, _, _} = test_request:put(UserDbUrl, ?ADMIN_REQ_HEADERS, ""), |
| |
| UserXDocUrl = url() ++ "/_users/org.couchdb.user:x", |
| UserXDocBody = "{ \"name\":\"x\", \"roles\": [], \"password\":\"x\", \"type\": \"user\" }", |
| {ok, 201, _, _} = test_request:put(UserXDocUrl, ?ADMIN_REQ_HEADERS, UserXDocBody), |
| |
| UserYDocUrl = url() ++ "/_users/org.couchdb.user:y", |
| UserYDocBody = "{ \"name\":\"y\", \"roles\": [], \"password\":\"y\", \"type\": \"user\" }", |
| {ok, 201, _, _} = test_request:put(UserYDocUrl, ?ADMIN_REQ_HEADERS, UserYDocBody), |
| Couch. |
| |
| after_all(_) -> |
| ok = test_util:stop_couch(done). |
| |
| access_test_() -> |
| Tests = [ |
| % Doc creation |
| fun should_not_let_anonymous_user_create_doc/2, |
| fun should_let_admin_create_doc_with_access/2, |
| fun should_let_admin_create_doc_without_access/2, |
| fun should_let_user_create_doc_for_themselves/2, |
| fun should_not_let_user_create_doc_for_someone_else/2, |
| fun should_let_user_create_access_ddoc/2, |
| fun access_ddoc_should_have_no_effects/2, |
| |
| % Doc updates |
| fun users_with_access_can_update_doc/2, |
| fun users_with_access_can_not_change_access/2, |
| fun users_with_access_can_not_remove_access/2, |
| |
| % Doc reads |
| fun should_let_admin_read_doc_with_access/2, |
| fun user_with_access_can_read_doc/2, |
| fun user_without_access_can_not_read_doc/2, |
| fun user_can_not_read_doc_without_access/2, |
| fun admin_with_access_can_read_conflicted_doc/2, |
| fun user_with_access_can_not_read_conflicted_doc/2, |
| |
| % Doc deletes |
| fun should_let_admin_delete_doc_with_access/2, |
| fun should_let_user_delete_doc_for_themselves/2, |
| fun should_not_let_user_delete_doc_for_someone_else/2, |
| |
| % _all_docs with include_docs |
| fun should_let_admin_fetch_all_docs/2, |
| fun should_let_user_fetch_their_own_all_docs/2, |
| % % potential future feature |
| % % fun should_let_user_fetch_their_own_all_docs_plus_users_ddocs/2%, |
| |
| % _changes |
| fun should_let_admin_fetch_changes/2, |
| fun should_let_user_fetch_their_own_changes/2, |
| |
| % views |
| fun should_not_allow_admin_access_ddoc_view_request/2, |
| fun should_not_allow_user_access_ddoc_view_request/2, |
| fun should_allow_admin_users_access_ddoc_view_request/2, |
| fun should_allow_user_users_access_ddoc_view_request/2, |
| |
| % replication |
| fun should_allow_admin_to_replicate_from_access_to_access/2, |
| fun should_allow_admin_to_replicate_from_no_access_to_access/2, |
| fun should_allow_admin_to_replicate_from_access_to_no_access/2, |
| fun should_allow_admin_to_replicate_from_no_access_to_no_access/2, |
| |
| fun should_allow_user_to_replicate_from_access_to_access/2, |
| fun should_allow_user_to_replicate_from_access_to_no_access/2, |
| fun should_allow_user_to_replicate_from_no_access_to_access/2, |
| fun should_allow_user_to_replicate_from_no_access_to_no_access/2, |
| |
| % TODO: try getting _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 |
| ], |
| { |
| "Access tests", |
| { |
| setup, |
| fun before_all/0, fun after_all/1, |
| [ |
| make_test_cases(clustered, Tests) |
| ] |
| } |
| }. |
| |
| make_test_cases(Mod, Funs) -> |
| { |
| lists:flatten(io_lib:format("~s", [Mod])), |
| {foreachx, fun before_each/1, fun after_each/2, [{Mod, Fun} || Fun <- Funs]} |
| }. |
| |
| % Doc creation |
| % 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 |
| % {ok, _, _, _} = test_request:put(Url ++ "/db/a", |
| % ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), |
| % {ok, Code, _, _} = test_request:get(Url ++ "/db/a?revs=true&open_revs=%5B%221-23202479633c2b380f79507a776743d5%22%5D&latest=true", |
| % ?USERX_REQ_HEADERS), |
| % ?assertEqual(200, Code) |
| % end). |
| % |
| |
| should_not_let_anonymous_user_create_doc(_PortType, Url) -> |
| {ok, Code, _, _} = test_request:put(Url ++ "/db/a", "{\"a\":1,\"_access\":[\"x\"]}"), |
| ?_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\"]}"), |
| ?_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}"), |
| ?_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\"]}"), |
| ?_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\"]}"), |
| ?_assertEqual(403, Code). |
| |
| should_let_user_create_access_ddoc(_PortType, Url) -> |
| {ok, Code, _, _} = test_request:put(Url ++ "/db/_design/dx", |
| ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), |
| ?_assertEqual(201, Code). |
| |
| access_ddoc_should_have_no_effects(_PortType, Url) -> |
| ?_test(begin |
| Ddoc = "{ \"_access\":[\"x\"], \"validate_doc_update\": \"function(newDoc, oldDoc, userCtx) { throw({unauthorized: 'throw error'})}\", \"views\": { \"foo\": { \"map\": \"function(doc) { emit(doc._id) }\" } }, \"shows\": { \"boo\": \"function() {}\" }, \"lists\": { \"hoo\": \"function() {}\" }, \"update\": { \"goo\": \"function() {}\" }, \"filters\": { \"loo\": \"function() {}\" } }", |
| {ok, Code, _, _} = test_request:put(Url ++ "/db/_design/dx", |
| ?USERX_REQ_HEADERS, Ddoc), |
| ?assertEqual(201, Code), |
| {ok, Code1, _, _} = test_request:put(Url ++ "/db/b", |
| ?USERX_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), |
| ?assertEqual(201, Code1), |
| {ok, Code2, _, _} = test_request:get(Url ++ "/db/_design/dx/_view/foo", |
| ?USERX_REQ_HEADERS), |
| ?assertEqual(403, Code2), |
| {ok, Code3, _, _} = test_request:get(Url ++ "/db/_design/dx/_show/boo/b", |
| ?USERX_REQ_HEADERS), |
| ?assertEqual(403, Code3), |
| {ok, Code4, _, _} = test_request:get(Url ++ "/db/_design/dx/_list/hoo/foo", |
| ?USERX_REQ_HEADERS), |
| ?assertEqual(403, Code4), |
| {ok, Code5, _, _} = test_request:post(Url ++ "/db/_design/dx/_update/goo", |
| ?USERX_REQ_HEADERS, ""), |
| ?assertEqual(403, Code5), |
| {ok, Code6, _, _} = test_request:get(Url ++ "/db/_changes?filter=dx/loo", |
| ?USERX_REQ_HEADERS), |
| ?assertEqual(403, Code6), |
| {ok, Code7, _, _} = test_request:get(Url ++ "/db/_changes?filter=_view&view=dx/foo", |
| ?USERX_REQ_HEADERS), |
| ?assertEqual(403, Code7) |
| end). |
| |
| % Doc updates |
| |
| users_with_access_can_update_doc(_PortType, Url) -> |
| {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", |
| ?USERX_REQ_HEADERS, |
| "{\"a\":2,\"_access\":[\"x\"],\"_rev\":\"" ++ binary_to_list(Rev) ++ "\"}"), |
| ?_assertEqual(201, 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\"]}"), |
| {Json} = jiffy:decode(Body), |
| Rev = couch_util:get_value(<<"rev">>, Json), |
| {ok, Code, _, _} = test_request:put(Url ++ "/db/b", |
| ?USERX_REQ_HEADERS, |
| "{\"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\"]}"), |
| {Json} = jiffy:decode(Body), |
| Rev = couch_util:get_value(<<"rev">>, Json), |
| {ok, Code, _, _} = test_request:put(Url ++ "/db/b", |
| ?USERX_REQ_HEADERS, |
| "{\"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), |
| ?_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), |
| ?_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), |
| ?_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), |
| ?_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), |
| ?_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), |
| ?_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), |
| ?_assertEqual(201, 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), |
| ?_assertEqual(201, 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), |
| ?_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), |
| {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), |
| {Json} = jiffy:decode(Body), |
| Rows = proplists:get_value(<<"rows">>, Json), |
| ?assertEqual([{[{<<"id">>,<<"a">>}, |
| {<<"key">>,<<"a">>}, |
| {<<"value">>,<<"1-23202479633c2b380f79507a776743d5">>}, |
| {<<"doc">>, |
| {[{<<"_id">>,<<"a">>}, |
| {<<"_rev">>,<<"1-23202479633c2b380f79507a776743d5">>}, |
| {<<"a">>,1}, |
| {<<"_access">>,[<<"x">>]}]}}]}, |
| {[{<<"id">>,<<"b">>}, |
| {<<"key">>,<<"b">>}, |
| {<<"value">>,<<"1-d33fb05384fa65a8081da2046595de0f">>}, |
| {<<"doc">>, |
| {[{<<"_id">>,<<"b">>}, |
| {<<"_rev">>,<<"1-d33fb05384fa65a8081da2046595de0f">>}, |
| {<<"b">>,2}, |
| {<<"_access">>,[<<"x">>]}]}}]}], Rows), |
| ?assertEqual(2, length(Rows)), |
| ?assertEqual(4, proplists:get_value(<<"total_rows">>, Json)), |
| |
| {ok, 200, _, Body1} = test_request:get(Url ++ "/db/_all_docs?include_docs=true", |
| ?USERY_REQ_HEADERS), |
| {Json1} = jiffy:decode(Body1), |
| ?assertEqual( [{<<"total_rows">>,4}, |
| {<<"offset">>,2}, |
| {<<"rows">>, |
| [{[{<<"id">>,<<"c">>}, |
| {<<"key">>,<<"c">>}, |
| {<<"value">>,<<"1-92aef5b0e4a3f4db0aba1320869bc95d">>}, |
| {<<"doc">>, |
| {[{<<"_id">>,<<"c">>}, |
| {<<"_rev">>,<<"1-92aef5b0e4a3f4db0aba1320869bc95d">>}, |
| {<<"c">>,3}, |
| {<<"_access">>,[<<"y">>]}]}}]}, |
| {[{<<"id">>,<<"d">>}, |
| {<<"key">>,<<"d">>}, |
| {<<"value">>,<<"1-ae984f6550038b1ed1565ac4b6cd8c5d">>}, |
| {<<"doc">>, |
| {[{<<"_id">>,<<"d">>}, |
| {<<"_rev">>,<<"1-ae984f6550038b1ed1565ac4b6cd8c5d">>}, |
| {<<"d">>,4}, |
| {<<"_access">>,[<<"y">>]}]}}]}]}], Json1) |
| end). |
| |
| |
| % _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), |
| {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), |
| {Json} = jiffy:decode(Body), |
| ?assertMatch([{<<"results">>, |
| [{[{<<"seq">>, |
| <<"2-", _/binary>>}, |
| {<<"id">>,<<"a">>}, |
| {<<"changes">>, |
| [{[{<<"rev">>,<<"1-23202479633c2b380f79507a776743d5">>}]}]}]}, |
| {[{<<"seq">>, |
| <<"3-", _/binary>>}, |
| {<<"id">>,<<"b">>}, |
| {<<"changes">>, |
| [{[{<<"rev">>,<<"1-d33fb05384fa65a8081da2046595de0f">>}]}]}]}]}, |
| {<<"last_seq">>, |
| <<"3-", _/binary>>}, |
| {<<"pending">>,2}], Json), |
| AmountOfDocs = length(proplists:get_value(<<"results">>, Json)), |
| ?assertEqual(2, AmountOfDocs) |
| end). |
| |
| % views |
| |
| should_not_allow_admin_access_ddoc_view_request(_PortType, Url) -> |
| DDoc = "{\"a\":1,\"_access\":[\"x\"],\"views\":{\"foo\":{\"map\":\"function() {}\"}}}", |
| {ok, Code, _, _} = test_request:put(Url ++ "/db/_design/a", |
| ?ADMIN_REQ_HEADERS, DDoc), |
| ?assertEqual(201, Code), |
| {ok, Code1, _, _} = test_request:get(Url ++ "/db/_design/a/_view/foo", |
| ?ADMIN_REQ_HEADERS), |
| ?_assertEqual(403, 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), |
| ?assertEqual(201, Code), |
| {ok, Code1, _, _} = test_request:get(Url ++ "/db/_design/a/_view/foo", |
| ?USERX_REQ_HEADERS), |
| ?_assertEqual(403, 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), |
| ?assertEqual(201, Code), |
| {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), |
| ?assertEqual(201, Code), |
| {ok, Code1, _, _} = test_request:get(Url ++ "/db/_design/a/_view/foo", |
| ?USERX_REQ_HEADERS), |
| ?_assertEqual(200, Code1). |
| |
| % replication |
| |
| 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, ""), |
| % set target db security |
| {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\"]}"), |
| |
| % 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)), |
| |
| % assert replication status |
| {EJResponseBody} = jiffy:decode(ResponseBody), |
| ?assertEqual(ResponseCode, 200), |
| ?assertEqual(true, couch_util:get_value(<<"ok">>, EJResponseBody)), |
| [{History}] = couch_util:get_value(<<"history">>, EJResponseBody), |
| |
| MissingChecked = couch_util:get_value(<<"missing_checked">>, History), |
| MissingFound = couch_util:get_value(<<"missing_found">>, History), |
| 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), |
| {Json} = jiffy:decode(ADBody), |
| ?assertEqual(3, proplists:get_value(<<"total_rows">>, Json)) |
| end). |
| |
| 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, ""), |
| % set target db security |
| {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\"]}"), |
| |
| % 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)), |
| |
| % assert replication status |
| {EJResponseBody} = jiffy:decode(ResponseBody), |
| ?assertEqual(ResponseCode, 200), |
| ?assertEqual(true, couch_util:get_value(<<"ok">>, EJResponseBody)), |
| [{History}] = couch_util:get_value(<<"history">>, EJResponseBody), |
| |
| MissingChecked = couch_util:get_value(<<"missing_checked">>, History), |
| MissingFound = couch_util:get_value(<<"missing_found">>, History), |
| 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), |
| {Json} = jiffy:decode(ADBody), |
| ?assertEqual(3, proplists:get_value(<<"total_rows">>, Json)) |
| end). |
| |
| 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, ""), |
| % set target db security |
| {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\"]}"), |
| |
| % 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)), |
| |
| % assert replication status |
| {EJResponseBody} = jiffy:decode(ResponseBody), |
| ?assertEqual(ResponseCode, 200), |
| ?assertEqual(true, couch_util:get_value(<<"ok">>, EJResponseBody)), |
| [{History}] = couch_util:get_value(<<"history">>, EJResponseBody), |
| |
| MissingChecked = couch_util:get_value(<<"missing_checked">>, History), |
| MissingFound = couch_util:get_value(<<"missing_found">>, History), |
| 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), |
| {Json} = jiffy:decode(ADBody), |
| ?assertEqual(3, proplists:get_value(<<"total_rows">>, Json)) |
| end). |
| |
| 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, ""), |
| % 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, ""), |
| % set target db security |
| {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\"]}"), |
| |
| % 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)), |
| |
| % assert replication status |
| {EJResponseBody} = jiffy:decode(ResponseBody), |
| ?assertEqual(ResponseCode, 200), |
| ?assertEqual(true, couch_util:get_value(<<"ok">>, EJResponseBody)), |
| [{History}] = couch_util:get_value(<<"history">>, EJResponseBody), |
| |
| MissingChecked = couch_util:get_value(<<"missing_checked">>, History), |
| MissingFound = couch_util:get_value(<<"missing_found">>, History), |
| 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), |
| {Json} = jiffy:decode(ADBody), |
| ?assertEqual(3, proplists:get_value(<<"total_rows">>, Json)) |
| end). |
| |
| 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, ""), |
| % set target db security |
| {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\"]}"), |
| |
| % 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)), |
| % ?debugFmt("~nResponseBody: ~p~n", [ResponseBody]), |
| |
| % assert replication status |
| {EJResponseBody} = jiffy:decode(ResponseBody), |
| ?assertEqual(ResponseCode, 200), |
| ?assertEqual(true, couch_util:get_value(<<"ok">>, EJResponseBody)), |
| |
| [{History}] = couch_util:get_value(<<"history">>, EJResponseBody), |
| |
| MissingChecked = couch_util:get_value(<<"missing_checked">>, History), |
| MissingFound = couch_util:get_value(<<"missing_found">>, History), |
| 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), |
| {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), |
| |
| % assert docs in target db |
| {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). |
| |
| 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, ""), |
| % set target db security |
| {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\"]}"), |
| |
| % 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)), |
| |
| % assert replication status |
| {EJResponseBody} = jiffy:decode(ResponseBody), |
| ?assertEqual(ResponseCode, 200), |
| ?assertEqual(true, couch_util:get_value(<<"ok">>, EJResponseBody)), |
| [{History}] = couch_util:get_value(<<"history">>, EJResponseBody), |
| |
| MissingChecked = couch_util:get_value(<<"missing_checked">>, History), |
| MissingFound = couch_util:get_value(<<"missing_found">>, History), |
| 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), |
| {Json} = jiffy:decode(ADBody), |
| ?assertEqual(2, proplists:get_value(<<"total_rows">>, Json)) |
| end). |
| |
| 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, ""), |
| % set target db security |
| {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\":[\"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)), |
| |
| % assert replication status |
| {EJResponseBody} = jiffy:decode(ResponseBody), |
| ?assertEqual(ResponseCode, 200), |
| ?assertEqual(true, couch_util:get_value(<<"ok">>, EJResponseBody)), |
| [{History}] = couch_util:get_value(<<"history">>, EJResponseBody), |
| |
| MissingChecked = couch_util:get_value(<<"missing_checked">>, History), |
| MissingFound = couch_util:get_value(<<"missing_found">>, History), |
| 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 ++ "/db/_all_docs?include_docs=true", |
| ?ADMIN_REQ_HEADERS), |
| {Json} = jiffy:decode(ADBody), |
| ?assertEqual(2, proplists:get_value(<<"total_rows">>, Json)) |
| end). |
| |
| 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, ""), |
| % 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, ""), |
| % set target db security |
| {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\"]}"), |
| |
| % 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)), |
| |
| % assert replication status |
| {EJResponseBody} = jiffy:decode(ResponseBody), |
| ?assertEqual(ResponseCode, 200), |
| ?assertEqual(true, couch_util:get_value(<<"ok">>, EJResponseBody)), |
| [{History}] = couch_util:get_value(<<"history">>, EJResponseBody), |
| |
| MissingChecked = couch_util:get_value(<<"missing_checked">>, History), |
| MissingFound = couch_util:get_value(<<"missing_found">>, History), |
| 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 ++ "/db3/_all_docs?include_docs=true", |
| ?ADMIN_REQ_HEADERS), |
| {Json} = jiffy:decode(ADBody), |
| ?assertEqual(2, 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). |
| %% ------------------------------------------------------------------ |
| %% Internal Function Definitions |
| %% ------------------------------------------------------------------ |
| |
| port() -> |
| integer_to_list(mochiweb_socket_server:get(chttpd, port)). |
| |
| % Potential future feature:% |
| % should_let_user_fetch_their_own_all_docs_plus_users_ddocs(_PortType, Url) -> |
| % {ok, 201, _, _} = test_request:put(Url ++ "/db/a", |
| % ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"x\"]}"), |
| % {ok, 201, _, _} = test_request:put(Url ++ "/db/_design/foo", |
| % ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"_users\"]}"), |
| % {ok, 201, _, _} = test_request:put(Url ++ "/db/_design/bar", |
| % ?ADMIN_REQ_HEADERS, "{\"a\":1,\"_access\":[\"houdini\"]}"), |
| % {ok, 201, _, _} = test_request:put(Url ++ "/db/b", |
| % ?USERX_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), |
| % |
| % % % TODO: add allowing non-admin users adding non-admin ddocs |
| % {ok, 201, _, _} = test_request:put(Url ++ "/db/_design/x", |
| % ?ADMIN_REQ_HEADERS, "{\"b\":2,\"_access\":[\"x\"]}"), |
| % |
| % {ok, 201, _, _} = test_request:put(Url ++ "/db/c", |
| % ?ADMIN_REQ_HEADERS, "{\"c\":3,\"_access\":[\"y\"]}"), |
| % {ok, 201, _, _} = test_request:put(Url ++ "/db/d", |
| % ?USERY_REQ_HEADERS, "{\"d\":4,\"_access\":[\"y\"]}"), |
| % {ok, 200, _, Body} = test_request:get(Url ++ "/db/_all_docs?include_docs=true", |
| % ?USERX_REQ_HEADERS), |
| % {Json} = jiffy:decode(Body), |
| % ?debugFmt("~nHSOIN: ~p~n", [Json]), |
| % ?_assertEqual(3, length(proplists:get_value(<<"rows">>, Json))). |