couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dam...@apache.org
Subject svn commit: r706848 - in /incubator/couchdb/trunk/src/couchdb: couch_db.hrl couch_httpd.erl couch_httpd_db.erl couch_httpd_misc_handlers.erl couch_server.erl
Date Wed, 22 Oct 2008 03:08:54 GMT
Author: damien
Date: Tue Oct 21 20:08:53 2008
New Revision: 706848

URL: http://svn.apache.org/viewvc?rev=706848&view=rev
Log:
First check-in of admin http authentication and authorization.

Modified:
    incubator/couchdb/trunk/src/couchdb/couch_db.hrl
    incubator/couchdb/trunk/src/couchdb/couch_httpd.erl
    incubator/couchdb/trunk/src/couchdb/couch_httpd_db.erl
    incubator/couchdb/trunk/src/couchdb/couch_httpd_misc_handlers.erl
    incubator/couchdb/trunk/src/couchdb/couch_server.erl

Modified: incubator/couchdb/trunk/src/couchdb/couch_db.hrl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_db.hrl?rev=706848&r1=706847&r2=706848&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_db.hrl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_db.hrl Tue Oct 21 20:08:53 2008
@@ -17,6 +17,9 @@
 -define(JSON_ENCODE(V), mochijson2:encode(V)).
 -define(JSON_DECODE(V), mochijson2:decode(V)).
 
+-define(b2l(V), binary_to_list(V)).
+-define(l2b(V), list_to_binary(V)).
+
 -define(DEFAULT_ATTACHMENT_CONTENT_TYPE, <<"application/octet-stream">>).
         
 -define(LOG_DEBUG(Format, Args),

Modified: incubator/couchdb/trunk/src/couchdb/couch_httpd.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_httpd.erl?rev=706848&r1=706847&r2=706848&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_httpd.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_httpd.erl Tue Oct 21 20:08:53 2008
@@ -15,7 +15,8 @@
 
 -export([start_link/0, stop/0, handle_request/3]).
 
--export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,path/1,unquote/1]).
+-export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,path/1]).
+-export([check_is_admin/1,unquote/1]).
 -export([parse_form/1,json_body/1,body/1,doc_etag/1]).
 -export([primary_header_value/2,partition/1,serve_file/3]).
 -export([start_chunked_response/3,send_chunk/2]).
@@ -85,6 +86,7 @@
 
 stop() ->
     mochiweb_http:stop(?MODULE).
+    
 
 handle_request(MochiReq, UrlHandlers, DbUrlHandlers) ->
 
