diff options
author | Jan Lehnardt <jan@apache.org> | 2009-08-29 13:42:50 +0000 |
---|---|---|
committer | Jan Lehnardt <jan@apache.org> | 2009-08-29 13:42:50 +0000 |
commit | 69d5b41e7ecfa8d1b31628af5e0b2297984ea943 (patch) | |
tree | d260e4c19e2b2ebc43f599e7b2dcc2e1c97a944c | |
parent | 69e03ccc8e6e39750943e298d633acb4650e56b7 (diff) |
merge cascading auth patch by Jason Davies, closes COUCHDB-478, fix tests
git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@809134 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | share/www/script/couch.js | 4 | ||||
-rw-r--r-- | share/www/script/sha1.js | 2 | ||||
-rw-r--r-- | share/www/script/test/oauth.js | 76 | ||||
-rw-r--r-- | src/couchdb/couch_httpd_auth.erl | 62 |
4 files changed, 102 insertions, 42 deletions
diff --git a/share/www/script/couch.js b/share/www/script/couch.js index e902a730..81e25f1b 100644 --- a/share/www/script/couch.js +++ b/share/www/script/couch.js @@ -324,7 +324,9 @@ CouchDB.createUser = function(username, password, email, roles, basicAuth) { } } var headers = {"Content-Type": "application/x-www-form-urlencoded"}; - if (!basicAuth) { + if (basicAuth) { + headers['Authorization'] = basicAuth + } else { headers['X-CouchDB-WWW-Authenticate'] = 'Cookie'; } diff --git a/share/www/script/sha1.js b/share/www/script/sha1.js index 1b559823..ee73a634 100644 --- a/share/www/script/sha1.js +++ b/share/www/script/sha1.js @@ -12,7 +12,7 @@ * the server-side, but the defaults work in most cases.
*/
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
-var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
+var b64pad = "="; /* base-64 pad character. "=" for strict RFC compliance */
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
/*
diff --git a/share/www/script/test/oauth.js b/share/www/script/test/oauth.js index 6048a0ce..8b71d745 100644 --- a/share/www/script/test/oauth.js +++ b/share/www/script/test/oauth.js @@ -36,7 +36,7 @@ couchTests.oauth = function(debug) { return secret; } - function oauthRequest(path, message, accessor, method) { + function oauthRequest(method, path, message, accessor) { message.action = path; message.method = method || 'GET'; OAuth.SignatureMethod.sign(message, accessor); @@ -59,6 +59,8 @@ couchTests.oauth = function(debug) { var consumerSecret = generateSecret(64); var tokenSecret = generateSecret(64); + var admintokenSecret = generateSecret(64); + var testadminPassword = "ohsosecret"; var host = CouchDB.host; var dbPair = { @@ -76,24 +78,40 @@ couchTests.oauth = function(debug) { target: "http://" + host + "/test_suite_db_b" }; + var adminBasicAuthHeaderValue = function() { + var retval = 'Basic ' + binb2b64(str2binb("testadmin:" + testadminPassword)); + return retval; + } + // this function will be called on the modified server var testFun = function () { try { - var usersDb = new CouchDB("test_suite_users", {"X-Couch-Full-Commit":"false"}); + CouchDB.request("PUT", "http://" + host + "/_config/admins/testadmin", { + headers: {"X-Couch-Persist": "false"}, + body: JSON.stringify(testadminPassword) + }); + + var usersDb = new CouchDB("test_suite_users", { + "X-Couch-Full-Commit":"false", + "Authorization": adminBasicAuthHeaderValue() + }); usersDb.deleteDb(); usersDb.createDb(); - + // Create a user - T(CouchDB.createUser("jason", "testpassword", "test@somemail.com", ['test'], true).ok); + T(CouchDB.createUser("jason", "testpassword", "test@somemail.com", ['test'], adminBasicAuthHeaderValue()).ok); var accessor = { consumerSecret: consumerSecret, tokenSecret: tokenSecret }; + var adminAccessor = { + consumerSecret: consumerSecret, + tokenSecret: admintokenSecret + }; var signatureMethods = ["PLAINTEXT", "HMAC-SHA1"]; var consumerKeys = {key: 200, nonexistent_key: 400}; - for (var i=0; i<signatureMethods.length; i++) { for (var consumerKey in consumerKeys) { var expectedCode = consumerKeys[consumerKey]; @@ -108,11 +126,11 @@ couchTests.oauth = function(debug) { }; // Get request token via Authorization header - xhr = oauthRequest("http://" + host + "/_oauth/request_token", message, accessor); + xhr = oauthRequest("GET", "http://" + host + "/_oauth/request_token", message, accessor); T(xhr.status == expectedCode); // GET request token via query parameters - xhr = oauthRequest("http://" + host + "/_oauth/request_token", message, accessor, "GET"); + xhr = oauthRequest("GET", "http://" + host + "/_oauth/request_token", message, accessor); T(xhr.status == expectedCode); responseMessage = OAuth.decodeForm(xhr.responseText); @@ -122,7 +140,7 @@ couchTests.oauth = function(debug) { //xhr = CouchDB.request("GET", authorization_url + '?oauth_token=' + responseMessage.oauth_token); //T(xhr.status == expectedCode); - xhr = oauthRequest("http://" + host + "/_session", message, accessor); + xhr = oauthRequest("GET", "http://" + host + "/_session", message, accessor); T(xhr.status == expectedCode); if (xhr.status == expectedCode == 200) { data = JSON.parse(xhr.responseText); @@ -130,25 +148,49 @@ couchTests.oauth = function(debug) { T(data.roles[0] == "test"); } - xhr = oauthRequest("http://" + host + "/_session?foo=bar", message, accessor); + xhr = oauthRequest("GET", "http://" + host + "/_session?foo=bar", message, accessor); T(xhr.status == expectedCode); // Test HEAD method - xhr = oauthRequest("http://" + host + "/_session?foo=bar", message, accessor, "HEAD"); + xhr = oauthRequest("HEAD", "http://" + host + "/_session?foo=bar", message, accessor); T(xhr.status == expectedCode); // Replication var result = CouchDB.replicate(dbPair.source, dbPair.target); T(result.ok); + + // Test auth via admin user defined in .ini + var message = { + parameters: { + oauth_signature_method: signatureMethods[i], + oauth_consumer_key: consumerKey, + oauth_token: "bar", + oauth_token_secret: admintokenSecret, + oauth_version: "1.0" + } + }; + xhr = oauthRequest("GET", "http://" + host + "/_session?foo=bar", message, adminAccessor); + if (xhr.status == expectedCode == 200) { + data = JSON.parse(xhr.responseText); + T(data.name == "testadmin"); + T(data.roles[0] == "_admin"); + } } } - } finally { + var xhr = CouchDB.request("DELETE", "http://" + host + "/_config/admins/testadmin", { + headers: { + "Authorization": adminBasicAuthHeaderValue(), + "X-Couch-Persist": "false" + } + }); + T(xhr.status == 200); } }; run_on_modified_server( - [{section: "httpd", + [ + {section: "httpd", key: "WWW-Authenticate", value: 'Basic realm="administrator",OAuth'}, {section: "couch_httpd_auth", key: "secret", value: generateSecret(64)}, @@ -158,13 +200,15 @@ couchTests.oauth = function(debug) { key: "key", value: consumerSecret}, {section: "oauth_token_users", key: "foo", value: "jason"}, + {section: "oauth_token_users", + key: "bar", value: "testadmin"}, {section: "oauth_token_secrets", key: "foo", value: tokenSecret}, + {section: "oauth_token_secrets", + key: "bar", value: admintokenSecret}, {section: "couch_httpd_oauth", - key: "authorization_url", value: authorization_url}, - {section: "httpd", - key: "authentication_handlers", - value: "{couch_httpd_oauth, oauth_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"}], + key: "authorization_url", value: authorization_url} + ], testFun ); }; diff --git a/src/couchdb/couch_httpd_auth.erl b/src/couchdb/couch_httpd_auth.erl index f2974836..7be45b20 100644 --- a/src/couchdb/couch_httpd_auth.erl +++ b/src/couchdb/couch_httpd_auth.erl @@ -45,8 +45,10 @@ special_test_authentication_handler(Req) -> end. basic_username_pw(Req) -> - case header_value(Req, "Authorization") of + AuthorizationHeader = header_value(Req, "Authorization"), + case AuthorizationHeader of "Basic " ++ Base64Value -> + io:format("~n~nBase64Value: '~p'~n~n", [Base64Value]), case string:tokens(?b2l(couch_util:decodeBase64(Base64Value)),":") of [User, Pass] -> {User, Pass}; @@ -109,29 +111,41 @@ cookie_authentication_handler(Req) -> % maybe we can use hovercraft to simplify running this view query get_user(Db, UserName) -> - DesignId = <<"_design/_auth">>, - ViewName = <<"users">>, - % if the design doc or the view doesn't exist, then make it - ensure_users_view_exists(Db, DesignId, ViewName), - - case (catch couch_view:get_map_view(Db, DesignId, ViewName, nil)) of - {ok, View, _Group} -> - FoldlFun = fun - ({{Key, _DocId}, Value}, _, nil) when Key == UserName -> {ok, Value}; - (_, _, Acc) -> {stop, Acc} - end, - case couch_view:fold(View, {UserName, nil}, fwd, FoldlFun, nil) of - {ok, {Result}} -> Result; - _Else -> nil - end; - {not_found, _Reason} -> - nil - % case (catch couch_view:get_reduce_view(Db, DesignId, ViewName, nil)) of - % {ok, _ReduceView, _Group} -> - % not_implemented; - % {not_found, _Reason} -> - % nil - % end + % In the future this will be pluggable. For now we check the .ini first, + % then fall back to querying the db. + io:format("~n~nget-user: '~p'~n", [get_user]), + case couch_config:get("admins", ?b2l(UserName)) of + "-hashed-" ++ HashedPwdAndSalt -> + io:format("hashed: '~p'~n", [hashed]), + [HashedPwd, Salt] = string:tokens(HashedPwdAndSalt, ","), + [{<<"roles">>, [<<"_admin">>]}, + {<<"salt">>, ?l2b(Salt)}, + {<<"password_sha">>, ?l2b(HashedPwd)}]; + _ -> + DesignId = <<"_design/_auth">>, + ViewName = <<"users">>, + % if the design doc or the view doesn't exist, then make it + ensure_users_view_exists(Db, DesignId, ViewName), + + case (catch couch_view:get_map_view(Db, DesignId, ViewName, nil)) of + {ok, View, _Group} -> + FoldlFun = fun + ({{Key, _DocId}, Value}, _, nil) when Key == UserName -> {ok, Value}; + (_, _, Acc) -> {stop, Acc} + end, + case couch_view:fold(View, {UserName, nil}, fwd, FoldlFun, nil) of + {ok, {Result}} -> Result; + _Else -> nil + end; + {not_found, _Reason} -> + nil + % case (catch couch_view:get_reduce_view(Db, DesignId, ViewName, nil)) of + % {ok, _ReduceView, _Group} -> + % not_implemented; + % {not_found, _Reason} -> + % nil + % end + end end. ensure_users_db_exists(DbName) -> |