summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/www/script/couch_tests.js43
-rw-r--r--src/couchdb/couch_doc.erl12
-rw-r--r--src/couchdb/couch_httpd.erl2
3 files changed, 53 insertions, 4 deletions
diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js
index 2df262d7..55a63bc9 100644
--- a/share/www/script/couch_tests.js
+++ b/share/www/script/couch_tests.js
@@ -1482,6 +1482,49 @@ db.createDb();
T(db.view("test/no_docs") == null);
},
+ invalid_docids: function(debug) {
+ var db = new CouchDB("test_suite_db");
+ db.deleteDb();
+ db.createDb();
+ if (debug) debugger;
+
+ // Test _local explicitly first.
+ T(db.save({"_id": "_local/foo"}).ok);
+ T(db.open("_local/foo")._id == "_local/foo");
+
+ //Test non-string
+ try {
+ db.save({"_id": 1});
+ T(1 == 0);
+ } catch(e) {
+ T(db.last_req.status == 400);
+ T(e.error == "invalid_doc");
+ }
+
+ // Test invalid _prefix
+ try {
+ db.save({"_id": "_invalid"});
+ T(1 == 0);
+ } catch(e) {
+ T(db.last_req.status == 400);
+ T(e.error == "invalid_doc");
+ }
+
+ // Test _bulk_docs explicitly.
+ var docs = [{"_id": "_design/foo"}, {"_id": "_local/bar"}];
+ T(db.bulkSave(docs).ok);
+ docs.forEach(function(d) {T(db.open(d._id)._id == d._id);});
+
+ docs = [{"_id": "_invalid"}];
+ try {
+ db.bulkSave(docs);
+ T(1 == 0);
+ } catch(e) {
+ T(db.last_req.status == 400);
+ T(e.error == "invalid_doc");
+ }
+ },
+
view_collation: function(debug) {
var db = new CouchDB("test_suite_db");
db.deleteDb();
diff --git a/src/couchdb/couch_doc.erl b/src/couchdb/couch_doc.erl
index 5bad7854..1eb1575a 100644
--- a/src/couchdb/couch_doc.erl
+++ b/src/couchdb/couch_doc.erl
@@ -112,12 +112,12 @@ from_json_obj({Props}) ->
<<"conflicts">>, <<"deleted_conflicts">>, <<"deleted">>],
% collect all the doc-members that start with "_"
% if any aren't in the AllowedSpecialMembers list
- % then throw a doc_validation error
+ % then throw a invalid_doc error
[case lists:member(Name, AllowedSpecialMembers) of
true ->
ok;
false ->
- throw({doc_validation, io_lib:format("Bad special document member: _~s", [Name])})
+ throw({invalid_doc, io_lib:format("Bad special document member: _~s", [Name])})
end
|| {<<$_,Name/binary>>, _Value} <- Props],
Revs =
@@ -131,10 +131,14 @@ from_json_obj({Props}) ->
Revs0
end,
case proplists:get_value(<<"_id">>, Props, <<>>) of
+ <<"_design/", _/binary>> = Id -> ok;
+ <<"_local/", _/binary>> = Id -> ok;
+ <<"_", _/binary>> = Id ->
+ throw({invalid_doc, "Document Ids must not start with underscore."});
Id when is_binary(Id) -> ok;
Id ->
?LOG_DEBUG("Document id is not a string: ~p", [Id]),
- throw({invalid_document_id, "Document id is not a string"})
+ throw({invalid_doc, "Document id is not a string"})
end,
% strip out the all props beginning with _
@@ -148,7 +152,7 @@ from_json_obj({Props}) ->
};
from_json_obj(_Other) ->
- throw({invalid_json_object, "Document must be a JSON object"}).
+ throw({invalid_doc, "Document must be a JSON object"}).
to_doc_info(FullDocInfo) ->
{DocInfo, _Path} = to_doc_info_path(FullDocInfo),
diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
index 767b91d4..6b9079c4 100644
--- a/src/couchdb/couch_httpd.erl
+++ b/src/couchdb/couch_httpd.erl
@@ -372,6 +372,8 @@ send_error(Req, {not_found, Reason}) ->
send_error(Req, 404, <<"not_found">>, Reason);
send_error(Req, conflict) ->
send_error(Req, 409, <<"conflict">>, <<"Document update conflict.">>);
+send_error(Req, {invalid_doc, Reason}) ->
+ send_error(Req, 400, <<"invalid_doc">>, Reason);
send_error(Req, {forbidden, Msg}) ->
send_json(Req, 403,
{[{<<"error">>, <<"forbidden">>},