couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fdman...@apache.org
Subject [2/2] git commit: Fix OAuth authentication with VHosts + URL rewriting
Date Sat, 10 Dec 2011 20:03:21 GMT
Fix OAuth authentication with VHosts + URL rewriting

The OAuth handler was not getting the right path (the one
the client used to compute its OAuth signature) to verify
the client's signature. The right path is the one from
before doing the VHost dispatch.
Secondly, after the OAuth handler succeeds, the rewriter
kicks in and calls couch_httpd:handle_request_int/5 with a
new mochiweb request which contains the rewritten patch.
This will cause all the authentication handlers to run again,
which makes the OAuth handler fail this second time because
it gets a rewritten patch.

COUCHDB-1320


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/b86fa1f6
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/b86fa1f6
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/b86fa1f6

Branch: refs/heads/1.2.x
Commit: b86fa1f6bedee9d441bf4cac53c2794a60c69216
Parents: 25754ac
Author: Filipe David Borba Manana <fdmanana@apache.org>
Authored: Sat Dec 10 19:05:52 2011 +0000
Committer: Filipe David Borba Manana <fdmanana@apache.org>
Committed: Sat Dec 10 19:40:37 2011 +0000

----------------------------------------------------------------------
 src/couchdb/couch_httpd.erl         |    3 +-
 src/couchdb/couch_httpd_oauth.erl   |   11 +++-
 src/couchdb/couch_httpd_rewrite.erl |    4 +-
 test/etap/160-vhosts.t              |   89 +++++++++++++++++++++++++++++-
 4 files changed, 102 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/b86fa1f6/src/couchdb/couch_httpd.erl
----------------------------------------------------------------------
diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
index 11b0bca..2d4c38d 100644
--- a/src/couchdb/couch_httpd.erl
+++ b/src/couchdb/couch_httpd.erl
@@ -298,7 +298,8 @@ handle_request_int(MochiReq, DefaultFun,
         db_url_handlers = DbUrlHandlers,
         design_url_handlers = DesignUrlHandlers,
         default_fun = DefaultFun,
-        url_handlers = UrlHandlers
+        url_handlers = UrlHandlers,
+        user_ctx = erlang:erase(pre_rewrite_user_ctx)
     },
 
     HandlerFun = couch_util:dict_find(HandlerKey, UrlHandlers, DefaultFun),