@@ -195,6 +197,30 @@
 doc_etag(#doc{revs=[DiskRev|_]}) ->
      "\"" ++ binary_to_list(DiskRev) ++ "\"".
 
+check_is_admin(Req) ->
+    IsNamedAdmin =
+    case header_value(Req, "Authorization") of
+    "Basic " ++ Base64Value ->
+        [User, Pass] =
+            string:tokens(?b2l(couch_util:decodeBase64(Base64Value)),":"),
+        couch_server:is_admin(User, Pass);
+    _ ->
+        false
+    end,
+    
+    case IsNamedAdmin of
+    true ->
+        ok;
+    false ->
+        case couch_server:has_admins() of
+        true ->
+            throw(admin_auth_error);
+        false ->
+            % if no admins, then everyone is admin! Yay, admin party!
+            ok
+        end
+    end.
+
 start_chunked_response(#httpd{mochi_req=MochiReq}, Code, Headers) ->
     {ok, MochiReq:respond({Code, Headers ++ server_header(), chunked})}.
 
@@ -250,6 +276,11 @@
     send_error(Req, 404, <<"not_found">>, Reason);
 send_error(Req, conflict) ->
     send_error(Req, 412, <<"conflict">>, <<"Document update conflict.">>);
+send_error(Req, admin_auth_error) ->
+    send_json(Req, 401,
+        [{"WWW-Authenticate", "Basic realm=\"admin\""}],
+        {[{<<"error">>,  <<"auth_error">>},
+         {<<"reason">>, <<"Admin user name and password required">>}]});
 send_error(Req, {doc_validation, Msg}) ->
     send_error(Req, 406, <<"doc_validation">>, Msg);
 send_error(Req, file_exists) ->

Modified: incubator/couchdb/trunk/src/couchdb/couch_httpd_db.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=706848&r1=706847&r2=706848&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_httpd_db.erl Tue Oct 21 20:08:53 2008
@@ -42,6 +42,7 @@
     end.
 
 create_db_req(Req, DbName) ->
+    ok = couch_httpd:check_is_admin(Req),
     case couch_server:create(DbName, []) of
     {ok, Db} ->
         couch_db:close(Db),
@@ -51,6 +52,7 @@
     end.
 
 delete_db_req(Req, DbName) ->
+    ok = couch_httpd:check_is_admin(Req),
     case couch_server:delete(DbName) of
     ok ->
         send_json(Req, 200, {[{ok, true}]});

Modified: incubator/couchdb/trunk/src/couchdb/couch_httpd_misc_handlers.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_httpd_misc_handlers.erl?rev=706848&r1=706847&r2=706848&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_httpd_misc_handlers.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_httpd_misc_handlers.erl Tue Oct 21 20:08:53
2008
@@ -70,6 +70,7 @@
 
 
 handle_restart_req(#httpd{method='POST'}=Req) ->
+    ok = couch_httpd:check_is_admin(Req),
     Response = send_json(Req, {[{ok, true}]}),
     spawn(fun() -> couch_server:remote_restart() end),
     Response;
@@ -93,6 +94,7 @@
 % GET /_config/
 % GET /_config
 handle_config_req(#httpd{method='GET', path_parts=[_]}=Req) ->
+    ok = couch_httpd:check_is_admin(Req),
     Grouped = lists:foldl(fun({{Section, Key}, Value}, Acc) ->
         case dict:is_key(Section, Acc) of
         true ->
@@ -107,12 +109,14 @@
     send_json(Req, 200, {KVs});
 % GET /_config/Section
 handle_config_req(#httpd{method='GET', path_parts=[_,Section]}=Req) ->
+    ok = couch_httpd:check_is_admin(Req),
     KVs = [{list_to_binary(Key), list_to_binary(Value)}
             || {Key, Value} <- couch_config:get(Section)],
     send_json(Req, 200, {KVs});
 % PUT /_config/Section/Key
 % "value"
 handle_config_req(#httpd{method='PUT', path_parts=[_, Section, Key]}=Req) ->
+    ok = couch_httpd:check_is_admin(Req),
     Value = binary_to_list(couch_httpd:body(Req)),
     ok = couch_config:set(Section, Key, Value),
     send_json(Req, 200, {[
@@ -120,6 +124,7 @@
     ]});
 % GET /_config/Section/Key
 handle_config_req(#httpd{method='GET', path_parts=[_, Section, Key]}=Req) ->
+    ok = couch_httpd:check_is_admin(Req),
     case couch_config:get(Section, Key, null) of
     null ->
         throw({not_found, unknown_config_value});
@@ -128,6 +133,7 @@
     end;
 % DELETE /_config/Section/Key
 handle_config_req(#httpd{method='DELETE',path_parts=[_,Section,Key]}=Req) ->
+    ok = couch_httpd:check_is_admin(Req),
     case couch_config:get(Section, Key, null) of
     null ->
         throw({not_found, unknown_config_value});

Modified: incubator/couchdb/trunk/src/couchdb/couch_server.erl
URL: http://svn.apache.org/viewvc/incubator/couchdb/trunk/src/couchdb/couch_server.erl?rev=706848&r1=706847&r2=706848&view=diff
==============================================================================
--- incubator/couchdb/trunk/src/couchdb/couch_server.erl (original)
+++ incubator/couchdb/trunk/src/couchdb/couch_server.erl Tue Oct 21 20:08:53 2008
@@ -18,7 +18,7 @@
 -export([open/2,create/2,delete/1,all_databases/0,get_version/0]).
 -export([init/1, handle_call/3,sup_start_link/0]).
 -export([handle_cast/2,code_change/3,handle_info/2,terminate/2]).
--export([dev_start/0,remote_restart/0]).
+-export([dev_start/0,remote_restart/0,is_admin/2,has_admins/0]).
 
 -include("couch_db.hrl").
 
@@ -85,9 +85,31 @@
         ok
     end.
 
+is_admin(User, ClearPwd) ->
+    case couch_config:get("admins", User) of
+    "-hashed-" ++ HashedPwdAndSalt ->
+        [HashedPwd, Salt] = string:tokens(HashedPwdAndSalt, ","),
+        couch_util:to_hex(crypto:sha(ClearPwd ++ Salt)) == HashedPwd;
+    _Else ->
+        false
+    end.
+
+has_admins() ->
+    couch_config:get("admins") /= [].
+
 get_full_filename(Server, DbName) ->
     filename:join([Server#server.root_dir, "./" ++ DbName ++ ".couch"]).
 
+hash_admin_passwords() ->
+    lists:foreach(
+        fun({_User, "-hashed-" ++ _}) ->
+            ok; % already hashed
+        ({User, ClearPassword}) ->
+            Salt = ?b2l(couch_util:new_uuid()),
+            Hashed = couch_util:to_hex(crypto:sha(ClearPassword ++ Salt)),
+            couch_config:set("admins", User, "-hashed-" ++ Hashed ++ "," ++ Salt)
+        end, couch_config:get("admins")).
+
 init([]) ->
     % read config and register for configuration changes
     
@@ -103,6 +125,11 @@
         ("couchdb", "server_options") ->
             exit(Self, config_change)
         end),
+    ok = couch_config:register(
+        fun("admins", _) ->
+            hash_admin_passwords()
+        end),
+    hash_admin_passwords(),
     {ok, RegExp} = regexp:parse("^[a-z][a-z0-9\\_\\$()\\+\\-\\/]*$"),
     ets:new(couch_dbs_by_name, [set, private, named_table]),
     ets:new(couch_dbs_by_pid, [set, private, named_table]),



Mime
View raw message