You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ja...@apache.org on 2019/08/10 07:57:50 UTC
[couchdb] 02/02: feat: implement _users role handling
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 <ja...@apache.org>
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,