This is an automated email from the ASF dual-hosted git repository. jan pushed a commit to branch access in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 5ca0d46acb7aedc18472e98202fd54eba9a76df3 Author: Jan Lehnardt <[email protected]> AuthorDate: Sat Aug 10 09:57:39 2019 +0200 feat: implement _users role handling --- src/couch/src/couch_db.erl | 2 +- src/couch/src/couch_db_updater.erl | 21 ++++++++++++++------- src/couch/src/couch_httpd_auth.erl | 6 +++--- src/couch/test/couchdb_access_tests.erl | 21 ++++++++++++++++----- src/couch_mrview/src/couch_mrview.erl | 3 ++- 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/couch/src/couch_db.erl b/src/couch/src/couch_db.erl index 9231800..42a7176 100644 --- a/src/couch/src/couch_db.erl +++ b/src/couch/src/couch_db.erl @@ -765,7 +765,7 @@ check_name(UserName, Access) -> check_roles(Roles, Access) -> UserRolesSet = ordsets:from_list(Roles), - RolesSet = ordsets:from_list(Access), + RolesSet = ordsets:from_list(Access ++ ["_users"]), not ordsets:is_disjoint(UserRolesSet, RolesSet). get_admins(#db{security=SecProps}) -> diff --git a/src/couch/src/couch_db_updater.erl b/src/couch/src/couch_db_updater.erl index ff6d366..47f1ff1 100644 --- a/src/couch/src/couch_db_updater.erl +++ b/src/couch/src/couch_db_updater.erl @@ -21,12 +21,15 @@ -include("couch_db_int.hrl"). -define(IDLE_LIMIT_DEFAULT, 61000). - +-define(DEFAULT_SECURITY_OBJECT, [ + {<<"members">>,{[{<<"roles">>,[<<"_admin">>]}]}}, + {<<"admins">>, {[{<<"roles">>,[<<"_admin">>]}]}} +]). init({Engine, DbName, FilePath, Options0}) -> erlang:put(io_priority, {db_update, DbName}), update_idle_limit_from_config(), - DefaultSecObj = default_security_object(DbName), + DefaultSecObj = default_security_object(DbName, Options0), Options = [{default_security_object, DefaultSecObj} | Options0], try {ok, EngineState} = couch_db_engine:init(Engine, FilePath, Options), @@ -770,20 +773,24 @@ get_meta_body_size(Meta) -> {ejson_size, ExternalSize} = lists:keyfind(ejson_size, 1, Meta), ExternalSize. - +default_security_object(DbName, []) -> + default_security_object(DbName); +default_security_object(DbName, Options) -> + case lists:member({access, true}, Options) of + false -> default_security_object(DbName); + true -> ?DEFAULT_SECURITY_OBJECT + end. default_security_object(<<"shards/", _/binary>>) -> case config:get("couchdb", "default_security", "everyone") of "admin_only" -> - [{<<"members">>,{[{<<"roles">>,[<<"_admin">>]}]}}, - {<<"admins">>,{[{<<"roles">>,[<<"_admin">>]}]}}]; + ?DEFAULT_SECURITY_OBJECT; Everyone when Everyone == "everyone"; Everyone == "admin_local" -> [] end; default_security_object(_DbName) -> case config:get("couchdb", "default_security", "everyone") of Admin when Admin == "admin_only"; Admin == "admin_local" -> - [{<<"members">>,{[{<<"roles">>,[<<"_admin">>]}]}}, - {<<"admins">>,{[{<<"roles">>,[<<"_admin">>]}]}}]; + ?DEFAULT_SECURITY_OBJECT; "everyone" -> [] end. diff --git a/src/couch/src/couch_httpd_auth.erl b/src/couch/src/couch_httpd_auth.erl index b519534..9aa26ef 100644 --- a/src/couch/src/couch_httpd_auth.erl +++ b/src/couch/src/couch_httpd_auth.erl @@ -102,7 +102,7 @@ default_authentication_handler(Req, AuthModule) -> true -> Req#httpd{user_ctx=#user_ctx{ name=UserName, - roles=couch_util:get_value(<<"roles">>, UserProps, []) + roles=couch_util:get_value(<<"roles">>, UserProps, []) ++ [<<"_users">>] }}; false -> authentication_warning(Req, UserName), @@ -165,7 +165,7 @@ proxy_auth_user(Req) -> Roles = case header_value(Req, XHeaderRoles) of undefined -> []; Else -> - [?l2b(R) || R <- string:tokens(Else, ",")] + [?l2b(R) || R <- string:tokens(Else, ",")] ++ [<<"_users">>] end, case config:get("couch_httpd_auth", "proxy_use_secret", "false") of "true" -> @@ -231,7 +231,7 @@ cookie_authentication_handler(#httpd{mochi_req=MochiReq}=Req, AuthModule) -> [User]), Req#httpd{user_ctx=#user_ctx{ name=?l2b(User), - roles=couch_util:get_value(<<"roles">>, UserProps, []) + roles=couch_util:get_value(<<"roles">>, UserProps, []) ++ [<<"_users">>] }, auth={FullSecret, TimeLeft < Timeout*0.9}}; _Else -> Req diff --git a/src/couch/test/couchdb_access_tests.erl b/src/couch/test/couchdb_access_tests.erl index bba0da2..74ee77c 100644 --- a/src/couch/test/couchdb_access_tests.erl +++ b/src/couch/test/couchdb_access_tests.erl @@ -18,17 +18,22 @@ -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, _, _, _} = test_request:put(url() ++ "/db?q=1&n=1&access=true", ?ADMIN_REQ_HEADERS, ""), + {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, _, _, _} = test_request:delete(Url ++ "/db", ?ADMIN_REQ_HEADERS), + {ok, 200, _, _} = test_request:delete(Url ++ "/db", ?ADMIN_REQ_HEADERS), ok. before_all() -> @@ -38,7 +43,7 @@ before_all() -> % 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, ""), + % {ok, _, _, _} = test_request:put(url() ++ "/db?q=1&n=1&access=true", ?ADMIN_REQ_HEADERS, ""), % create users UserDbUrl = url() ++ "/_users?q=1&n=1", @@ -59,6 +64,7 @@ after_all(_) -> access_test_() -> Tests = [ + fun should_not_let_anonymous_user_create_doc/2, fun should_let_admin_create_doc_with_access/2, fun should_let_user_create_doc_for_themselves/2, fun should_not_let_user_create_doc_for_someone_else/2, @@ -91,8 +97,13 @@ make_test_cases(Mod, Funs) -> }. % Doc creation -should_let_admin_create_doc_with_access(_PortType, Url) -> + +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_user_create_doc_for_themselves(_PortType, Url) -> @@ -154,7 +165,7 @@ should_let_user_fetch_their_own_all_docs(_PortType, Url) -> {ok, 200, _, Body} = test_request:get(Url ++ "/db/_all_docs?include_docs=true", ?USERX_REQ_HEADERS), {Json} = jiffy:decode(Body), ?_assertEqual(2, length(proplists:get_value(<<"rows">>, Json))). -% TODO ?_assertEqual(2, proplists:get_value(<<"total_rows">>, Json)). + % TODO ?_assertEqual(2, proplists:get_value(<<"total_rows">>, Json)). % _changes should_let_admin_fetch_changes(_PortType, Url) -> diff --git a/src/couch_mrview/src/couch_mrview.erl b/src/couch_mrview/src/couch_mrview.erl index 8222dde..72fbb92 100644 --- a/src/couch_mrview/src/couch_mrview.erl +++ b/src/couch_mrview/src/couch_mrview.erl @@ -594,7 +594,8 @@ all_docs_fold(Db, #mrargs{direction=Dir, keys=Keys0}=Args, Callback, UAcc) -> map_fold(Db, View, Args, Callback, UAcc) -> {ok, Total} = case View#mrview.def of <<"_access/by-id-map">> -> - {ok, 0}; % TODO: couch_mrview_util:get_access_row_count(View, Args#mrargs.start_key); + % TODO: couch_mrview_util:get_access_row_count(View, [Args#mrargs.start_key]); + {ok, 0}; _Else -> couch_mrview_util:get_row_count(View) end,
