diff options
author | Joan Touzet <joant@atypical.net> | 2017-07-08 01:28:39 -0400 |
---|---|---|
committer | Joan Touzet <wohali@users.noreply.github.com> | 2017-07-09 18:06:00 -0400 |
commit | 34b803a35531f305e05080c35c3e9af9e3b9dbf2 (patch) | |
tree | 8ef25bac1891c68449ec156b07d885516ceb63e1 | |
parent | 017d76ff5b1b72e97699b957cc05a3fcce98d6c0 (diff) | |
download | couchdb-34b803a35531f305e05080c35c3e9af9e3b9dbf2.tar.gz |
Remove deprecated OAuth 1.0 implementation
Helps resolve issue #656. Implementation broken since bigcouch merge.
Replicator oauth hooks are left in place for future work towards
adding cookie-based authentication support.
-rw-r--r-- | LICENSE | 25 | ||||
-rw-r--r-- | NOTICE | 4 | ||||
-rwxr-xr-x | build-aux/print-committerlist.sh | 2 | ||||
-rw-r--r-- | license.skip | 2 | ||||
-rw-r--r-- | rebar.config.script | 4 | ||||
-rw-r--r-- | rel/overlay/etc/default.ini | 29 | ||||
-rw-r--r-- | rel/reltool.config | 2 | ||||
-rw-r--r-- | src/couch/include/couch_js_functions.hrl | 18 | ||||
-rw-r--r-- | src/couch/src/couch.app.src | 1 | ||||
-rw-r--r-- | src/couch/src/couch.erl | 1 | ||||
-rw-r--r-- | src/couch/src/couch_httpd_handlers.erl | 1 | ||||
-rw-r--r-- | src/couch/src/couch_httpd_oauth.erl | 391 | ||||
-rw-r--r-- | src/couch/src/test_util.erl | 1 | ||||
-rw-r--r-- | src/couch/test/chttpd_endpoints_tests.erl | 1 | ||||
-rw-r--r-- | src/couch/test/couchdb_auth_tests.erl | 2 | ||||
-rw-r--r-- | src/couch/test/couchdb_vhosts_tests.erl | 139 | ||||
-rwxr-xr-x | src/couch/test/fixtures/os_daemon_configer.escript | 1 | ||||
-rw-r--r-- | test/javascript/oauth.js | 511 | ||||
-rwxr-xr-x | test/javascript/run | 1 | ||||
-rw-r--r-- | test/javascript/tests/oauth_users_db.js | 168 | ||||
-rw-r--r-- | test/javascript/tests/replicator_db_security.js | 28 |
21 files changed, 5 insertions, 1327 deletions
@@ -272,31 +272,6 @@ For the src/ibrowse component: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -For the src/oauth component: - - Copyright the authors and contributors. All rights reserved. - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - For the test/etap/etap.erl component: Copyright (c) 2008-2009 Nick Gerakines <nick@gerakines.net> @@ -38,10 +38,6 @@ This product also includes the following third-party components: Copyright 2005-2012, Chandrashekhar Mullaparthi - * Erlang OAuth (http://github.com/tim/erlang-oauth) - - Copyright 2012, the authors and contributors - * ETap (http://github.com/ngerakines/etap/) Copyright 2009, Nick Gerakines <nick@gerakines.net> diff --git a/build-aux/print-committerlist.sh b/build-aux/print-committerlist.sh index 7fbb96b7f..f6abc4c78 100755 --- a/build-aux/print-committerlist.sh +++ b/build-aux/print-committerlist.sh @@ -40,7 +40,7 @@ function get_contributors { function print_comitter_list { # list of external repos that we exclude - local EXCLUDE=("bear" "folsom" "goldrush" "ibrowse" "jiffy" "lager" "meck" "mochiweb" "oauth" "snappy") + local EXCLUDE=("bear" "folsom" "goldrush" "ibrowse" "jiffy" "lager" "meck" "mochiweb" "snappy") local EXCLUDE=$(printf "\|%s" "${EXCLUDE[@]}") local EXCLUDE=${EXCLUDE:2} local SUBREPOS=$(ls src/ | grep -v "$EXCLUDE") diff --git a/license.skip b/license.skip index c1bd0e901..143639251 100644 --- a/license.skip +++ b/license.skip @@ -133,7 +133,6 @@ ^src/couchjs-node/README.md ^src/couchjs-node/Makefile ^src/couchjs-node/Makefile.in -^src/erlang-oauth/.* ^src/couch_dbupdates ^src/ejson/.* ^src/etap/.* @@ -188,7 +187,6 @@ ^src/twig/ebin/twig.app ^src/twig/README.md ^src/twig/src/trunc_io.erl -^src/oauth/.* ^stamp-h1 ^test/Makefile ^test/Makefile.in diff --git a/rebar.config.script b/rebar.config.script index 5d0e4e85a..ebf4d94c8 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -64,10 +64,8 @@ DepDescs = [ {ibrowse, "ibrowse", "4af2d408607874d124414ac45df1edbe3961d1cd"}, {jiffy, "jiffy", "1acdd1ac389dff3d763589f64331227ae594f3b7"}, {mochiweb, "mochiweb", "bd6ae7cbb371666a1f68115056f7b30d13765782"}, -{meck, "meck", {tag, "0.8.2"}}, +{meck, "meck", {tag, "0.8.2"}} -%% Deprecated -{oauth, "oauth", "099057a98e41f3aff91e77e3cf496d6c6fd901df"} ], diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini index 30a03bc87..eaa0801b7 100644 --- a/rel/overlay/etc/default.ini +++ b/rel/overlay/etc/default.ini @@ -76,10 +76,10 @@ delete_dbs = false [httpd] port = {{backend_port}} bind_address = 127.0.0.1 -authentication_handlers = {couch_httpd_oauth, oauth_authentication_handler}, {couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler} +authentication_handlers = {couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler} default_handler = {couch_httpd_db, handle_request} secure_rewrites = true -vhost_global_handlers = _utils, _uuids, _session, _oauth, _users +vhost_global_handlers = _utils, _uuids, _session, _users allow_jsonp = false ; Options for the MochiWeb HTTP server. ;server_options = [{backlog, 128}, {acceptor_pool_size, 16}] @@ -196,30 +196,6 @@ credentials = false ; List of hosts separated by a comma. * means accept all ; hosts = -[couch_httpd_oauth] -; If set to 'true', oauth token and consumer secrets will be looked up -; in the authentication database (_users). These secrets are stored in -; a top level property named "oauth" in user documents. Example: -; { -; "_id": "org.couchdb.user:joe", -; "type": "user", -; "name": "joe", -; "password_sha": "fe95df1ca59a9b567bdca5cbaf8412abd6e06121", -; "salt": "4e170ffeb6f34daecfd814dfb4001a73" -; "roles": ["foo", "bar"], -; "oauth": { -; "consumer_keys": { -; "consumerKey1": "key1Secret", -; "consumerKey2": "key2Secret" -; }, -; "tokens": { -; "token1": "token1Secret", -; "token2": "token2Secret" -; } -; } -; } -use_users_db = false - [query_servers] javascript = {{prefix}}/bin/couchjs {{prefix}}/share/server/main.js coffeescript = {{prefix}}/bin/couchjs {{prefix}}/share/server/main-coffee.js @@ -266,7 +242,6 @@ _uuids = {couch_httpd_misc_handlers, handle_uuids_req} _restart = {couch_httpd_misc_handlers, handle_restart_req} _stats = {couch_stats_httpd, handle_stats_req} _session = {couch_httpd_auth, handle_session_req} -_oauth = {couch_httpd_oauth, handle_oauth_req} _plugins = {couch_plugins_httpd, handle_req} _system = {chttpd_misc, handle_system_req} diff --git a/rel/reltool.config b/rel/reltool.config index 135d38676..762848f22 100644 --- a/rel/reltool.config +++ b/rel/reltool.config @@ -53,7 +53,6 @@ mango, mem3, mochiweb, - oauth, rexi, setup, snappy @@ -107,7 +106,6 @@ {app, mango, [{incl_cond, include}]}, {app, mem3, [{incl_cond, include}]}, {app, mochiweb, [{incl_cond, include}]}, - {app, oauth, [{incl_cond, include}]}, {app, rexi, [{incl_cond, include}]}, {app, setup, [{incl_cond, include}]}, {app, snappy, [{incl_cond, include}]} diff --git a/src/couch/include/couch_js_functions.hrl b/src/couch/include/couch_js_functions.hrl index 0ae6427e4..6851718ff 100644 --- a/src/couch/include/couch_js_functions.hrl +++ b/src/couch/include/couch_js_functions.hrl @@ -150,21 +150,3 @@ } } ">>). - - --define(OAUTH_MAP_FUN, <<" - function(doc) { - if (doc.type === 'user' && doc.oauth && doc.oauth.consumer_keys) { - for (var consumer_key in doc.oauth.consumer_keys) { - for (var token in doc.oauth.tokens) { - var obj = { - 'consumer_secret': doc.oauth.consumer_keys[consumer_key], - 'token_secret': doc.oauth.tokens[token], - 'username': doc.name - }; - emit([consumer_key, token], obj); - } - } - } - } -">>). diff --git a/src/couch/src/couch.app.src b/src/couch/src/couch.app.src index cf3dc795d..8fc0d89ff 100644 --- a/src/couch/src/couch.app.src +++ b/src/couch/src/couch.app.src @@ -39,7 +39,6 @@ % Upstream deps ibrowse, mochiweb, - oauth, % ASF deps couch_epi, diff --git a/src/couch/src/couch.erl b/src/couch/src/couch.erl index 44cea065a..fd5c9e101 100644 --- a/src/couch/src/couch.erl +++ b/src/couch/src/couch.erl @@ -23,7 +23,6 @@ deps() -> crypto, public_key, ssl, - oauth, ibrowse, mochiweb, config, diff --git a/src/couch/src/couch_httpd_handlers.erl b/src/couch/src/couch_httpd_handlers.erl index b8ee3efd9..e64287566 100644 --- a/src/couch/src/couch_httpd_handlers.erl +++ b/src/couch/src/couch_httpd_handlers.erl @@ -14,7 +14,6 @@ -export([url_handler/1, db_handler/1, design_handler/1]). -url_handler(<<"_oauth">>) -> fun couch_httpd_oauth:handle_oauth_req/1; url_handler(_) -> no_match. db_handler(_) -> no_match. diff --git a/src/couch/src/couch_httpd_oauth.erl b/src/couch/src/couch_httpd_oauth.erl deleted file mode 100644 index 03107525b..000000000 --- a/src/couch/src/couch_httpd_oauth.erl +++ /dev/null @@ -1,391 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - --module(couch_httpd_oauth). - --include_lib("couch/include/couch_db.hrl"). --include_lib("couch/include/couch_js_functions.hrl"). - --export([oauth_authentication_handler/1, handle_oauth_req/1]). - --define(OAUTH_DDOC_ID, <<"_design/oauth">>). --define(OAUTH_VIEW_NAME, <<"oauth_credentials">>). - --record(callback_params, { - consumer, - token, - token_secret, - url, - signature, - params, - username -}). - -% OAuth auth handler using per-node user db -oauth_authentication_handler(Req) -> - serve_oauth(Req, fun oauth_auth_callback/2, true). - - -oauth_auth_callback(Req, #callback_params{token_secret = undefined}) -> - couch_httpd:send_error( - Req, 400, <<"invalid_token">>, <<"Invalid OAuth token.">>); - -oauth_auth_callback(#httpd{mochi_req = MochiReq} = Req, CbParams) -> - Method = atom_to_list(MochiReq:get(method)), - #callback_params{ - consumer = Consumer, - token = Token, - token_secret = TokenSecret, - url = Url, - signature = Sig, - params = Params, - username = User - } = CbParams, - case oauth:verify(Sig, Method, Url, Params, Consumer, TokenSecret) of - true -> - set_user_ctx(Req, User); - false -> - couch_log:debug("OAuth handler: signature verification failed for" - " user `~p`~n" - "Received signature is `~p`~n" - "HTTP method is `~p`~n" - "URL is `~p`~n" - "Parameters are `~p`~n" - "Consumer is `~p`, token secret is `~p`~n" - "Expected signature was `~p`~n", - [User, Sig, Method, Url, Params, Consumer, TokenSecret, - oauth:sign(Method, Url, Params, Consumer, Token, - TokenSecret)]), - Req - end. - - -% Look up the consumer key and get the roles to give the consumer -set_user_ctx(_Req, undefined) -> - throw({bad_request, unknown_oauth_token}); -set_user_ctx(Req, Name) -> - case couch_auth_cache:get_user_creds(Name) of - nil -> - couch_log:debug("OAuth handler: user `~p` credentials not found", - [Name]), - Req; - {ok, User, _AuthCtx} -> - Roles = couch_util:get_value(<<"roles">>, User, []), - Req#httpd{user_ctx=#user_ctx{name=Name, roles=Roles}} - end. - -% OAuth request_token -handle_oauth_req(#httpd{path_parts=[_OAuth, <<"request_token">>], method=Method}=Req1) -> - serve_oauth(Req1, fun(Req, CbParams) -> - #callback_params{ - consumer = Consumer, - token_secret = TokenSecret, - url = Url, - signature = Sig, - params = Params - } = CbParams, - case oauth:verify( - Sig, atom_to_list(Method), Url, Params, Consumer, TokenSecret) of - true -> - ok(Req, <<"oauth_token=requestkey&oauth_token_secret=requestsecret">>); - false -> - invalid_signature(Req) - end - end, false); -handle_oauth_req(#httpd{path_parts=[_OAuth, <<"authorize">>]}=Req) -> - {ok, serve_oauth_authorize(Req)}; -handle_oauth_req(#httpd{path_parts=[_OAuth, <<"access_token">>], method='GET'}=Req1) -> - serve_oauth(Req1, fun(Req, CbParams) -> - #callback_params{ - consumer = Consumer, - token = Token, - url = Url, - signature = Sig, - params = Params - } = CbParams, - case Token of - "requestkey" -> - case oauth:verify( - Sig, "GET", Url, Params, Consumer, "requestsecret") of - true -> - ok(Req, - <<"oauth_token=accesskey&oauth_token_secret=accesssecret">>); - false -> - invalid_signature(Req) - end; - _ -> - couch_httpd:send_error( - Req, 400, <<"invalid_token">>, <<"Invalid OAuth token.">>) - end - end, false); -handle_oauth_req(#httpd{path_parts=[_OAuth, <<"access_token">>]}=Req) -> - couch_httpd:send_method_not_allowed(Req, "GET"). - -invalid_signature(Req) -> - couch_httpd:send_error(Req, 400, <<"invalid_signature">>, <<"Invalid signature value.">>). - -% This needs to be protected i.e. force user to login using HTTP Basic Auth or form-based login. -serve_oauth_authorize(#httpd{method=Method}=Req1) -> - case Method of - 'GET' -> - % Confirm with the User that they want to authenticate the Consumer - serve_oauth(Req1, fun(Req, CbParams) -> - #callback_params{ - consumer = Consumer, - token_secret = TokenSecret, - url = Url, - signature = Sig, - params = Params - } = CbParams, - case oauth:verify( - Sig, "GET", Url, Params, Consumer, TokenSecret) of - true -> - ok(Req, <<"oauth_token=requestkey&", - "oauth_token_secret=requestsecret">>); - false -> - invalid_signature(Req) - end - end, false); - 'POST' -> - % If the User has confirmed, we direct the User back to the Consumer with a verification code - serve_oauth(Req1, fun(Req, CbParams) -> - #callback_params{ - consumer = Consumer, - token_secret = TokenSecret, - url = Url, - signature = Sig, - params = Params - } = CbParams, - case oauth:verify( - Sig, "POST", Url, Params, Consumer, TokenSecret) of - true -> - %redirect(oauth_callback, oauth_token, oauth_verifier), - ok(Req, <<"oauth_token=requestkey&", - "oauth_token_secret=requestsecret">>); - false -> - invalid_signature(Req) - end - end, false); - _ -> - couch_httpd:send_method_not_allowed(Req1, "GET,POST") - end. - -serve_oauth(#httpd{mochi_req=MochiReq}=Req, Fun, FailSilently) -> - % 1. In the HTTP Authorization header as defined in OAuth HTTP Authorization Scheme. - % 2. As the HTTP POST request body with a content-type of application/x-www-form-urlencoded. - % 3. Added to the URLs in the query part (as defined by [RFC3986] section 3). - AuthHeader = case MochiReq:get_header_value("authorization") of - undefined -> - ""; - Else -> - [Head | Tail] = re:split(Else, "\\s", [{parts, 2}, {return, list}]), - case [string:to_lower(Head) | Tail] of - ["oauth", Rest] -> Rest; - _ -> "" - end - end, - HeaderParams = oauth:header_params_decode(AuthHeader), - %Realm = couch_util:get_value("realm", HeaderParams), - - % get requested path - RequestedPath = case MochiReq:get_header_value("x-couchdb-requested-path") of - 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), - - Params = proplists:delete("realm", HeaderParams) ++ mochiweb_util:parse_qs(QueryString), - - couch_log:debug("OAuth Params: ~p", [Params]), - case couch_util:get_value("oauth_version", Params, "1.0") of - "1.0" -> - case couch_util:get_value("oauth_consumer_key", Params, undefined) of - undefined -> - case FailSilently of - true -> Req; - false -> couch_httpd:send_error(Req, 400, <<"invalid_consumer">>, <<"Invalid consumer.">>) - end; - ConsumerKey -> - Url = couch_httpd:absolute_uri(Req, RequestedPath), - case get_callback_params(ConsumerKey, Params, Url) of - {ok, CallbackParams} -> - Fun(Req, CallbackParams); - invalid_consumer_token_pair -> - couch_httpd:send_error( - Req, 400, - <<"invalid_consumer_token_pair">>, - <<"Invalid consumer and token pair.">>); - {error, {Error, Reason}} -> - couch_httpd:send_error(Req, 400, Error, Reason) - end - end; - _ -> - couch_httpd:send_error(Req, 400, <<"invalid_oauth_version">>, <<"Invalid OAuth version.">>) - end. - - -get_callback_params(ConsumerKey, Params, Url) -> - Token = couch_util:get_value("oauth_token", Params), - SigMethod = sig_method(Params), - CbParams0 = #callback_params{ - token = Token, - signature = couch_util:get_value("oauth_signature", Params), - params = proplists:delete("oauth_signature", Params), - url = Url - }, - case oauth_credentials_info(Token, ConsumerKey) of - nil -> - invalid_consumer_token_pair; - {error, _} = Err -> - Err; - {OauthCreds} -> - User = couch_util:get_value(<<"username">>, OauthCreds, []), - ConsumerSecret = ?b2l(couch_util:get_value( - <<"consumer_secret">>, OauthCreds, <<>>)), - TokenSecret = ?b2l(couch_util:get_value( - <<"token_secret">>, OauthCreds, <<>>)), - case (User =:= []) orelse (ConsumerSecret =:= []) orelse - (TokenSecret =:= []) of - true -> - invalid_consumer_token_pair; - false -> - CbParams = CbParams0#callback_params{ - consumer = {ConsumerKey, ConsumerSecret, SigMethod}, - token_secret = TokenSecret, - username = User - }, - couch_log:debug("Got OAuth credentials, for ConsumerKey `~p` and " - "Token `~p`, from the views, User: `~p`, " - "ConsumerSecret: `~p`, TokenSecret: `~p`", - [ConsumerKey, Token, User, ConsumerSecret, - TokenSecret]), - {ok, CbParams} - end - end. - - -sig_method(Params) -> - sig_method_1(couch_util:get_value("oauth_signature_method", Params)). -sig_method_1("PLAINTEXT") -> - plaintext; -% sig_method_1("RSA-SHA1") -> -% rsa_sha1; -sig_method_1("HMAC-SHA1") -> - hmac_sha1; -sig_method_1(_) -> - undefined. - - -ok(#httpd{mochi_req=MochiReq}, Body) -> - {ok, MochiReq:respond({200, [], Body})}. - - -oauth_credentials_info(Token, ConsumerKey) -> - case use_auth_db() of - {ok, Db} -> - Result = case query_oauth_view(Db, [?l2b(ConsumerKey), ?l2b(Token)]) of - [] -> - nil; - [Creds] -> - Creds; - [_ | _] -> - Reason = iolist_to_binary( - io_lib:format("Found multiple OAuth credentials for the pair " - " (consumer_key: `~p`, token: `~p`)", [ConsumerKey, Token])), - {error, {<<"oauth_token_consumer_key_pair">>, Reason}} - end, - couch_db:close(Db), - Result; - nil -> - { - case config:get("oauth_consumer_secrets", ConsumerKey) of - undefined -> []; - ConsumerSecret -> [{<<"consumer_secret">>, ?l2b(ConsumerSecret)}] - end - ++ - case config:get("oauth_token_secrets", Token) of - undefined -> []; - TokenSecret -> [{<<"token_secret">>, ?l2b(TokenSecret)}] - end - ++ - case config:get("oauth_token_users", Token) of - undefined -> []; - User -> [{<<"username">>, ?l2b(User)}] - end - } - end. - - -use_auth_db() -> - case config:get("couch_httpd_oauth", "use_users_db", "false") of - "false" -> - nil; - "true" -> - AuthDb = open_auth_db(), - {ok, _AuthDb2} = ensure_oauth_views_exist(AuthDb) - end. - - -open_auth_db() -> - DbName = ?l2b(config:get("couch_httpd_auth", "authentication_db")), - {ok, AuthDb} = couch_db:open_int(DbName, [?ADMIN_CTX]), - AuthDb. - - -ensure_oauth_views_exist(AuthDb) -> - case couch_db:open_doc(AuthDb, ?OAUTH_DDOC_ID, []) of - {ok, _DDoc} -> - {ok, AuthDb}; - _ -> - {ok, DDoc} = get_oauth_ddoc(), - {ok, _Rev} = couch_db:update_doc(AuthDb, DDoc, []), - {ok, _AuthDb2} = couch_db:reopen(AuthDb) - end. - - -get_oauth_ddoc() -> - Json = {[ - {<<"_id">>, ?OAUTH_DDOC_ID}, - {<<"language">>, <<"javascript">>}, - {<<"views">>, - {[ - {?OAUTH_VIEW_NAME, - {[ - {<<"map">>, ?OAUTH_MAP_FUN} - ]} - } - ]} - } - ]}, - {ok, couch_doc:from_json_obj(Json)}. - - -query_oauth_view(Db, Key) -> - ViewOptions = [ - {start_key, Key}, - {end_key, Key} - ], - Callback = fun({row, Row}, Acc) -> - {ok, [couch_util:get_value(value, Row) | Acc]}; - (_, Acc) -> - {ok, Acc} - end, - {ok, Result} = couch_mrview:query_view( - Db, ?OAUTH_DDOC_ID, ?OAUTH_VIEW_NAME, ViewOptions, Callback, []), - Result. diff --git a/src/couch/src/test_util.erl b/src/couch/src/test_util.erl index 54fefd50e..e652dd9b3 100644 --- a/src/couch/src/test_util.erl +++ b/src/couch/src/test_util.erl @@ -47,7 +47,6 @@ init_code_path() -> Paths = [ "couchdb", "jiffy", - "oauth", "ibrowse", "mochiweb", "snappy" diff --git a/src/couch/test/chttpd_endpoints_tests.erl b/src/couch/test/chttpd_endpoints_tests.erl index 06de1e923..715576713 100644 --- a/src/couch/test/chttpd_endpoints_tests.erl +++ b/src/couch/test/chttpd_endpoints_tests.erl @@ -47,7 +47,6 @@ handlers(url_handler) -> {<<"_replicate">>, chttpd_misc, handle_replicate_req}, {<<"_uuids">>, chttpd_misc, handle_uuids_req}, {<<"_session">>, chttpd_auth, handle_session_req}, - {<<"_oauth">>, couch_httpd_oauth, handle_oauth_req}, {<<"_up">>, chttpd_misc, handle_up_req}, {<<"_membership">>, mem3_httpd, handle_membership_req}, {<<"_db_updates">>, global_changes_httpd, handle_global_changes_req}, diff --git a/src/couch/test/couchdb_auth_tests.erl b/src/couch/test/couchdb_auth_tests.erl index b2a490fed..ed2c064de 100644 --- a/src/couch/test/couchdb_auth_tests.erl +++ b/src/couch/test/couchdb_auth_tests.erl @@ -68,7 +68,7 @@ should_not_return_authenticated_field(_PortType, Url) -> end). should_return_list_of_handlers(backdoor, Url) -> - ?_assertEqual([<<"oauth">>,<<"cookie">>,<<"default">>], + ?_assertEqual([<<"cookie">>,<<"default">>], begin couch_util:get_nested_json_value(session(Url), [ <<"info">>, <<"authentication_handlers">>]) diff --git a/src/couch/test/couchdb_vhosts_tests.erl b/src/couch/test/couchdb_vhosts_tests.erl index d1da0635a..dfac73cb3 100644 --- a/src/couch/test/couchdb_vhosts_tests.erl +++ b/src/couch/test/couchdb_vhosts_tests.erl @@ -59,42 +59,6 @@ setup() -> Url = "http://" ++ Addr ++ ":" ++ Port, {Url, ?b2l(DbName)}. -setup_oauth() -> - DbName = ?tempdb(), - {ok, Db} = couch_db:create(DbName, [?ADMIN_CTX]), - - config:set("couch_httpd_auth", "authentication_db", - ?b2l(?tempdb()), false), - config:set("oauth_token_users", "otoksec1", "joe", false), - config:set("oauth_consumer_secrets", "consec1", "foo", false), - config:set("oauth_token_secrets", "otoksec1", "foobar", false), - config:set("couchdb", "default_security", "everyone", false), - config:set("couch_httpd_auth", "require_valid_user", "true", false), - - ok = config:set( - "vhosts", "oauth-example.com", - "/" ++ ?b2l(DbName) ++ "/_design/test/_rewrite/foobar", false), - - DDoc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"_design/test">>}, - {<<"language">>, <<"javascript">>}, - {<<"rewrites">>, [ - {[ - {<<"from">>, <<"foobar">>}, - {<<"to">>, <<"_info">>} - ]} - ]} - ]}), - {ok, _} = couch_db:update_doc(Db, DDoc, []), - - couch_db:ensure_full_commit(Db), - couch_db:close(Db), - - Addr = config:get("httpd", "bind_address", "127.0.0.1"), - Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)), - Url = "http://" ++ Addr ++ ":" ++ Port, - {Url, ?b2l(DbName)}. - teardown({_, DbName}) -> ok = couch_server:delete(?l2b(DbName), []), ok. @@ -127,25 +91,6 @@ vhosts_test_() -> } }. -oauth_test_() -> - { - "Virtual Hosts OAuth tests", - { - setup, - fun test_util:start_couch/0, fun test_util:stop_couch/1, - { - foreach, - fun setup_oauth/0, fun teardown/1, - [ - fun should_require_auth/1, - fun should_succeed_oauth/1, - fun should_fail_oauth_with_wrong_credentials/1 - ] - } - } - }. - - should_return_database_info({Url, DbName}) -> ?_test(begin ok = config:set("vhosts", "example.com", "/" ++ DbName, false), @@ -351,90 +296,6 @@ should_return_path_for_vhost_with_wildcard_host({Url, DbName}) -> end end). -should_require_auth({Url, _}) -> - ?_test(begin - case test_request:get(Url, [], [{host_header, "oauth-example.com"}]) of - {ok, Code, _, Body} -> - ?assertEqual(401, Code), - {JsonBody} = jiffy:decode(Body), - ?assertEqual(<<"unauthorized">>, - couch_util:get_value(<<"error">>, JsonBody)); - Else -> - erlang:error({assertion_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {reason, ?iofmt("Request failed: ~p", [Else])}]}) - end - end). - -should_succeed_oauth({Url, _}) -> - ?_test(begin - AuthDbName = config:get("couch_httpd_auth", "authentication_db"), - JoeDoc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"org.couchdb.user:joe">>}, - {<<"type">>, <<"user">>}, - {<<"name">>, <<"joe">>}, - {<<"roles">>, []}, - {<<"password_sha">>, <<"fe95df1ca59a9b567bdca5cbaf8412abd6e06121">>}, - {<<"salt">>, <<"4e170ffeb6f34daecfd814dfb4001a73">>} - ]}), - {ok, AuthDb} = couch_db:open_int(?l2b(AuthDbName), [?ADMIN_CTX]), - {ok, _} = couch_db:update_doc(AuthDb, JoeDoc, [?ADMIN_CTX]), - - Host = "oauth-example.com", - Consumer = {"consec1", "foo", hmac_sha1}, - SignedParams = oauth:sign( - "GET", "http://" ++ Host ++ "/", [], Consumer, "otoksec1", "foobar"), - OAuthUrl = oauth:uri(Url, SignedParams), - - case test_request:get(OAuthUrl, [], [{host_header, Host}]) of - {ok, Code, _, Body} -> - ?assertEqual(200, Code), - {JsonBody} = jiffy:decode(Body), - ?assertEqual(<<"test">>, - couch_util:get_value(<<"name">>, JsonBody)); - Else -> - erlang:error({assertion_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {reason, ?iofmt("Request failed: ~p", [Else])}]}) - end - end). - -should_fail_oauth_with_wrong_credentials({Url, _}) -> - ?_test(begin - AuthDbName = config:get("couch_httpd_auth", "authentication_db"), - JoeDoc = couch_doc:from_json_obj({[ - {<<"_id">>, <<"org.couchdb.user:joe">>}, - {<<"type">>, <<"user">>}, - {<<"name">>, <<"joe">>}, - {<<"roles">>, []}, - {<<"password_sha">>, <<"fe95df1ca59a9b567bdca5cbaf8412abd6e06121">>}, - {<<"salt">>, <<"4e170ffeb6f34daecfd814dfb4001a73">>} - ]}), - {ok, AuthDb} = couch_db:open_int(?l2b(AuthDbName), [?ADMIN_CTX]), - {ok, _} = couch_db:update_doc(AuthDb, JoeDoc, [?ADMIN_CTX]), - - Host = "oauth-example.com", - Consumer = {"consec1", "bad_secret", hmac_sha1}, - SignedParams = oauth:sign( - "GET", "http://" ++ Host ++ "/", [], Consumer, "otoksec1", "foobar"), - OAuthUrl = oauth:uri(Url, SignedParams), - - case test_request:get(OAuthUrl, [], [{host_header, Host}]) of - {ok, Code, _, Body} -> - ?assertEqual(401, Code), - {JsonBody} = jiffy:decode(Body), - ?assertEqual(<<"unauthorized">>, - couch_util:get_value(<<"error">>, JsonBody)); - Else -> - erlang:error({assertion_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {reason, ?iofmt("Request failed: ~p", [Else])}]}) - end - end). - ensure_index_file() -> Body = <<"<!DOCTYPE html>\n<html>\n<body>\nHello world\n</body>\n</html>">>, file:write_file(filename:join([?TEMPDIR, "index.html"]), Body). diff --git a/src/couch/test/fixtures/os_daemon_configer.escript b/src/couch/test/fixtures/os_daemon_configer.escript index 3e02ef971..f146b8314 100755 --- a/src/couch/test/fixtures/os_daemon_configer.escript +++ b/src/couch/test/fixtures/os_daemon_configer.escript @@ -88,7 +88,6 @@ init_code_path() -> Paths = [ "couchdb", "jiffy", - "erlang-oauth", "ibrowse", "mochiweb", "snappy" diff --git a/test/javascript/oauth.js b/test/javascript/oauth.js deleted file mode 100644 index ada00a275..000000000 --- a/test/javascript/oauth.js +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright 2008 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Here's some JavaScript software for implementing OAuth. - - This isn't as useful as you might hope. OAuth is based around - allowing tools and websites to talk to each other. However, - JavaScript running in web browsers is hampered by security - restrictions that prevent code running on one website from - accessing data stored or served on another. - - Before you start hacking, make sure you understand the limitations - posed by cross-domain XMLHttpRequest. - - On the bright side, some platforms use JavaScript as their - language, but enable the programmer to access other web sites. - Examples include Google Gadgets, and Microsoft Vista Sidebar. - For those platforms, this library should come in handy. -*/ - -// The HMAC-SHA1 signature method calls b64_hmac_sha1, defined by -// http://pajhome.org.uk/crypt/md5/sha1.js - -/* An OAuth message is represented as an object like this: - {method: "GET", action: "http://server.com/path", parameters: ...} - - The parameters may be either a map {name: value, name2: value2} - or an Array of name-value pairs [[name, value], [name2, value2]]. - The latter representation is more powerful: it supports parameters - in a specific sequence, or several parameters with the same name; - for example [["a", 1], ["b", 2], ["a", 3]]. - - Parameter names and values are NOT percent-encoded in an object. - They must be encoded before transmission and decoded after reception. - For example, this message object: - {method: "GET", action: "http://server/path", parameters: {p: "x y"}} - ... can be transmitted as an HTTP request that begins: - GET /path?p=x%20y HTTP/1.0 - (This isn't a valid OAuth request, since it lacks a signature etc.) - Note that the object "x y" is transmitted as x%20y. To encode - parameters, you can call OAuth.addToURL, OAuth.formEncode or - OAuth.getAuthorization. - - This message object model harmonizes with the browser object model for - input elements of an form, whose value property isn't percent encoded. - The browser encodes each value before transmitting it. For example, - see consumer.setInputs in example/consumer.js. - */ -var OAuth; if (OAuth == null) OAuth = {}; - -OAuth.setProperties = function setProperties(into, from) { - if (into != null && from != null) { - for (var key in from) { - into[key] = from[key]; - } - } - return into; -} - -OAuth.setProperties(OAuth, // utility functions -{ - percentEncode: function percentEncode(s) { - if (s == null) { - return ""; - } - if (s instanceof Array) { - var e = ""; - for (var i = 0; i < s.length; ++i) { - if (e != "") e += '&'; - e += percentEncode(s[i]); - } - return e; - } - s = encodeURIComponent(s); - // Now replace the values which encodeURIComponent doesn't do - // encodeURIComponent ignores: - _ . ! ~ * ' ( ) - // OAuth dictates the only ones you can ignore are: - _ . ~ - // Source: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Functions:encodeURIComponent - s = s.replace(/\!/g, "%21"); - s = s.replace(/\*/g, "%2A"); - s = s.replace(/\'/g, "%27"); - s = s.replace(/\(/g, "%28"); - s = s.replace(/\)/g, "%29"); - return s; - } -, - decodePercent: function decodePercent(s) { - if (s != null) { - // Handle application/x-www-form-urlencoded, which is defined by - // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 - s = s.replace(/\+/g, " "); - } - return decodeURIComponent(s); - } -, - /** Convert the given parameters to an Array of name-value pairs. */ - getParameterList: function getParameterList(parameters) { - if (parameters == null) { - return []; - } - if (typeof parameters != "object") { - return decodeForm(parameters + ""); - } - if (parameters instanceof Array) { - return parameters; - } - var list = []; - for (var p in parameters) { - list.push([p, parameters[p]]); - } - return list; - } -, - /** Convert the given parameters to a map from name to value. */ - getParameterMap: function getParameterMap(parameters) { - if (parameters == null) { - return {}; - } - if (typeof parameters != "object") { - return getParameterMap(decodeForm(parameters + "")); - } - if (parameters instanceof Array) { - var map = {}; - for (var p = 0; p < parameters.length; ++p) { - var key = parameters[p][0]; - if (map[key] === undefined) { // first value wins - map[key] = parameters[p][1]; - } - } - return map; - } - return parameters; - } -, - getParameter: function getParameter(parameters, name) { - if (parameters instanceof Array) { - for (var p = 0; p < parameters.length; ++p) { - if (parameters[p][0] == name) { - return parameters[p][1]; // first value wins - } - } - } else { - return OAuth.getParameterMap(parameters)[name]; - } - return null; - } -, - formEncode: function formEncode(parameters) { - var form = ""; - var list = OAuth.getParameterList(parameters); - for (var p = 0; p < list.length; ++p) { - var value = list[p][1]; - if (value == null) value = ""; - if (form != "") form += '&'; - form += OAuth.percentEncode(list[p][0]) - +'='+ OAuth.percentEncode(value); - } - return form; - } -, - decodeForm: function decodeForm(form) { - var list = []; - var nvps = form.split('&'); - for (var n = 0; n < nvps.length; ++n) { - var nvp = nvps[n]; - if (nvp == "") { - continue; - } - var equals = nvp.indexOf('='); - var name; - var value; - if (equals < 0) { - name = OAuth.decodePercent(nvp); - value = null; - } else { - name = OAuth.decodePercent(nvp.substring(0, equals)); - value = OAuth.decodePercent(nvp.substring(equals + 1)); - } - list.push([name, value]); - } - return list; - } -, - setParameter: function setParameter(message, name, value) { - var parameters = message.parameters; - if (parameters instanceof Array) { - for (var p = 0; p < parameters.length; ++p) { - if (parameters[p][0] == name) { - if (value === undefined) { - parameters.splice(p, 1); - } else { - parameters[p][1] = value; - value = undefined; - } - } - } - if (value !== undefined) { - parameters.push([name, value]); - } - } else { - parameters = OAuth.getParameterMap(parameters); - parameters[name] = value; - message.parameters = parameters; - } - } -, - setParameters: function setParameters(message, parameters) { - var list = OAuth.getParameterList(parameters); - for (var i = 0; i < list.length; ++i) { - OAuth.setParameter(message, list[i][0], list[i][1]); - } - } -, - /** Fill in parameters to help construct a request message. - This function doesn't fill in every parameter. - The accessor object should be like: - {consumerKey:'foo', consumerSecret:'bar', accessorSecret:'nurn', token:'krelm', tokenSecret:'blah'} - The accessorSecret property is optional. - */ - completeRequest: function completeRequest(message, accessor) { - if (message.method == null) { - message.method = "GET"; - } - var map = OAuth.getParameterMap(message.parameters); - if (map.oauth_consumer_key == null) { - OAuth.setParameter(message, "oauth_consumer_key", accessor.consumerKey || ""); - } - if (map.oauth_token == null && accessor.token != null) { - OAuth.setParameter(message, "oauth_token", accessor.token); - } - if (map.oauth_version == null) { - OAuth.setParameter(message, "oauth_version", "1.0"); - } - if (map.oauth_timestamp == null) { - OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp()); - } - if (map.oauth_nonce == null) { - OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6)); - } - OAuth.SignatureMethod.sign(message, accessor); - } -, - setTimestampAndNonce: function setTimestampAndNonce(message) { - OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp()); - OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6)); - } -, - addToURL: function addToURL(url, parameters) { - newURL = url; - if (parameters != null) { - var toAdd = OAuth.formEncode(parameters); - if (toAdd.length > 0) { - var q = url.indexOf('?'); - if (q < 0) newURL += '?'; - else newURL += '&'; - newURL += toAdd; - } - } - return newURL; - } -, - /** Construct the value of the Authorization header for an HTTP request. */ - getAuthorizationHeader: function getAuthorizationHeader(realm, parameters) { - var header = 'OAuth realm="' + OAuth.percentEncode(realm) + '"'; - var list = OAuth.getParameterList(parameters); - for (var p = 0; p < list.length; ++p) { - var parameter = list[p]; - var name = parameter[0]; - if (name.indexOf("oauth_") == 0) { - header += ',' + OAuth.percentEncode(name) + '="' + OAuth.percentEncode(parameter[1]) + '"'; - } - } - return header; - } -, - timestamp: function timestamp() { - var d = new Date(); - return Math.floor(d.getTime()/1000); - } -, - nonce: function nonce(length) { - var chars = OAuth.nonce.CHARS; - var result = ""; - for (var i = 0; i < length; ++i) { - var rnum = Math.floor(Math.random() * chars.length); - result += chars.substring(rnum, rnum+1); - } - return result; - } -}); - -OAuth.nonce.CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz"; - -/** Define a constructor function, - without causing trouble to anyone who was using it as a namespace. - That is, if parent[name] already existed and had properties, - copy those properties into the new constructor. - */ -OAuth.declareClass = function declareClass(parent, name, newConstructor) { - var previous = parent[name]; - parent[name] = newConstructor; - if (newConstructor != null && previous != null) { - for (var key in previous) { - if (key != "prototype") { - newConstructor[key] = previous[key]; - } - } - } - return newConstructor; -} - -/** An abstract algorithm for signing messages. */ -OAuth.declareClass(OAuth, "SignatureMethod", function OAuthSignatureMethod(){}); - -OAuth.setProperties(OAuth.SignatureMethod.prototype, // instance members -{ - /** Add a signature to the message. */ - sign: function sign(message) { - var baseString = OAuth.SignatureMethod.getBaseString(message); - var signature = this.getSignature(baseString); - OAuth.setParameter(message, "oauth_signature", signature); - return signature; // just in case someone's interested - } -, - /** Set the key string for signing. */ - initialize: function initialize(name, accessor) { - var consumerSecret; - if (accessor.accessorSecret != null - && name.length > 9 - && name.substring(name.length-9) == "-Accessor") - { - consumerSecret = accessor.accessorSecret; - } else { - consumerSecret = accessor.consumerSecret; - } - this.key = OAuth.percentEncode(consumerSecret) - +"&"+ OAuth.percentEncode(accessor.tokenSecret); - } -}); - -/* SignatureMethod expects an accessor object to be like this: - {tokenSecret: "lakjsdflkj...", consumerSecret: "QOUEWRI..", accessorSecret: "xcmvzc..."} - The accessorSecret property is optional. - */ -// Class members: -OAuth.setProperties(OAuth.SignatureMethod, // class members -{ - sign: function sign(message, accessor) { - var name = OAuth.getParameterMap(message.parameters).oauth_signature_method; - if (name == null || name == "") { - name = "HMAC-SHA1"; - OAuth.setParameter(message, "oauth_signature_method", name); - } - OAuth.SignatureMethod.newMethod(name, accessor).sign(message); - } -, - /** Instantiate a SignatureMethod for the given method name. */ - newMethod: function newMethod(name, accessor) { - var impl = OAuth.SignatureMethod.REGISTERED[name]; - if (impl != null) { - var method = new impl(); - method.initialize(name, accessor); - return method; - } - var err = new Error("signature_method_rejected"); - var acceptable = ""; - for (var r in OAuth.SignatureMethod.REGISTERED) { - if (acceptable != "") acceptable += '&'; - acceptable += OAuth.percentEncode(r); - } - err.oauth_acceptable_signature_methods = acceptable; - throw err; - } -, - /** A map from signature method name to constructor. */ - REGISTERED : {} -, - /** Subsequently, the given constructor will be used for the named methods. - The constructor will be called with no parameters. - The resulting object should usually implement getSignature(baseString). - You can easily define such a constructor by calling makeSubclass, below. - */ - registerMethodClass: function registerMethodClass(names, classConstructor) { - for (var n = 0; n < names.length; ++n) { - OAuth.SignatureMethod.REGISTERED[names[n]] = classConstructor; - } - } -, - /** Create a subclass of OAuth.SignatureMethod, with the given getSignature function. */ - makeSubclass: function makeSubclass(getSignatureFunction) { - var superClass = OAuth.SignatureMethod; - var subClass = function() { - superClass.call(this); - }; - subClass.prototype = new superClass(); - // Delete instance variables from prototype: - // delete subclass.prototype... There aren't any. - subClass.prototype.getSignature = getSignatureFunction; - subClass.prototype.constructor = subClass; - return subClass; - } -, - getBaseString: function getBaseString(message) { - var URL = message.action; - var q = URL.indexOf('?'); - var parameters; - if (q < 0) { - parameters = message.parameters; - } else { - // Combine the URL query string with the other parameters: - parameters = OAuth.decodeForm(URL.substring(q + 1)); - var toAdd = OAuth.getParameterList(message.parameters); - for (var a = 0; a < toAdd.length; ++a) { - parameters.push(toAdd[a]); - } - } - return OAuth.percentEncode(message.method.toUpperCase()) - +'&'+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeUrl(URL)) - +'&'+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeParameters(parameters)); - } -, - normalizeUrl: function normalizeUrl(url) { - var uri = OAuth.SignatureMethod.parseUri(url); - var scheme = uri.protocol.toLowerCase(); - var authority = uri.authority.toLowerCase(); - var dropPort = (scheme == "http" && uri.port == 80) - || (scheme == "https" && uri.port == 443); - if (dropPort) { - // find the last : in the authority - var index = authority.lastIndexOf(":"); - if (index >= 0) { - authority = authority.substring(0, index); - } - } - var path = uri.path; - if (!path) { - path = "/"; // conforms to RFC 2616 section 3.2.2 - } - // we know that there is no query and no fragment here. - return scheme + "://" + authority + path; - } -, - parseUri: function parseUri (str) { - /* This function was adapted from parseUri 1.2.1 - http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js - */ - var o = {key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], - parser: {strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ }}; - var m = o.parser.strict.exec(str); - var uri = {}; - var i = 14; - while (i--) uri[o.key[i]] = m[i] || ""; - return uri; - } -, - normalizeParameters: function normalizeParameters(parameters) { - if (parameters == null) { - return ""; - } - var list = OAuth.getParameterList(parameters); - var sortable = []; - for (var p = 0; p < list.length; ++p) { - var nvp = list[p]; - if (nvp[0] != "oauth_signature") { - sortable.push([ OAuth.percentEncode(nvp[0]) - + " " // because it comes before any character that can appear in a percentEncoded string. - + OAuth.percentEncode(nvp[1]) - , nvp]); - } - } - sortable.sort(function(a,b) { - if (a[0] < b[0]) return -1; - if (a[0] > b[0]) return 1; - return 0; - }); - var sorted = []; - for (var s = 0; s < sortable.length; ++s) { - sorted.push(sortable[s][1]); - } - return OAuth.formEncode(sorted); - } -}); - -OAuth.SignatureMethod.registerMethodClass(["PLAINTEXT", "PLAINTEXT-Accessor"], - OAuth.SignatureMethod.makeSubclass( - function getSignature(baseString) { - return this.key; - } - )); - -OAuth.SignatureMethod.registerMethodClass(["HMAC-SHA1", "HMAC-SHA1-Accessor"], - OAuth.SignatureMethod.makeSubclass( - function getSignature(baseString) { - b64pad = '='; - var signature = b64_hmac_sha1(this.key, baseString); - return signature; - } - )); diff --git a/test/javascript/run b/test/javascript/run index 7f366ebed..f7659b0f2 100755 --- a/test/javascript/run +++ b/test/javascript/run @@ -29,7 +29,6 @@ COUCHJS = "src/couch/priv/couchjs" SCRIPTS = """ test/javascript/json2.js test/javascript/sha1.js - test/javascript/oauth.js test/javascript/couch.js test/javascript/replicator_db_inc.js test/javascript/couch_test_runner.js diff --git a/test/javascript/tests/oauth_users_db.js b/test/javascript/tests/oauth_users_db.js deleted file mode 100644 index 6e4b3de33..000000000 --- a/test/javascript/tests/oauth_users_db.js +++ /dev/null @@ -1,168 +0,0 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. - -couchTests.oauth_users_db = function(debug) { - return console.log('TODO: oauth not available on clustered interface'); - // This tests OAuth authentication using the _users DB instead of the ini - // configuration for storing OAuth tokens and secrets. - - if (debug) debugger; - - var users_db_name = get_random_db_name(); - var usersDb = new CouchDB(users_db_name, {"X-Couch-Full-Commit":"false"}); - usersDb.createDb(); - - var db_name = get_random_db_name(); - var db = new CouchDB(db_name, {"X-Couch-Full-Commit":"false"}); - db.createDb(); - - var host = CouchDB.host; - var authorization_url = "/_oauth/authorize"; - - - // Simple secret key generator - function generateSecret(length) { - var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - var secret = ''; - for (var i = 0; i < length; i++) { - secret += tab.charAt(Math.floor(Math.random() * 64)); - } - return secret; - } - - - function oauthRequest(method, path, message, accessor) { - message.action = path; - message.method = method || 'GET'; - OAuth.SignatureMethod.sign(message, accessor); - var parameters = message.parameters; - if (method == "POST" || method == "GET") { - if (method == "GET") { - return CouchDB.request("GET", OAuth.addToURL(path, parameters)); - } else { - return CouchDB.request("POST", path, { - headers: {"Content-Type": "application/x-www-form-urlencoded"}, - body: OAuth.formEncode(parameters) - }); - } - } else { - return CouchDB.request(method, path, { - headers: {Authorization: OAuth.getAuthorizationHeader('', parameters)} - }); - } - } - - - // this function will be called on the modified server - var testFun = function () { - var fdmanana = CouchDB.prepareUserDoc({ - name: "fdmanana", - roles: ["dev"], - oauth: { - consumer_keys: { - "key_foo": "bar", - "key_xpto": "mars" - }, - tokens: { - "salut": "ola", - "tok1": "123" - } - } - }, "qwerty"); - TEquals(true, usersDb.save(fdmanana).ok); - - var signatureMethods = ["PLAINTEXT", "HMAC-SHA1"]; - var message, xhr, responseMessage, accessor, data; - - for (var i = 0; i < signatureMethods.length; i++) { - message = { - parameters: { - oauth_signature_method: signatureMethods[i], - oauth_consumer_key: "key_foo", - oauth_token: "tok1", - oauth_version: "1.0" - } - }; - accessor = { - consumerSecret: "bar", - tokenSecret: "123" - }; - - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_oauth/request_token", - message, accessor - ); - TEquals(200, xhr.status); - - responseMessage = OAuth.decodeForm(xhr.responseText); - - // Obtaining User Authorization - // Only needed for 3-legged OAuth - //xhr = CouchDB.request( - // "GET", authorization_url + '?oauth_token=' + responseMessage.oauth_token); - //TEquals(200, xhr.status); - - xhr = oauthRequest( - "GET", CouchDB.protocol + host + "/_session", message, accessor); - TEquals(200, xhr.status); - data = JSON.parse(xhr.responseText); - TEquals(true, data.ok); - TEquals("object", typeof data.userCtx); - TEquals("fdmanana", data.userCtx.name); - TEquals("dev", data.userCtx.roles[0]); - TEquals("oauth", data.info.authenticated); - - // test invalid token - message.parameters.oauth_token = "not a token!"; - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session", - message, accessor - ); - TEquals(400, xhr.status, "Request should be invalid."); - - // test invalid secret - message.parameters.oauth_token = "tok1"; - accessor.tokenSecret = "badone"; - xhr = oauthRequest("GET", CouchDB.protocol + host + "/_session", - message, accessor - ); - data = JSON.parse(xhr.responseText); - TEquals(null, data.userCtx.name); - TEquals(1, data.userCtx.roles.length); - TEquals("_admin", data.userCtx.roles[0]); - TEquals(true, data.info.authentication_handlers.indexOf("default") >= 0); - TEquals("default", data.info.authenticated); - } - }; - - - usersDb.deleteDb(); - - run_on_modified_server( - [ - {section: "httpd", - key: "WWW-Authenticate", value: 'OAuth'}, - {section: "couch_httpd_auth", - key: "secret", value: generateSecret(64)}, - {section: "couch_httpd_auth", - key: "authentication_db", value: usersDb.name}, - {section: "couch_httpd_oauth", - key: "use_users_db", value: "true"}, - {section: "httpd", key: "authentication_handlers", - value: "{couch_httpd_oauth, oauth_authentication_handler}, " + - "{couch_httpd_auth, default_authentication_handler}"} - ], - testFun - ); - - // cleanup - usersDb.deleteDb(); - db.deleteDb(); -}; diff --git a/test/javascript/tests/replicator_db_security.js b/test/javascript/tests/replicator_db_security.js index 2fc0f6c8a..ffb5c4036 100644 --- a/test/javascript/tests/replicator_db_security.js +++ b/test/javascript/tests/replicator_db_security.js @@ -344,34 +344,6 @@ couchTests.replicator_db_security = function(debug) { doc.source, "source field contains credentials (doc from _changes)"); CouchDB.logout(); - var fdmananaRepDocOAuth = { - _id: "fdmanana-rep-doc-oauth", - source: dbC.name, - target: { - url: "http://" + CouchDB.host + "/" + dbA.name, - oauth: { - token: "abc", - token_secret: "foo", - consumer_key: "123", - consumer_secret: "321" - } - }, - user_ctx: { name: "fdmanana", roles: [] } - }; - - var result = save_as(repDb, fdmananaRepDocOAuth, "fdmanana"); - TEquals(true, result.ok, "should create rep doc"); - waitForDocPos(repDb, fdmananaRepDocOAuth._id, 3); - fdmananaRepDocOAuth = open_as(repDb, fdmananaRepDocOAuth._id, "fdmanana"); - TEquals("fdmanana", fdmananaRepDocOAuth.owner, "should assign correct owner"); - TEquals("object", typeof fdmananaRepDocOAuth.target.oauth, - "target field has oauth credentials"); - - fdmananaRepDocOAuth = open_as(repDb, fdmananaRepDocOAuth._id, "jchris"); - TEquals("fdmanana", fdmananaRepDocOAuth.owner, "should assign correct owner"); - TEquals("undefined", typeof fdmananaRepDocOAuth.target.oauth, - "target field doesn't have oauth credentials"); - // ensure "old" replicator docs still work // done in replicator_db.js? |