luminus-framework / luminus

documentation site for Luminus framework
http://www.luminusweb.net/
629 stars 121 forks source link

Can't make method spoofing on HTML forms #251

Closed juniorgarcia closed 4 years ago

juniorgarcia commented 4 years ago

I looked at the documentation and can't make method spoofing on my forms. I'm unable to use methods other than GET and POST. I don't believe that's an error in my routes because I can use other HTTP methods through cURL or similar.

Currently I have a button to delete a row which is a submit to the following form:

<form action="/todos/{{todo.id}}" class="ml-3" method="POST">
  {% csrf-field %}
  <input type="hidden" name="_method" value="delete">
  <input type="hidden" name="todo" value="{{todo.id}}">
  <input type="submit" value="Delete" class="btn btn-danger">
</form>

Look at the input named _method. Is this the right way to spoof methods in Luminus?

Here is my route function for this entity:

(defn todos-routes []
  [["/todos"
    {:middleware [middleware/wrap-csrf
                  middleware/wrap-formats]
     :get list}]
   ["/todos/:todo"
    {:middleware [middleware/wrap-csrf
                  middleware/wrap-formats]
     :get edit
     :delete delete}]])

I know that there isn't a :post key, but I assumed I didn't have to handle that unless I would like to use the POST method directly, which is not the case yet.

The delete method mapped to the :delete key on the routes is like the following:

(defn delete [todo]
  (delete-todo todo)
  (redirect "/todos"))

Anyway, everytime I submit a form, the only methods that work are GET and POST. But as I said before, using cURL directly setting the method to any other compliant to the HTTP specification works.

yogthos commented 4 years ago

There's no built in support for method spoofing as it's not part of HTTP spec, but you could add a middleware function to spoof the HTTP method in the <app>.middleware namespace as follows:

(defn wrap-spoof-method [handler]
  (fn [request]
    (handler
     (update request :request-method
             #(or (some-> request :params :_method keyword) %)))))

(defn wrap-base [handler]
  (-> ((:middleware defaults) handler)
      wrap-spoof-method
      (wrap-defaults
        (-> site-defaults
            (assoc-in [:security :anti-forgery] false)
            (assoc-in  [:session :store] (ttl-memory-store (* 60 30)))))
      wrap-internal-error))