You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@couchdb.apache.org by Benoit Chesneau <bc...@gmail.com> on 2011/12/10 21:08:46 UTC

Re: [2/2] git commit: Fix OAuth authentication with VHosts + URL rewriting

what is the point of wariting in the process registry?

On Sat, Dec 10, 2011 at 9:03 PM,  <fd...@apache.org> 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 <fd...@apache.org>
> Authored: Sat Dec 10 19:05:52 2011 +0000
> Committer: Filipe David Borba Manana <fd...@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()]).
>

Re: [2/2] git commit: Fix OAuth authentication with VHosts + URL rewriting

Posted by Filipe David Manana <fd...@apache.org>.
On Monday, December 12, 2011, Randall Leeds <ra...@gmail.com> wrote:
> I'm missing something. Is there any place where pre_rewrite_user_ctx
> is read? I only see put and erase calls.

Yes. You miss the fact that erase returns the current value (or undefined).
>
> On Sat, Dec 10, 2011 at 12:11, Filipe David Manana <fd...@apache.org>
wrote:
>> On Sat, Dec 10, 2011 at 8:08 PM, Benoit Chesneau <bc...@gmail.com>
wrote:
>>> 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,  <fd...@apache.org> 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 <fd...@apache.org>
>>>> Authored: Sat Dec 10 19:05:52 2011 +0000
>>>> Committer: Filipe David Borba Manana <fd...@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),
>>>>

Re: [2/2] git commit: Fix OAuth authentication with VHosts + URL rewriting

Posted by Randall Leeds <ra...@gmail.com>.
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 <fd...@apache.org> wrote:
> On Sat, Dec 10, 2011 at 8:08 PM, Benoit Chesneau <bc...@gmail.com> wrote:
>> 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,  <fd...@apache.org> 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 <fd...@apache.org>
>>> Authored: Sat Dec 10 19:05:52 2011 +0000
>>> Committer: Filipe David Borba Manana <fd...@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()]).
>>>
>
>
>
> --
> Filipe David Manana,
>
> "Reasonable men adapt themselves to the world.
>  Unreasonable men adapt the world to themselves.
>  That's why all progress depends on unreasonable men."

Re: [2/2] git commit: Fix OAuth authentication with VHosts + URL rewriting

Posted by Filipe David Manana <fd...@apache.org>.
On Sat, Dec 10, 2011 at 8:08 PM, Benoit Chesneau <bc...@gmail.com> wrote:
> 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,  <fd...@apache.org> 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 <fd...@apache.org>
>> Authored: Sat Dec 10 19:05:52 2011 +0000
>> Committer: Filipe David Borba Manana <fd...@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()]).
>>



-- 
Filipe David Manana,

"Reasonable men adapt themselves to the world.
 Unreasonable men adapt the world to themselves.
 That's why all progress depends on unreasonable men."