具有防伪造和环模拟测试POST路线

问题描述 投票:5回答:3

我想用ring.mock编写一个简单的POST请求测试 - 是这样的:

(testing "id post route"
    (let [response (app (mock/request :post "/" {:id "Foo"}))]
      (is (= 302 (:status response)))))

然而,由于我用的是wrap-csrf中间件,我收到了403个状态响应,因为我不提供防伪标记。

有没有写与ring.mock POST测试,没有禁用wrap-csrf中间件的方法吗?

testing clojure mocking csrf ring
3个回答
4
投票

你要什么测试?这不是从你的问题清楚,为什么你不应该禁用防伪中间件都还不清楚。

  1. 如果您正在测试一个Web服务,你不应该在所有使用CSRF令牌和切换到不同的安全机制(例如授权头,API令牌等)
  2. 如果你想测试终端到终端的流程,包括CSRF逻辑,那么你需要通过与会话ID一起先调用相应的URL,并从响应提取它(如解析隐藏字段),这样你可以得到一个有效的CSRF令牌在测试请求中使用它们。
  3. 如果你想测试你的handler逻辑对其进行测试,而不捆绑“基础设施”的中间件。还有,用嘲讽的防伪中间件,如果你可以没有它在测试中应用到您的处理功能,问题消失没有意义的。

4
投票

首先,我想回应一些关于此地另一篇文章的方法和推理的要点:

  1. CSRF令牌仅需要HTML登录,但没有必要或适当的Web服务。
  2. 与环/的Compojure,可以直接测试处理程序,或包裹在它们仅在必要的中间件。在下面的例子中,我需要app它代表了整个应用程序,但你可以在命令如果需要直接测试端点绕过CSRF。这将允许您测试给定的终点,但不考了CSRF保护工作正常。

假设上述情况,你可能需要测试的登录过程是否正常工作和CSRF集成。下面的测试助手应该帮助你沿着这条路径:

(ns myapp.test.handler-test
  (:require [clojure.test :refer :all]
            [ring.mock.request :refer [request]]
            [net.cgrand.enlive-html :as html]
            [myapp.handler :refer [app]]))

(defn get-session
  "Given a response, grab out just the key=value of the ring session"
  [resp]
  (let [headers (:headers resp)
        cookies (get headers "Set-Cookie")
        session-cookies (first (filter #(.startsWith % "ring-session") cookies))
        session-pair (first (clojure.string/split session-cookies #";"))]
    session-pair))

(defn get-csrf-field
  "Given an HTML response, parse the body for an anti-forgery input field"
  [resp]
  (-> (html/select (html/html-snippet (:body resp)) [:input#__anti-forgery-token])
      first
      (get-in [:attrs :value])))

(defn get-login-session!
  "Fetch a login page and return the associated session and csrf token"
  []
  (let [resp (app (request :get "/login"))]
    {:session (get-session resp)
     :csrf (get-csrf-field resp)}))

(defn login!
  "Login a user given a username and password"
  [username password]
  (let [{:keys [csrf session]} (get-login-session!)
        req (-> (request :post "/login")
                (assoc :headers {"cookie" session})
                (assoc :params {:username username
                                :password password})
                (assoc :form-params {"__anti-forgery-token" csrf}))]
    (app req)
    session))

在上文中,我假设/login页面使用__anti-forgery-token隐藏的输入和要测试的此。你也可以考虑把会话数据的CSRF令牌,这使得它更容易与像curl可以从一个响应,为连续请求使用的文件保存会话数据的工具来测试。

由于我拉令牌出了HTML的身体,我决定使用enlive,这样我可以使用CSS选择器来定义在那里我从一个简单的和声明的方式拉这个数据。

为了利用上述的,你可以打电话login!,然后用它要与相同的会话进行身份验证的连续请求,如返回会话数据:

 (deftest test-home-page-authentication
  (testing "authenticated response"
    (let [session (login! "bob" "bob")
          request (-> (request :get "/")
                      (assoc :headers {"cookie" session}))]
      (is (= 200 (:status (app request)))))))

这里假设你有Bob的用户名/密码和鲍勃在任何权威性的后端使用的是成立。你可能会需要有一个创建该用户在此之前登录过程将工作一个用户名/密码设置步骤。


0
投票

我们可以使用csrf在测试中禁用(assoc site-defaults :security false)。完整的代码是这样的:

; Create a copy of testing app in utilities.testing
; by wrapping handler with testing middlewares

(ns utilities.testing
    (:require [your-web-app.handler :refer [path-handler]]
              [ring.middleware.defaults :refer [wrap-defaults site-defaults]]))

; Disabling CSRF for testing
(def app
    (-> path-handler
        (wrap-defaults (assoc site-defaults :security false))))

现在你可以使用这个应用程序在测试中

(ns users.views-test
    (:require [utilities.testing :refer [app]]
              ;...
              ))

;...
(testing "id post route"
    (let [response (app (mock/request :post "/" {:id "Foo"}))]
      (is (= 302 (:status response)))))

© www.soinside.com 2019 - 2024. All rights reserved.