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

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

commit eeef2eec91bbcdea8bd86674604eddb1858cbde1
Author: Alexander Shorin <[email protected]>
AuthorDate: Sun Dec 20 18:26:44 2015 +0300

    Improve checks for db admin/member
    
    - Use lists:member/2 to check if user name is in list
    - Throw forbidden error if user is authenticated on db membership check
    - Normalize terminology readers vs members
    - Make checks more Erlang-ish
    
    COUCHDB-2534
---
 share/doc/src/whatsnew/1.7.rst |   1 +
 src/couchdb/couch_db.erl       | 105 ++++++++++++++++++++++++++---------------
 2 files changed, 69 insertions(+), 37 deletions(-)

diff --git a/share/doc/src/whatsnew/1.7.rst b/share/doc/src/whatsnew/1.7.rst
index 75bdca7..58af573 100644
--- a/share/doc/src/whatsnew/1.7.rst
+++ b/share/doc/src/whatsnew/1.7.rst
@@ -47,6 +47,7 @@ Build
 Database Core
 -------------
 
+- :issue:`2534`: Improve checks for db admin/member.
 - :issue:`2735`: Duplicate document _ids created under high edit load.
 
 Documentation
diff --git a/src/couchdb/couch_db.erl b/src/couchdb/couch_db.erl
index 2957818..e2321eb 100644
--- a/src/couchdb/couch_db.erl
+++ b/src/couchdb/couch_db.erl
@@ -317,48 +317,79 @@ get_design_docs(Db) ->
     {ok, _, Docs} = couch_btree:fold(by_id_btree(Db), FoldFun, [], KeyOpts),
     Docs.
 
-check_is_admin(#db{user_ctx=#user_ctx{name=Name,roles=Roles}}=Db) ->
-    {Admins} = get_admins(Db),
-    AdminRoles = [<<"_admin">> | couch_util:get_value(<<"roles">>, Admins, 
[])],
-    AdminNames = couch_util:get_value(<<"names">>, Admins,[]),
-    case AdminRoles -- Roles of
-    AdminRoles -> % same list, not an admin role
-        case AdminNames -- [Name] of
-        AdminNames -> % same names, not an admin
-            throw({unauthorized, <<"You are not a db or server admin.">>});
-        _ ->
-            ok
-        end;
-    _ ->
-        ok
+
+check_is_admin(#db{user_ctx=UserCtx}=Db) ->
+    case is_admin(Db) of
+        true -> ok;
+        false ->
+            Reason = <<"You are not a db or server admin.">>,
+            throw_security_error(UserCtx, Reason)
     end.
 
-check_is_member(#db{user_ctx=#user_ctx{name=Name,roles=Roles}=UserCtx}=Db) ->
-    case (catch check_is_admin(Db)) of
-    ok -> ok;
-    _ ->
-        {Members} = get_members(Db),
-        ReaderRoles = couch_util:get_value(<<"roles">>, Members,[]),
-        WithAdminRoles = [<<"_admin">> | ReaderRoles],
-        ReaderNames = couch_util:get_value(<<"names">>, Members,[]),
-        case ReaderRoles ++ ReaderNames of
-        [] -> ok; % no readers == public access
-        _Else ->
-            case WithAdminRoles -- Roles of
-            WithAdminRoles -> % same list, not an reader role
-                case ReaderNames -- [Name] of
-                ReaderNames -> % same names, not a reader
-                    ?LOG_DEBUG("Not a reader: UserCtx ~p vs Names ~p Roles 
~p",[UserCtx, ReaderNames, WithAdminRoles]),
-                    throw({unauthorized, <<"You are not authorized to access 
this db.">>});
-                _ ->
-                    ok
-                end;
-            _ ->
-                ok
+check_is_member(#db{user_ctx=UserCtx}=Db) ->
+    case is_member(Db) of
+        true -> ok;
+        false -> throw_security_error(UserCtx)
+    end.
+
+is_admin(#db{user_ctx=UserCtx}=Db) ->
+    {Admins} = get_admins(Db),
+    is_authorized(UserCtx, Admins).
+
+is_member(#db{user_ctx=UserCtx}=Db) ->
+    case is_admin(Db) of
+        true -> true;
+        false ->
+            case is_public_db(Db) of
+                true -> true;
+                false ->
+                    {Members} = get_members(Db),
+                    is_authorized(UserCtx, Members)
             end
-        end
     end.
 
+is_public_db(#db{}=Db) ->
+    {Members} = get_members(Db),
+    Names = couch_util:get_value(<<"names">>, Members, []),
+    Roles = couch_util:get_value(<<"roles">>, Members, []),
+    Names =:= [] andalso Roles =:= [].
+
+is_authorized(#user_ctx{name=UserName,roles=UserRoles}, Security) ->
+    Names = couch_util:get_value(<<"names">>, Security, []),
+    Roles = couch_util:get_value(<<"roles">>, Security, []),
+    case check_security(roles, UserRoles, [<<"_admin">> | Roles]) of
+        true -> true;
+        false -> check_security(names, UserName, Names)
+    end.
+
+check_security(roles, [], _) ->
+    false;
+check_security(roles, UserRoles, Roles) ->
+    UserRolesSet = ordsets:from_list(UserRoles),
+    RolesSet = ordsets:from_list(Roles),
+    not ordsets:is_disjoint(UserRolesSet, RolesSet);
+check_security(names, _, []) ->
+    false;
+check_security(names, null, _) ->
+    false;
+check_security(names, UserName, Names) ->
+    lists:member(UserName, Names).
+
+throw_security_error(#user_ctx{name=null}=UserCtx) ->
+    Reason = <<"You are not authorized to access this db.">>,
+    throw_security_error(UserCtx, Reason);
+throw_security_error(#user_ctx{name=_}=UserCtx) ->
+    Reason = <<"You are not allowed to access this db.">>,
+    throw_security_error(UserCtx, Reason).
+throw_security_error(#user_ctx{}=UserCtx, Reason) ->
+    Error = security_error_type(UserCtx),
+    throw({Error, Reason}).
+
+security_error_type(#user_ctx{name=null}) ->
+    unauthorized;
+security_error_type(#user_ctx{name=_}) ->
+    forbidden.
+
 get_admins(#db{security=SecProps}) ->
     couch_util:get_value(<<"admins">>, SecProps, {[]}).
 

-- 
To stop receiving notification emails like this one, please contact
"[email protected]" <[email protected]>.

Reply via email to