summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJay Doane <jaydoane@apache.org>2021-08-10 15:09:17 -0700
committerJay Doane <jaydoane@apache.org>2021-08-10 22:42:23 -0700
commit8414eabfb1de81d43c313671eda0edc063b768f0 (patch)
tree9539d90220a89915fca02f673c43e10e57e4413a
parent28539d3b2bc5d8e50fca1eb94d5e205827fe08b2 (diff)
downloadcouchdb-restrict-ddoc-update-methods.tar.gz
Restrict design doc update methodsrestrict-ddoc-update-methods
Documentation for design doc update functions specifies that permitted methods are: POST /{db}/_design/{ddoc}/_update/{func} PUT /{db}/_design/{ddoc}/_update/{func}/{docid} [2] But currently anything is accepted, even bogus methods such as in the following example work: ``` $ curl -v -u adm localhost:15984/db-1/_design/ddoc/_update/change -X BOGUS > BOGUS /db-1/_design/bar/_update/change HTTP/1.1 > < HTTP/1.1 200 OK < content-length: 11 < content-type: text/html; charset=utf-8 < * Connection #0 to host localhost left intact Empty World* Closing connection 0 ``` A strict reading of the docs indicates that the null document endpoint only allows POST [1] while the endpoint taking a document id only allows PUT [2]. However, the examples in both cases exclusively use POST, and also PUT doesn't make as much sense from a semantic perspective since a new resource isn't being created, I assume the PUT is a typo, and both should be changed to POST. However, given the inconsistent usage even in CouchDB's own test suite, it seems prudent to minimize incompatibility, while still imposing reasonable restrictions, and allow either PUT or POST for both endpoints. [1] https://docs.couchdb.org/en/latest/api/ddoc/render.html#post--db-_design-ddoc-_update-func [2] https://docs.couchdb.org/en/latest/api/ddoc/render.html#put--db-_design-ddoc-_update-func-docid
-rw-r--r--src/chttpd/src/chttpd_show.erl11
-rw-r--r--test/elixir/test/update_documents_test.exs19
2 files changed, 21 insertions, 9 deletions
diff --git a/src/chttpd/src/chttpd_show.erl b/src/chttpd/src/chttpd_show.erl
index c2c37c66d..de1eb2805 100644
--- a/src/chttpd/src/chttpd_show.erl
+++ b/src/chttpd/src/chttpd_show.erl
@@ -95,11 +95,12 @@ show_etag(#httpd{user_ctx=UserCtx}=Req, Doc, DDoc, More) ->
couch_httpd:make_etag({couch_httpd:doc_etag(DDoc), DocPart, Accept,
UserCtx#user_ctx.roles, More}).
-% /db/_design/foo/update/bar/docid
-% updates a doc based on a request
-% handle_doc_update_req(#httpd{method = 'GET'}=Req, _Db, _DDoc) ->
-% % anything but GET
-% send_method_not_allowed(Req, "POST,PUT,DELETE,ETC");
+
+handle_doc_update_req(#httpd{
+ method=Method,
+ path_parts=[_, <<"_design">>, _, <<"_update">> | _MaybeDocIdParts]
+ }=Req, _Db, _DDoc) when Method =/= 'POST' andalso Method =/= 'PUT' ->
+ chttpd:send_method_not_allowed(Req, "POST,PUT");
handle_doc_update_req(#httpd{
path_parts=[_, _, _, _, UpdateName]
diff --git a/test/elixir/test/update_documents_test.exs b/test/elixir/test/update_documents_test.exs
index c29b31a4d..df5856b44 100644
--- a/test/elixir/test/update_documents_test.exs
+++ b/test/elixir/test/update_documents_test.exs
@@ -106,6 +106,17 @@ defmodule UpdateDocumentsTest do
@document %{word: "plankton", name: "Rusty"}
@tag :with_db
+ test "update error method not allowed", context do
+ db_name = context[:db_name]
+ create_doc(db_name, @ddoc)
+
+ resp = Couch.get("/#{db_name}/_design/update/_update/")
+ assert resp.status_code == 405
+ assert resp.body["error"] == "method_not_allowed"
+ assert resp.body["reason"] == "Only POST,PUT allowed"
+ end
+
+ @tag :with_db
test "update error invalid path", context do
db_name = context[:db_name]
create_doc(db_name, @ddoc)
@@ -135,7 +146,7 @@ defmodule UpdateDocumentsTest do
# Fix for COUCHDB-379
assert String.starts_with?(resp.headers["Server"], "CouchDB")
- resp = Couch.put("/#{db_name}/_design/update/_update/hello")
+ resp = Couch.post("/#{db_name}/_design/update/_update/hello")
assert resp.status_code == 200
assert resp.body == "<p>Empty World</p>"
end
@@ -246,7 +257,7 @@ defmodule UpdateDocumentsTest do
test "Server provides UUID when POSTing without an ID in the URL", context do
db_name = context[:db_name]
create_doc(db_name, @ddoc)
- resp = Couch.put("/#{db_name}/_design/update/_update/get-uuid/")
+ resp = Couch.post("/#{db_name}/_design/update/_update/get-uuid/")
assert resp.status_code == 200
assert String.length(resp.body) == 32
end
@@ -286,10 +297,10 @@ defmodule UpdateDocumentsTest do
assert resp.status_code == 200
assert resp.body["counter"] == 3
- resp = Couch.put("/#{db_name}/_design/update/_update/resp-code/")
+ resp = Couch.post("/#{db_name}/_design/update/_update/resp-code/")
assert resp.status_code == 302
- resp = Couch.put("/#{db_name}/_design/update/_update/resp-code-and-json/")
+ resp = Couch.post("/#{db_name}/_design/update/_update/resp-code-and-json/")
assert resp.status_code == 302
assert resp.body["ok"] == true
end