summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/www/script/couch.js10
-rw-r--r--share/www/script/test/reader_acl.js77
-rw-r--r--share/www/script/test/security_validation.js4
-rw-r--r--src/couchdb/couch_db.erl65
-rw-r--r--src/couchdb/couch_httpd_db.erl23
5 files changed, 81 insertions, 98 deletions
diff --git a/share/www/script/couch.js b/share/www/script/couch.js
index 4438a870..c5495424 100644
--- a/share/www/script/couch.js
+++ b/share/www/script/couch.js
@@ -263,16 +263,16 @@ function CouchDB(name, httpHeaders) {
return JSON.parse(this.last_req.responseText);
}
- this.setAdmins = function(adminsArray) {
- this.last_req = this.request("PUT", this.uri + "_admins",{
- body:JSON.stringify(adminsArray)
+ this.setSecObj = function(secObj) {
+ this.last_req = this.request("PUT", this.uri + "_security",{
+ body:JSON.stringify(secObj)
});
CouchDB.maybeThrowError(this.last_req);
return JSON.parse(this.last_req.responseText);
}
- this.getAdmins = function() {
- this.last_req = this.request("GET", this.uri + "_admins");
+ this.getSecObj = function() {
+ this.last_req = this.request("GET", this.uri + "_security");
CouchDB.maybeThrowError(this.last_req);
return JSON.parse(this.last_req.responseText);
}
diff --git a/share/www/script/test/reader_acl.js b/share/www/script/test/reader_acl.js
index a5fc6a1a..6f834bfb 100644
--- a/share/www/script/test/reader_acl.js
+++ b/share/www/script/test/reader_acl.js
@@ -35,9 +35,12 @@ couchTests.reader_acl = function(debug) {
T(secretDb.save({_id:"baz",foo:"bar"}).ok);
T(secretDb.open("baz").foo == "bar");
- T(secretDb.setDbProperty("_readers", {
- roles : ["super-secret-club"],
- names : ["joe","barb"]}).ok);
+ T(secretDb.setSecObj({
+ "readers" : {
+ roles : ["super-secret-club"],
+ names : ["joe","barb"]
+ }
+ }).ok);
// can't read it as jchris
T(CouchDB.login("jchris@apache.org", "funnybone").ok);
T(CouchDB.session().userCtx.name == "jchris@apache.org");
@@ -51,54 +54,76 @@ couchTests.reader_acl = function(debug) {
CouchDB.logout();
- // make top-secret an admin
- T(secretDb.setDbProperty("_admins", {
- roles : ["top-secret"],
- names : []}).ok);
+ // make anyone with the top-secret role an admin
+ // db admins are automatically readers
+ T(secretDb.setSecObj({
+ "admins" : {
+ roles : ["top-secret"],
+ names : []
+ },
+ "readers" : {
+ roles : ["super-secret-club"],
+ names : ["joe","barb"]
+ }
+ }).ok);
T(CouchDB.login("jchris@apache.org", "funnybone").ok);
T(secretDb.open("baz").foo == "bar");
CouchDB.logout();
-
- T(secretDb.setDbProperty("_admins", {
- roles : [],
- names : []}).ok);
-
- // admin now adds the top-secret role to the db's readers
T(CouchDB.session().userCtx.roles.indexOf("_admin") != -1);
- T(secretDb.setDbProperty("_readers", {
- roles : ["super-secret-club", "top-secret"],
- names : ["joe","barb"]}).ok);
+ // admin now adds the top-secret role to the db's readers
+ // and removes db-admins
+ T(secretDb.setSecObj({
+ "admins" : {
+ roles : [],
+ names : []
+ },
+ "readers" : {
+ roles : ["super-secret-club", "top-secret"],
+ names : ["joe","barb"]
+ }
+ }).ok);
- // now top-secret users can read it
+ // server _admin can always read
T(secretDb.open("baz").foo == "bar");
+
+ // now top-secret users can read too
T(CouchDB.login("jchris@apache.org", "funnybone").ok);
+ T(CouchDB.session().userCtx.roles.indexOf("_admin") == -1);
T(secretDb.open("baz").foo == "bar");
CouchDB.logout();
// can't set non string reader names or roles
try {
- secretDb.setDbProperty("_readers", {
- roles : ["super-secret-club", {"top-secret":"awesome"}],
- names : ["joe","barb"]});
+ secretDb.setSecObj({
+ "readers" : {
+ roles : ["super-secret-club", {"top-secret":"awesome"}],
+ names : ["joe","barb"]
+ }
+ })
T(false && "only string roles");
} catch (e) {}
try {
- secretDb.setDbProperty("_readers", {
- roles : ["super-secret-club", "top-secret"],
- names : ["joe",22]});
+ secretDb.setSecObj({
+ "readers" : {
+ roles : ["super-secret-club", {"top-secret":"awesome"}],
+ names : ["joe",22]
+ }
+ });
T(false && "only string names");
} catch (e) {}
try {
- secretDb.setDbProperty("_readers", {
- roles : ["super-secret-club", "top-secret"],
- names : "joe"
+ secretDb.setSecObj({
+ "readers" : {
+ roles : ["super-secret-club", {"top-secret":"awesome"}],
+ names : "joe"
+ }
});
T(false && "only lists of names");
} catch (e) {}
diff --git a/share/www/script/test/security_validation.js b/share/www/script/test/security_validation.js
index d618a5ac..94fe62c4 100644
--- a/share/www/script/test/security_validation.js
+++ b/share/www/script/test/security_validation.js
@@ -105,7 +105,9 @@ couchTests.security_validation = function(debug) {
}
// set user as the admin
- T(db.setDbProperty("_admins", {names : ["Damien Katz"]}).ok);
+ T(db.setSecObj({
+ admins : {names : ["Damien Katz"]}
+ }).ok);
T(userDb.save(designDoc).ok);
diff --git a/src/couchdb/couch_db.erl b/src/couchdb/couch_db.erl
index 39ce6a9b..6d5da15b 100644
--- a/src/couchdb/couch_db.erl
+++ b/src/couchdb/couch_db.erl
@@ -23,7 +23,7 @@
-export([enum_docs_since_reduce_to_count/1,enum_docs_reduce_to_count/1]).
-export([increment_update_seq/1,get_purge_seq/1,purge_docs/2,get_last_purged/1]).
-export([start_link/3,open_doc_int/3,ensure_full_commit/1]).
--export([set_readers/2,get_readers/1,set_admins/2,get_admins/1,set_security/2,get_security/1]).
+-export([set_security/2,get_security/1]).
-export([init/1,terminate/2,handle_call/3,handle_cast/2,code_change/3,handle_info/2]).
-export([changes_since/5,changes_since/6,read_doc/2,new_revid/1]).
@@ -232,8 +232,8 @@ get_design_docs(#db{fulldocinfo_by_id_btree=Btree}=Db) ->
check_is_admin(#db{user_ctx=#user_ctx{name=Name,roles=Roles}}=Db) ->
{Admins} = get_admins(Db),
- AdminRoles = [<<"_admin">> | proplists:get_value(roles, Admins, [])],
- AdminNames = proplists:get_value(names, Admins,[]),
+ AdminRoles = [<<"_admin">> | proplists:get_value(<<"roles">>, Admins, [])],
+ AdminNames = proplists:get_value(<<"names">>, Admins,[]),
case AdminRoles -- Roles of
AdminRoles -> % same list, not an admin role
case AdminNames -- [Name] of
@@ -251,9 +251,9 @@ check_is_reader(#db{user_ctx=#user_ctx{name=Name,roles=Roles}=UserCtx}=Db) ->
ok -> ok;
_ ->
{Readers} = get_readers(Db),
- ReaderRoles = proplists:get_value(roles, Readers,[]),
+ ReaderRoles = proplists:get_value(<<"roles">>, Readers,[]),
WithAdminRoles = [<<"_admin">> | ReaderRoles],
- ReaderNames = proplists:get_value(names, Readers,[]),
+ ReaderNames = proplists:get_value(<<"names">>, Readers,[]),
case ReaderRoles ++ ReaderNames of
[] -> ok; % no readers == public access
_Else ->
@@ -273,64 +273,43 @@ check_is_reader(#db{user_ctx=#user_ctx{name=Name,roles=Roles}=UserCtx}=Db) ->
end.
get_admins(#db{security=SecProps}) ->
- proplists:get_value(admins, SecProps, {[]}).
-
-set_admins(#db{security=SecProps,update_pid=Pid}=Db, Admins) ->
- check_is_admin(Db),
- SecProps2 = update_sec_field(admins, SecProps, just_names_and_roles(Admins)),
- gen_server:call(Pid, {set_security, SecProps2}, infinity).
+ proplists:get_value(<<"admins">>, SecProps, {[]}).
get_readers(#db{security=SecProps}) ->
- proplists:get_value(readers, SecProps, {[]}).
-
-set_readers(#db{security=SecProps,update_pid=Pid}=Db, Readers) ->
- check_is_admin(Db),
- SecProps2 = update_sec_field(readers, SecProps, just_names_and_roles(Readers)),
- gen_server:call(Pid, {set_security, SecProps2}, infinity).
+ proplists:get_value(<<"readers">>, SecProps, {[]}).
get_security(#db{security=SecProps}) ->
- proplists:get_value(sec_obj, SecProps, {[]}).
+ {SecProps}.
-set_security(#db{security=SecProps, update_pid=Pid}=Db, {SecObjProps}) when is_list(SecObjProps) ->
+set_security(#db{update_pid=Pid}=Db, {NewSecProps}) when is_list(NewSecProps) ->
check_is_admin(Db),
- SecProps2 = update_sec_field(sec_obj, SecProps, {SecObjProps}),
- gen_server:call(Pid, {set_security, SecProps2}, infinity);
+ ok = validate_security_object(NewSecProps),
+ gen_server:call(Pid, {set_security, NewSecProps}, infinity);
set_security(_, _) ->
throw(bad_request).
-update_sec_field(Field, SecProps, Value) ->
- Admins = proplists:get_value(admins, SecProps, {[]}),
- Readers = proplists:get_value(readers, SecProps, {[]}),
- SecObj = proplists:get_value(sec_obj, SecProps, {[]}),
- if Field == admins ->
- [{admins, Value}];
- true -> [{admins, Admins}]
- end ++ if Field == readers ->
- [{readers, Value}];
- true -> [{readers, Readers}]
- end ++ if Field == sec_obj ->
- [{sec_obj, Value}];
- true -> [{sec_obj, SecObj}]
- end.
+validate_security_object(SecProps) ->
+ Admins = proplists:get_value(<<"admins">>, SecProps, {[]}),
+ Readers = proplists:get_value(<<"readers">>, SecProps, {[]}),
+ ok = validate_names_and_roles(Admins),
+ ok = validate_names_and_roles(Readers),
+ ok.
-% validate user input and convert proplist to atom keys
-just_names_and_roles({Props}) when is_list(Props) ->
- Names = case proplists:get_value(<<"names">>,Props,[]) of
+% validate user input
+validate_names_and_roles({Props}) when is_list(Props) ->
+ case proplists:get_value(<<"names">>,Props,[]) of
Ns when is_list(Ns) ->
[throw("names must be a JSON list of strings") ||N <- Ns, not is_binary(N)],
Ns;
_ -> throw("names must be a JSON list of strings")
end,
- Roles = case proplists:get_value(<<"roles">>,Props,[]) of
+ case proplists:get_value(<<"roles">>,Props,[]) of
Rs when is_list(Rs) ->
[throw("roles must be a JSON list of strings") ||R <- Rs, not is_binary(R)],
Rs;
_ -> throw("roles must be a JSON list of strings")
end,
- {[
- {names, Names},
- {roles, Roles}
- ]}.
+ ok.
get_revs_limit(#db{revs_limit=Limit}) ->
Limit.
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index 4f2f1565..9ad34752 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -547,29 +547,6 @@ db_req(#httpd{method='POST',path_parts=[_,<<"_revs_diff">>]}=Req, Db) ->
db_req(#httpd{path_parts=[_,<<"_revs_diff">>]}=Req, _Db) ->
send_method_not_allowed(Req, "POST");
-
-db_req(#httpd{method='PUT',path_parts=[_,<<"_admins">>]}=Req, Db) ->
- Admins = couch_httpd:json_body(Req),
- ok = couch_db:set_admins(Db, Admins),
- send_json(Req, {[{<<"ok">>, true}]});
-
-db_req(#httpd{method='GET',path_parts=[_,<<"_admins">>]}=Req, Db) ->
- send_json(Req, couch_db:get_admins(Db));
-
-db_req(#httpd{path_parts=[_,<<"_admins">>]}=Req, _Db) ->
- send_method_not_allowed(Req, "PUT,GET");
-
-db_req(#httpd{method='PUT',path_parts=[_,<<"_readers">>]}=Req, Db) ->
- Readers = couch_httpd:json_body(Req),
- ok = couch_db:set_readers(Db, Readers),
- send_json(Req, {[{<<"ok">>, true}]});
-
-db_req(#httpd{method='GET',path_parts=[_,<<"_readers">>]}=Req, Db) ->
- send_json(Req, couch_db:get_readers(Db));
-
-db_req(#httpd{path_parts=[_,<<"_readers">>]}=Req, _Db) ->
- send_method_not_allowed(Req, "PUT,GET");
-
db_req(#httpd{method='PUT',path_parts=[_,<<"_security">>]}=Req, Db) ->
SecObj = couch_httpd:json_body(Req),
ok = couch_db:set_security(Db, SecObj),