summaryrefslogtreecommitdiff
path: root/src/couchdb/couch_httpd.erl
diff options
context:
space:
mode:
authorDamien F. Katz <damien@apache.org>2008-11-17 18:18:51 +0000
committerDamien F. Katz <damien@apache.org>2008-11-17 18:18:51 +0000
commitaee6f18edf8cdf3f7c09c93fcf1af48c2c15fcd8 (patch)
treefd3a259ba4b0edd2c9ea5e6657f0ae314b36cbb5 /src/couchdb/couch_httpd.erl
parentd32d8acff4bac6f51b87ddef7091c04ff7245d40 (diff)
More security and validation work. Still incomplete.
git-svn-id: https://svn.apache.org/repos/asf/incubator/couchdb/trunk@718311 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/couchdb/couch_httpd.erl')
-rw-r--r--src/couchdb/couch_httpd.erl83
1 files changed, 50 insertions, 33 deletions
diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
index c58255e3..6c8873dd 100644
--- a/src/couchdb/couch_httpd.erl
+++ b/src/couchdb/couch_httpd.erl
@@ -13,16 +13,17 @@
-module(couch_httpd).
-include("couch_db.hrl").
--export([start_link/0, stop/0, handle_request/3]).
+-export([start_link/0, stop/0, handle_request/4]).
-export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,path/1]).
--export([check_is_admin/1,unquote/1,creds/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]).
-export([start_json_response/2, start_json_response/3, end_json_response/1]).
-export([send_response/4,send_method_not_allowed/2,send_error/4]).
-export([send_json/2,send_json/3,send_json/4]).
+-export([default_authenticate/1]).
% Maximum size of document PUT request body (4GB)
@@ -36,32 +37,24 @@ start_link() ->
BindAddress = couch_config:get("httpd", "bind_address", any),
Port = couch_config:get("httpd", "port", "5984"),
-
+ AuthenticationFun = make_arity_1_fun(
+ couch_config:get("httpd", "authentication",
+ "{couch_httpd, default_authenticate}")),
+
UrlHandlersList = lists:map(
fun({UrlKey, SpecStr}) ->
- case couch_util:parse_term(SpecStr) of
- {ok, {M, F, A}} ->
- {list_to_binary(UrlKey), fun(Req) -> apply(M, F, [Req, A]) end};
- {ok, {M, F}} ->
- {list_to_binary(UrlKey), fun(Req) -> apply(M, F, [Req]) end}
- end
+ {list_to_binary(UrlKey), make_arity_1_fun(SpecStr)}
end, couch_config:get("httpd_global_handlers")),
DbUrlHandlersList = lists:map(
fun({UrlKey, SpecStr}) ->
- case couch_util:parse_term(SpecStr) of
- {ok, {M, F, A}} ->
- {list_to_binary(UrlKey),
- fun(Req, Db) -> apply(M, F, [Req, Db, A]) end};
- {ok, {M, F}} ->
- {list_to_binary(UrlKey),
- fun(Req, Db) -> apply(M, F, [Req, Db]) end}
- end
+ {list_to_binary(UrlKey), make_arity_2_fun(SpecStr)}
end, couch_config:get("httpd_db_handlers")),
UrlHandlers = dict:from_list(UrlHandlersList),
DbUrlHandlers = dict:from_list(DbUrlHandlersList),
Loop = fun(Req)->
- apply(?MODULE, handle_request, [Req, UrlHandlers, DbUrlHandlers])
+ apply(?MODULE, handle_request,
+ [Req, UrlHandlers, DbUrlHandlers, AuthenticationFun])
end,
% and off we go
@@ -76,6 +69,8 @@ start_link() ->
?MODULE:stop();
("httpd", "port") ->
?MODULE:stop();
+ ("httpd", "authentication") ->
+ ?MODULE:stop();
("httpd_global_handlers", _) ->
?MODULE:stop();
("httpd_db_handlers", _) ->
@@ -84,11 +79,29 @@ start_link() ->
{ok, Pid}.
+% SpecStr is a string like "{my_module, my_fun}" or "{my_module, my_fun, foo}"
+make_arity_1_fun(SpecStr) ->
+ case couch_util:parse_term(SpecStr) of
+ {ok, {Mod, Fun, SpecArg}} ->
+ fun(Arg) -> apply(Mod, Fun, [Arg, SpecArg]) end;
+ {ok, {Mod, Fun}} ->
+ fun(Arg) -> apply(Mod, Fun, [Arg]) end
+ end.
+
+make_arity_2_fun(SpecStr) ->
+ case couch_util:parse_term(SpecStr) of
+ {ok, {Mod, Fun, SpecArg}} ->
+ fun(Arg1, Arg2) -> apply(Mod, Fun, [Arg1, Arg2, SpecArg]) end;
+ {ok, {Mod, Fun}} ->
+ fun(Arg1, Arg2) -> apply(Mod, Fun, [Arg1, Arg2]) end
+ end.
+
+
stop() ->
mochiweb_http:stop(?MODULE).
-handle_request(MochiReq, UrlHandlers, DbUrlHandlers) ->
+handle_request(MochiReq, UrlHandlers, DbUrlHandlers, AuthenticationFun) ->
% for the path, use the raw path with the query string and fragment
% removed, but URL quoting left intact
@@ -128,13 +141,16 @@ handle_request(MochiReq, UrlHandlers, DbUrlHandlers) ->
|| Part <- string:tokens(Path, "/")],
db_url_handlers = DbUrlHandlers
},
-
DefaultFun = fun couch_httpd_db:handle_request/1,
HandlerFun = couch_util:dict_find(HandlerKey, UrlHandlers, DefaultFun),
+ CouchHeaders = [{?l2b(K), ?l2b(V)}
+ || {"X-Couch-" ++ _= K,V}
+ <- mochiweb_headers:to_list(MochiReq:get(headers))],
{ok, Resp} =
try
- HandlerFun(HttpReq)
+ {UserCtxProps} = AuthenticationFun(HttpReq),
+ HandlerFun(HttpReq#httpd{user_ctx={UserCtxProps ++ CouchHeaders}})
catch
Error ->
send_error(HttpReq, Error)
@@ -150,6 +166,17 @@ handle_request(MochiReq, UrlHandlers, DbUrlHandlers) ->
+default_authenticate(Req) ->
+ % by default, we just assume the users credentials for basic authentication
+ % are correct.
+ case basic_username_pw(Req) of
+ {Username, _Pw} ->
+ {[{<<"name">>, ?l2b(Username)}]};
+ nil ->
+ {[]}
+ end.
+
+
% Utilities
partition(Path) ->
@@ -197,17 +224,7 @@ json_body(#httpd{mochi_req=MochiReq}) ->
doc_etag(#doc{revs=[DiskRev|_]}) ->
"\"" ++ binary_to_list(DiskRev) ++ "\"".
-
-% user credentials
-creds(Req) ->
- case username_pw(Req) of
- {User, _Pw} ->
- {[{<<"name">>, ?l2b(User)}]};
- nil ->
- {[]}
- end.
-
-username_pw(Req) ->
+basic_username_pw(Req) ->
case header_value(Req, "Authorization") of
"Basic " ++ Base64Value ->
case string:tokens(?b2l(couch_util:decodeBase64(Base64Value)),":") of
@@ -224,7 +241,7 @@ username_pw(Req) ->
check_is_admin(Req) ->
IsNamedAdmin =
- case username_pw(Req) of
+ case basic_username_pw(Req) of
{User, Pass} ->
couch_server:is_admin(User, Pass);
nil ->