diff --git a/src/couch_auth_cache.erl b/src/couch_auth_cache.erl index 8cf631b1..61ff9dfc 100644 --- a/src/couch_auth_cache.erl +++ b/src/couch_auth_cache.erl @@ -25,6 +25,8 @@ -export([handle_config_change/5]). -export([handle_db_event/3]). +-export([update_auth_doc/1]). + -include_lib("couch/include/couch_db.hrl"). -include("couch_js_functions.hrl"). @@ -455,3 +457,9 @@ auth_design_doc(DocId) -> {<<"validate_doc_update">>, ?AUTH_DB_DOC_VALIDATE_FUNCTION} ], {ok, couch_doc:from_json_obj({DocProps})}. + +update_auth_doc(Doc) -> + DbName = ?l2b(config:get("couch_httpd_auth", "authentication_db", "_users")), + couch_util:with_db(DbName, fun(UserDb) -> + couch_db:update_doc(UserDb, Doc, []) + end). diff --git a/src/couch_httpd_auth.erl b/src/couch_httpd_auth.erl index 752dd20e..a905b6c4 100644 --- a/src/couch_httpd_auth.erl +++ b/src/couch_httpd_auth.erl @@ -367,14 +367,11 @@ maybe_upgrade_password_hash(UserName, Password, UserProps, AuthModule) -> IsAdmin = lists:member(<<"_admin">>, couch_util:get_value(<<"roles">>, UserProps, [])), case {IsAdmin, couch_util:get_value(<<"password_scheme">>, UserProps, <<"simple">>)} of {false, <<"simple">>} -> - DbName = ?l2b(config:get("couch_httpd_auth", "authentication_db", "_users")), - couch_util:with_db(DbName, fun(UserDb) -> - UserProps2 = proplists:delete(<<"password_sha">>, UserProps), - UserProps3 = [{<<"password">>, Password} | UserProps2], - NewUserDoc = couch_doc:from_json_obj({UserProps3}), - {ok, _NewRev} = couch_db:update_doc(UserDb, NewUserDoc, []), - AuthModule:get_user_creds(UserName) - end); + UserProps2 = proplists:delete(<<"password_sha">>, UserProps), + UserProps3 = [{<<"password">>, Password} | UserProps2], + NewUserDoc = couch_doc:from_json_obj({UserProps3}), + {ok, _NewRev} = AuthModule:update_auth_doc(NewUserDoc), + AuthModule:get_user_creds(UserName); _ -> UserProps end. diff --git a/src/couch_httpd_db.erl b/src/couch_httpd_db.erl index 34f04f21..12e59d10 100644 --- a/src/couch_httpd_db.erl +++ b/src/couch_httpd_db.erl @@ -489,6 +489,29 @@ db_doc_req(#httpd{method='DELETE'}=Req, Db, DocId) -> end; db_doc_req(#httpd{method = 'GET', mochi_req = MochiReq} = Req, Db, DocId) -> + case DocId of + <<"_design/", _/binary>> -> + DbName = mem3:dbname(Db#db.name), + AuthDbName = ?l2b(config:get("couch_httpd_auth", "authentication_db")), + case AuthDbName of + DbName -> + % in the authentication database, design doc access are admin-only. + %{SecProps} = fabric:get_security(DbName), + %case (catch couch_db:check_is_admin(Db#db{security=SecProps})) of + case (catch couch_db:check_is_admin(Db)) of + ok -> + ok; + _ -> + throw({forbidden, + <<"Only administrators can view design docs in the users database.">>}) + end; + _Else -> + % on other databases, design doc access is free for all. + ok + end; + _Else -> + ok + end, #doc_query_args{ rev = Rev, open_revs = Revs, diff --git a/src/couch_server.erl b/src/couch_server.erl index 8f9696b8..024ab4de 100644 --- a/src/couch_server.erl +++ b/src/couch_server.erl @@ -116,30 +116,37 @@ create(DbName, Options0) -> delete(DbName, Options) -> gen_server:call(couch_server, {delete, DbName, Options}, infinity). +is_users_db(DbName) -> + IsUsersDbPreds = [ + fun() -> DbName == config:get("couch_httpd_auth", "authentication_db", "_users") end, + fun() -> path_ends_with(DbName, <<"_users">>) end, + fun() -> binary_to_list(mem3:dbname(DbName)) == config:get("chttpd_auth", "authentication_db", "_users") end + ], + lists:any(fun(F) -> F() end, IsUsersDbPreds). + maybe_add_sys_db_callbacks(DbName, Options) when is_binary(DbName) -> maybe_add_sys_db_callbacks(?b2l(DbName), Options); maybe_add_sys_db_callbacks(DbName, Options) -> DbsDbName = config:get("mem3", "shard_db", "dbs"), NodesDbName = config:get("mem3", "shard_db", "nodes"), IsReplicatorDb = DbName == config:get("replicator", "db", "_replicator") orelse - path_ends_with(DbName, <<"_replicator">>), - IsUsersDb = DbName ==config:get("couch_httpd_auth", "authentication_db", "_users") orelse - path_ends_with(DbName, <<"_users">>), + path_ends_with(DbName, <<"_replicator">>), + IsUsersDb = is_users_db(DbName), if - DbName == DbsDbName -> - [sys_db | Options]; - DbName == NodesDbName -> - [sys_db | Options]; - IsReplicatorDb -> - [{before_doc_update, fun couch_replicator_manager:before_doc_update/2}, - {after_doc_read, fun couch_replicator_manager:after_doc_read/2}, - sys_db | Options]; - IsUsersDb -> - [{before_doc_update, fun couch_users_db:before_doc_update/2}, - {after_doc_read, fun couch_users_db:after_doc_read/2}, - sys_db | Options]; - true -> - Options + DbName == DbsDbName -> + [sys_db | Options]; + DbName == NodesDbName -> + [sys_db | Options]; + IsReplicatorDb -> + [{before_doc_update, fun couch_replicator_manager:before_doc_update/2}, + {after_doc_read, fun couch_replicator_manager:after_doc_read/2}, + sys_db | Options]; + IsUsersDb -> + [{before_doc_update, fun couch_users_db:before_doc_update/2}, + {after_doc_read, fun couch_users_db:after_doc_read/2}, + sys_db | Options]; + true -> + Options end. path_ends_with(Path, Suffix) -> diff --git a/src/couch_users_db.erl b/src/couch_users_db.erl index 3b768623..a16b874a 100644 --- a/src/couch_users_db.erl +++ b/src/couch_users_db.erl @@ -73,11 +73,8 @@ save_doc(#doc{body={Body}} = Doc) -> Doc#doc{body={Body3}} end. -% If the doc is a design doc -% If the request's userCtx identifies an admin -% -> return doc -% Else -% -> 403 // Forbidden +% If the doc is a design doc return it and relay on the http layer to restrict +% access to admins % If the request's userCtx identifies an admin % -> return doc % If the request's userCtx.name doesn't match the doc's name @@ -85,13 +82,7 @@ save_doc(#doc{body={Body}} = Doc) -> % Else % -> return doc after_doc_read(#doc{id = <>} = Doc, Db) -> - case (catch couch_db:check_is_admin(Db)) of - ok -> - Doc; - _ -> - throw({forbidden, - <<"Only administrators can view design docs in the users database.">>}) - end; + Doc; after_doc_read(Doc, #db{user_ctx = UserCtx} = Db) -> #user_ctx{name=Name} = UserCtx, DocName = get_doc_name(Doc),