Return-Path: X-Original-To: apmail-couchdb-dev-archive@www.apache.org Delivered-To: apmail-couchdb-dev-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 9D3927B0C for ; Mon, 12 Dec 2011 01:05:44 +0000 (UTC) Received: (qmail 25145 invoked by uid 500); 12 Dec 2011 01:05:44 -0000 Delivered-To: apmail-couchdb-dev-archive@couchdb.apache.org Received: (qmail 25106 invoked by uid 500); 12 Dec 2011 01:05:44 -0000 Mailing-List: contact dev-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 dev@couchdb.apache.org Received: (qmail 25098 invoked by uid 99); 12 Dec 2011 01:05:44 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 12 Dec 2011 01:05:44 +0000 X-ASF-Spam-Status: No, hits=-0.7 required=5.0 tests=RCVD_IN_DNSWL_LOW,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (athena.apache.org: domain of randall.leeds@gmail.com designates 209.85.210.180 as permitted sender) Received: from [209.85.210.180] (HELO mail-iy0-f180.google.com) (209.85.210.180) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 12 Dec 2011 01:05:38 +0000 Received: by iaae16 with SMTP id e16so11131625iaa.11 for ; Sun, 11 Dec 2011 17:05:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :content-type:content-transfer-encoding; bh=0IUe1PZas2qdOsN0X/UEqWxUBdmAT5bo7NqK9+jhY8M=; b=AUySFEKNJS9HaPjajjkZe2VHByo2Kpcb/Y1Tvprtbn0VlRahGxd7e4WPOIe9Hr95AG Gm5AlTj1/HOyH+BghS5WR9TwTInPqvfagCln+tnKn0pvF8ycNh9EV9Ja+2XwVOJQD5nc rNgogGzu40UTiF4pUtbGknDATkXu3e18owlaE= MIME-Version: 1.0 Received: by 10.50.219.135 with SMTP id po7mr8030578igc.11.1323651917698; Sun, 11 Dec 2011 17:05:17 -0800 (PST) Received: by 10.42.142.4 with HTTP; Sun, 11 Dec 2011 17:05:17 -0800 (PST) In-Reply-To: References: <20111210200321.822D45668D@tyr.zones.apache.org> Date: Sun, 11 Dec 2011 17:05:17 -0800 Message-ID: Subject: Re: [2/2] git commit: Fix OAuth authentication with VHosts + URL rewriting From: Randall Leeds To: dev@couchdb.apache.org Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable I'm missing something. Is there any place where pre_rewrite_user_ctx is read? I only see put and erase calls. On Sat, Dec 10, 2011 at 12:11, Filipe David Manana wr= ote: > On Sat, Dec 10, 2011 at 8:08 PM, Benoit Chesneau wr= ote: >> what is the point of wariting in the process registry? > > To make it simple, not adding a new handle_request_int clause to > couch_httpd or a new entry point. > >> >> On Sat, Dec 10, 2011 at 9:03 PM, =C2=A0 wrote: >>> 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 >>> Authored: Sat Dec 10 19:05:52 2011 +0000 >>> Committer: Filipe David Borba Manana >>> Committed: Sat Dec 10 19:40:37 2011 +0000 >>> >>> ---------------------------------------------------------------------- >>> =C2=A0src/couchdb/couch_httpd.erl =C2=A0 =C2=A0 =C2=A0 =C2=A0 | =C2=A0 = =C2=A03 +- >>> =C2=A0src/couchdb/couch_httpd_oauth.erl =C2=A0 | =C2=A0 11 +++- >>> =C2=A0src/couchdb/couch_httpd_rewrite.erl | =C2=A0 =C2=A04 +- >>> =C2=A0test/etap/160-vhosts.t =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0| =C2=A0 89 +++++++++++++++++++++++++++++- >>> =C2=A04 files changed, 102 insertions(+), 5 deletions(-) >>> ---------------------------------------------------------------------- >>> >>> >>> http://git-wip-us.apache.org/repos/asf/couchdb/blob/b86fa1f6/src/couchd= b/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, >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 db_url_handlers =3D DbUrlHandlers, >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 design_url_handlers =3D DesignUrlHandlers, >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 default_fun =3D DefaultFun, >>> - =C2=A0 =C2=A0 =C2=A0 =C2=A0url_handlers =3D UrlHandlers >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0url_handlers =3D UrlHandlers, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0user_ctx =3D erlang:erase(pre_rewrite_user= _ctx) >>> =C2=A0 =C2=A0 }, >>> >>> =C2=A0 =C2=A0 HandlerFun =3D couch_util:dict_find(HandlerKey, UrlHandle= rs, DefaultFun), >>> >>> http://git-wip-us.apache.org/repos/asf/couchdb/blob/b86fa1f6/src/couchd= b/couch_httpd_oauth.erl >>> ---------------------------------------------------------------------- >>> diff --git a/src/couchdb/couch_httpd_oauth.erl b/src/couchdb/couch_http= d_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=3DMochiReq}=3DReq, Fu= n, FailSilently) -> >>> >>> =C2=A0 =C2=A0 % get requested path >>> =C2=A0 =C2=A0 RequestedPath =3D case MochiReq:get_header_value("x-couch= db-requested-path") of >>> - =C2=A0 =C2=A0 =C2=A0 =C2=A0undefined -> MochiReq:get(raw_path); >>> - =C2=A0 =C2=A0 =C2=A0 =C2=A0RequestedPath0 -> RequestedPath0 >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0undefined -> >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case MochiReq:get_header_val= ue("x-couchdb-vhost-path") of >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0undefined -> >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= MochiReq:get(raw_path); >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0VHostPath -> >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= VHostPath >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0end; >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0RequestedPath0 -> >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 RequestedPath0 >>> =C2=A0 =C2=A0 end, >>> =C2=A0 =C2=A0 {_, QueryString, _} =3D mochiweb_util:urlsplit_path(Reque= stedPath), >>> >>> >>> http://git-wip-us.apache.org/repos/asf/couchdb/blob/b86fa1f6/src/couchd= b/couch_httpd_rewrite.erl >>> ---------------------------------------------------------------------- >>> diff --git a/src/couchdb/couch_httpd_rewrite.erl b/src/couchdb/couch_ht= tpd_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{ >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 db_url_handlers= =3D DbUrlHandlers, >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 design_url_hand= lers =3D DesignUrlHandlers, >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 default_fun =3D= DefaultFun, >>> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0url_handlers = =3D UrlHandlers >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0url_handlers = =3D UrlHandlers, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0user_ctx =3D U= serCtx >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 } =3D Req, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0erlang:put(pre_rewrite_user_= ctx, UserCtx), >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 couch_httpd:handle_request_in= t(MochiReq1, DefaultFun, >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 U= rlHandlers, DbUrlHandlers, DesignUrlHandlers) >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 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=3D[<<"= _admin">>]}}. >>> =C2=A0main(_) -> >>> =C2=A0 =C2=A0 test_util:init_code_path(), >>> >>> - =C2=A0 =C2=A0etap:plan(15), >>> + =C2=A0 =C2=A0etap:plan(18), >>> =C2=A0 =C2=A0 case (catch test()) of >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 ok -> >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 etap:end_tests(); >>> @@ -135,9 +135,11 @@ test() -> >>> =C2=A0 =C2=A0 test_vhost_request_path2(), >>> =C2=A0 =C2=A0 test_vhost_request_path3(), >>> =C2=A0 =C2=A0 test_vhost_request_to_root(), >>> + =C2=A0 =C2=A0test_vhost_request_with_oauth(Db), >>> >>> =C2=A0 =C2=A0 %% restart boilerplate >>> =C2=A0 =C2=A0 couch_db:close(Db), >>> + =C2=A0 =C2=A0ok =3D couch_server:delete(couch_db:name(Db), [admin_use= r_ctx()]), >>> =C2=A0 =C2=A0 timer:sleep(3000), >>> =C2=A0 =C2=A0 couch_server_sup:stop(), >>> >>> @@ -301,3 +303,88 @@ test_vhost_request_to_root() -> >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 etap:is(HasCouchDBWelcome, tr= ue, "should allow redirect to /"); >>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 _Else -> etap:is(false, true, <<"ibrowse fa= il">>) >>> =C2=A0 =C2=A0 end. >>> + >>> +test_vhost_request_with_oauth(Db) -> >>> + =C2=A0 =C2=A0{ok, AuthDb} =3D couch_db:create( >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0<<"tap_test_sec_db">>, [admin_user_ctx(), = overwrite]), >>> + =C2=A0 =C2=A0PrevAuthDbName =3D couch_config:get("couch_httpd_auth", = "authentication_db"), >>> + =C2=A0 =C2=A0couch_config:set("couch_httpd_auth", "authentication_db"= , "tap_test_sec_db", false), >>> + =C2=A0 =C2=A0couch_config:set("oauth_token_users", "otoksec1", "joe",= false), >>> + =C2=A0 =C2=A0couch_config:set("oauth_consumer_secrets", "consec1", "f= oo", false), >>> + =C2=A0 =C2=A0couch_config:set("oauth_token_secrets", "otoksec1", "foo= bar", false), >>> + =C2=A0 =C2=A0couch_config:set("couch_httpd_auth", "require_valid_user= ", "true", false), >>> + >>> + =C2=A0 =C2=A0DDoc =3D couch_doc:from_json_obj({[ >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{<<"_id">>, <<"_design/test">>}, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{<<"language">>, <<"javascript">>}, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{<<"rewrites">>, [ >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{[ >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{<<"from">>, <= <"foobar">>}, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{<<"to">>, <<"= _info">>} >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0]} >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0]} >>> + =C2=A0 =C2=A0]}), >>> + =C2=A0 =C2=A0{ok, _} =3D couch_db:update_doc(Db, DDoc, []), >>> + >>> + =C2=A0 =C2=A0RewritePath =3D "/etap-test-db/_design/test/_rewrite/foo= bar", >>> + =C2=A0 =C2=A0ok =3D couch_config:set("vhosts", "oauth-example.com", R= ewritePath, false), >>> + =C2=A0 =C2=A0couch_httpd_vhost:reload(), >>> + >>> + =C2=A0 =C2=A0case ibrowse:send_req(server(), [], get, [], [{host_head= er, "oauth-example.com"}]) of >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{ok, "401", _, Body} -> >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{JsonBody} =3D ejson:decode(= Body), >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0etap:is( >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0couch_util:get= _value(<<"error">>, JsonBody), >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0<<"unauthorize= d">>, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"Request witho= ut OAuth credentials failed"); >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0Error -> >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 etap:bail("Request without OAuth c= redentials did not fail: " ++ >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 couch_util:to_list(E= rror)) >>> + =C2=A0 =C2=A0end, >>> + >>> + =C2=A0 =C2=A0JoeDoc =3D couch_doc:from_json_obj({[ >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{<<"_id">>, <<"org.couchdb.user:joe">>}, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{<<"type">>, <<"user">>}, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{<<"name">>, <<"joe">>}, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{<<"roles">>, []}, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{<<"password_sha">>, <<"fe95df1ca59a9b567b= dca5cbaf8412abd6e06121">>}, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{<<"salt">>, <<"4e170ffeb6f34daecfd814dfb4= 001a73">>} >>> + =C2=A0 =C2=A0]}), >>> + =C2=A0 =C2=A0{ok, _} =3D couch_db:update_doc(AuthDb, JoeDoc, []), >>> + >>> + =C2=A0 =C2=A0Url =3D "http://oauth-example.com/", >>> + =C2=A0 =C2=A0Consumer =3D {"consec1", "foo", hmac_sha1}, >>> + =C2=A0 =C2=A0SignedParams =3D oauth:signed_params( >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0"GET", Url, [], Consumer, "otoksec1", "foo= bar"), >>> + =C2=A0 =C2=A0OAuthUrl =3D oauth:uri(server(), SignedParams), >>> + >>> + =C2=A0 =C2=A0case ibrowse:send_req(OAuthUrl, [], get, [], [{host_head= er, "oauth-example.com"}]) of >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{ok, "200", _, Body2} -> >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{JsonBody2} =3D ejson:decode= (Body2), >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0etap:is(couch_util:get_value= (<<"name">>, JsonBody2), <<"test">>, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"should return= ddoc info with OAuth credentials"); >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0Error2 -> >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 etap:bail("Failed to access vhost = with OAuth credentials: " ++ >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 couch_util:to_list(E= rror2)) >>> + =C2=A0 =C2=A0end, >>> + >>> + =C2=A0 =C2=A0Consumer2 =3D {"consec1", "bad_secret", hmac_sha1}, >>> + =C2=A0 =C2=A0SignedParams2 =3D oauth:signed_params( >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0"GET", Url, [], Consumer2, "otoksec1", "fo= obar"), >>> + =C2=A0 =C2=A0OAuthUrl2 =3D oauth:uri(server(), SignedParams2), >>> + >>> + =C2=A0 =C2=A0case ibrowse:send_req(OAuthUrl2, [], get, [], [{host_hea= der, "oauth-example.com"}]) of >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0{ok, "401", _, Body3} -> >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{JsonBody3} =3D ejson:decode= (Body3), >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0etap:is( >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0couch_util:get= _value(<<"error">>, JsonBody3), >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0<<"unauthorize= d">>, >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"Request with = bad OAuth credentials failed"); >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0Error3 -> >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 etap:bail("Failed to access vhost = with bad OAuth credentials: " ++ >>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 couch_util:to_list(E= rror3)) >>> + =C2=A0 =C2=A0end, >>> + >>> + =C2=A0 =C2=A0couch_config:set("couch_httpd_auth", "authentication_db"= , PrevAuthDbName, false), >>> + =C2=A0 =C2=A0couch_config:set("couch_httpd_auth", "require_valid_user= ", "false", false), >>> + =C2=A0 =C2=A0ok =3D couch_server:delete(couch_db:name(AuthDb), [admin= _user_ctx()]). >>> > > > > -- > Filipe David Manana, > > "Reasonable men adapt themselves to the world. > =C2=A0Unreasonable men adapt the world to themselves. > =C2=A0That's why all progress depends on unreasonable men."