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."