http://git-wip-us.apache.org/repos/asf/couchdb/blob/b86fa1f6/src/couchdb/couch_httpd_oauth.erl
----------------------------------------------------------------------
diff --git a/src/couchdb/couch_httpd_oauth.erl b/src/couchdb/couch_httpd_oauth.erl
index 4d58a88..65304a3 100644
--- a/src/couchdb/couch_httpd_oauth.erl
+++ b/src/couchdb/couch_httpd_oauth.erl
@@ -133,8 +133,15 @@ serve_oauth(#httpd{mochi_req=MochiReq}=Req, Fun, FailSilently) ->
 
     % get requested path
     RequestedPath = case MochiReq:get_header_value("x-couchdb-requested-path") of
-        undefined -> MochiReq:get(raw_path);
-        RequestedPath0 -> RequestedPath0
+        undefined ->
+            case MochiReq:get_header_value("x-couchdb-vhost-path") of
+                undefined ->
+                    MochiReq:get(raw_path);
+                VHostPath ->
+                    VHostPath
+            end;
+        RequestedPath0 ->
+           RequestedPath0
     end,
     {_, QueryString, _} = mochiweb_util:urlsplit_path(RequestedPath),
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/b86fa1f6/src/couchdb/couch_httpd_rewrite.erl
----------------------------------------------------------------------
diff --git a/src/couchdb/couch_httpd_rewrite.erl b/src/couchdb/couch_httpd_rewrite.erl
index bf93478..c8cab85 100644
--- a/src/couchdb/couch_httpd_rewrite.erl
+++ b/src/couchdb/couch_httpd_rewrite.erl
@@ -187,8 +187,10 @@ handle_rewrite_req(#httpd{
                 db_url_handlers = DbUrlHandlers,
                 design_url_handlers = DesignUrlHandlers,
                 default_fun = DefaultFun,
-                url_handlers = UrlHandlers
+                url_handlers = UrlHandlers,
+                user_ctx = UserCtx
             } = Req,
+            erlang:put(pre_rewrite_user_ctx, UserCtx),
             couch_httpd:handle_request_int(MochiReq1, DefaultFun,
                     UrlHandlers, DbUrlHandlers, DesignUrlHandlers)
         end.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/b86fa1f6/test/etap/160-vhosts.t
----------------------------------------------------------------------
diff --git a/test/etap/160-vhosts.t b/test/etap/160-vhosts.t
index e959f74..0b239a1 100755
--- a/test/etap/160-vhosts.t
+++ b/test/etap/160-vhosts.t
@@ -52,7 +52,7 @@ admin_user_ctx() -> {user_ctx, #user_ctx{roles=[<<"_admin">>]}}.
 main(_) ->
     test_util:init_code_path(),
 
-    etap:plan(15),
+    etap:plan(18),
     case (catch test()) of
         ok ->
             etap:end_tests();
@@ -135,9 +135,11 @@ test() ->
     test_vhost_request_path2(),
     test_vhost_request_path3(),
     test_vhost_request_to_root(),
+    test_vhost_request_with_oauth(Db),
 
     %% restart boilerplate
     couch_db:close(Db),
+    ok = couch_server:delete(couch_db:name(Db), [admin_user_ctx()]),
     timer:sleep(3000),
     couch_server_sup:stop(),
 
@@ -301,3 +303,88 @@ test_vhost_request_to_root() ->
             etap:is(HasCouchDBWelcome, true, "should allow redirect to /");
         _Else -> etap:is(false, true, <<"ibrowse fail">>)
     end.
+
+test_vhost_request_with_oauth(Db) ->
+    {ok, AuthDb} = couch_db:create(
+        <<"tap_test_sec_db">>, [admin_user_ctx(), overwrite]),
+    PrevAuthDbName = couch_config:get("couch_httpd_auth", "authentication_db"),
+    couch_config:set("couch_httpd_auth", "authentication_db", "tap_test_sec_db", false),
+    couch_config:set("oauth_token_users", "otoksec1", "joe", false),
+    couch_config:set("oauth_consumer_secrets", "consec1", "foo", false),
+    couch_config:set("oauth_token_secrets", "otoksec1", "foobar", false),
+    couch_config:set("couch_httpd_auth", "require_valid_user", "true", false),
+
+    DDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"_design/test">>},
+        {<<"language">>, <<"javascript">>},
+        {<<"rewrites">>, [
+            {[
+                {<<"from">>, <<"foobar">>},
+                {<<"to">>, <<"_info">>}
+            ]}
+        ]}
+    ]}),
+    {ok, _} = couch_db:update_doc(Db, DDoc, []),
+
+    RewritePath = "/etap-test-db/_design/test/_rewrite/foobar",
+    ok = couch_config:set("vhosts", "oauth-example.com", RewritePath, false),
+    couch_httpd_vhost:reload(),
+
+    case ibrowse:send_req(server(), [], get, [], [{host_header, "oauth-example.com"}]) of
+        {ok, "401", _, Body} ->
+            {JsonBody} = ejson:decode(Body),
+            etap:is(
+                couch_util:get_value(<<"error">>, JsonBody),
+                <<"unauthorized">>,
+                "Request without OAuth credentials failed");
+        Error ->
+           etap:bail("Request without OAuth credentials did not fail: " ++
+               couch_util:to_list(Error))
+    end,
+
+    JoeDoc = couch_doc:from_json_obj({[
+        {<<"_id">>, <<"org.couchdb.user:joe">>},
+        {<<"type">>, <<"user">>},
+        {<<"name">>, <<"joe">>},
+        {<<"roles">>, []},
+        {<<"password_sha">>, <<"fe95df1ca59a9b567bdca5cbaf8412abd6e06121">>},
+        {<<"salt">>, <<"4e170ffeb6f34daecfd814dfb4001a73">>}
+    ]}),
+    {ok, _} = couch_db:update_doc(AuthDb, JoeDoc, []),
+
+    Url = "http://oauth-example.com/",
+    Consumer = {"consec1", "foo", hmac_sha1},
+    SignedParams = oauth:signed_params(
+        "GET", Url, [], Consumer, "otoksec1", "foobar"),
+    OAuthUrl = oauth:uri(server(), SignedParams),
+
+    case ibrowse:send_req(OAuthUrl, [], get, [], [{host_header, "oauth-example.com"}]) of
+        {ok, "200", _, Body2} ->
+            {JsonBody2} = ejson:decode(Body2),
+            etap:is(couch_util:get_value(<<"name">>, JsonBody2), <<"test">>,
+                "should return ddoc info with OAuth credentials");
+        Error2 ->
+           etap:bail("Failed to access vhost with OAuth credentials: " ++
+               couch_util:to_list(Error2))
+    end,
+
+    Consumer2 = {"consec1", "bad_secret", hmac_sha1},
+    SignedParams2 = oauth:signed_params(
+        "GET", Url, [], Consumer2, "otoksec1", "foobar"),
+    OAuthUrl2 = oauth:uri(server(), SignedParams2),
+
+    case ibrowse:send_req(OAuthUrl2, [], get, [], [{host_header, "oauth-example.com"}]) of
+        {ok, "401", _, Body3} ->
+            {JsonBody3} = ejson:decode(Body3),
+            etap:is(
+                couch_util:get_value(<<"error">>, JsonBody3),
+                <<"unauthorized">>,
+                "Request with bad OAuth credentials failed");
+        Error3 ->
+           etap:bail("Failed to access vhost with bad OAuth credentials: " ++
+               couch_util:to_list(Error3))
+    end,
+
+    couch_config:set("couch_httpd_auth", "authentication_db", PrevAuthDbName, false),
+    couch_config:set("couch_httpd_auth", "require_valid_user", "false", false),
+    ok = couch_server:delete(couch_db:name(AuthDb), [admin_user_ctx()]).


Mime
View raw message