Return-Path: X-Original-To: apmail-couchdb-commits-archive@www.apache.org Delivered-To: apmail-couchdb-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 96E3897D7 for ; Sat, 10 Dec 2011 20:03:14 +0000 (UTC) Received: (qmail 4479 invoked by uid 500); 10 Dec 2011 20:03:14 -0000 Delivered-To: apmail-couchdb-commits-archive@couchdb.apache.org Received: (qmail 4375 invoked by uid 500); 10 Dec 2011 20:03:14 -0000 Mailing-List: contact commits-help@couchdb.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@couchdb.apache.org Delivered-To: mailing list commits@couchdb.apache.org Received: (qmail 4368 invoked by uid 99); 10 Dec 2011 20:03:14 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 10 Dec 2011 20:03:14 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.114] (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 10 Dec 2011 20:03:12 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id BE77656678; Sat, 10 Dec 2011 20:02:52 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: fdmanana@apache.org To: commits@couchdb.apache.org X-Mailer: ASF-Git Admin Mailer Subject: [2/2] git commit: Fix OAuth authentication with VHosts + URL rewriting Message-Id: <20111210200252.BE77656678@tyr.zones.apache.org> Date: Sat, 10 Dec 2011 20:02:52 +0000 (UTC) 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/094cfe79 Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/094cfe79 Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/094cfe79 Branch: refs/heads/master Commit: 094cfe795286b74303ec517c2262e4845428def9 Parents: 8b94951 Author: Filipe David Borba Manana Authored: Fri Nov 25 20:12:36 2011 +0000 Committer: Filipe David Borba Manana Committed: Sat Dec 10 19:40:26 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/094cfe79/src/couchdb/couch_httpd.erl ---------------------------------------------------------------------- diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl index d668f98..97475c5 100644 --- a/src/couchdb/couch_httpd.erl +++ b/src/couchdb/couch_httpd.erl @@ -299,7 +299,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/094cfe79/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/094cfe79/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/094cfe79/test/etap/160-vhosts.t ---------------------------------------------------------------------- diff --git a/test/etap/160-vhosts.t b/test/etap/160-vhosts.t index 6e26b59..94882fe 100755 --- a/test/etap/160-vhosts.t +++ b/test/etap/160-vhosts.t @@ -30,7 +30,7 @@ admin_user_ctx() -> {user_ctx, #user_ctx{roles=[<<"_admin">>]}}. main(_) -> test_util:init_code_path(), - etap:plan(17), + etap:plan(20), case (catch test()) of ok -> etap:end_tests(); @@ -113,9 +113,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(), @@ -282,3 +284,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()]).