diff options
-rw-r--r-- | share/www/script/couch_tests.js | 43 | ||||
-rw-r--r-- | src/couchdb/couch_doc.erl | 12 | ||||
-rw-r--r-- | src/couchdb/couch_httpd.erl | 2 |
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">>}, |