summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Lehnardt <jan@apache.org>2009-08-29 13:42:50 +0000
committerJan Lehnardt <jan@apache.org>2009-08-29 13:42:50 +0000
commit69d5b41e7ecfa8d1b31628af5e0b2297984ea943 (patch)
treed260e4c19e2b2ebc43f599e7b2dcc2e1c97a944c
parent69e03ccc8e6e39750943e298d633acb4650e56b7 (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.js4
-rw-r--r--share/www/script/sha1.js2
-rw-r--r--share/www/script/test/oauth.js76
-rw-r--r--src/couchdb/couch_httpd_auth.erl62
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) ->