diff --git a/src/reitit/middleware.cljc b/src/reitit/middleware.cljc index 71af5d15..378c9974 100644 --- a/src/reitit/middleware.cljc +++ b/src/reitit/middleware.cljc @@ -1,5 +1,6 @@ (ns reitit.middleware - (:require [reitit.core :as reitit])) + (:require [meta-merge.core :refer [meta-merge]] + [reitit.core :as reitit])) (defprotocol ExpandMiddleware (expand-middleware [this])) @@ -40,5 +41,9 @@ (ensure-handler! path meta scope) ((compose-middleware middleware) handler))) -(defn router [data] - (reitit/router data {:compile compile-handler})) +(defn router + ([data] + (router data nil)) + ([data opts] + (let [opts (meta-merge {:compile compile-handler} opts)] + (reitit/router data opts)))) diff --git a/src/reitit/ring.cljc b/src/reitit/ring.cljc index 56c98c17..354d2f59 100644 --- a/src/reitit/ring.cljc +++ b/src/reitit/ring.cljc @@ -18,15 +18,18 @@ (fn ([request] (if-let [match (reitit/match-by-path router (:uri request))] - ((:handler match) request))) + ((:handler match) (assoc request ::match match)))) ([request respond raise] (if-let [match (reitit/match-by-path router (:uri request))] - ((:handler match) request respond raise)))) + ((:handler match) (assoc request ::match match) respond raise)))) {::router router})) (defn get-router [handler] (some-> handler meta ::router)) +(defn get-match [request] + (::match request)) + (defn coerce-handler [[path meta] {:keys [expand]}] [path (reduce (fn [acc method] @@ -53,6 +56,10 @@ (if-let [handler (resolved-handler (:request-method request))] (handler request respond raise)))))))) -(defn router [data] - (reitit/router data {:coerce coerce-handler - :compile compile-handler})) +(defn router + ([data] + (router data nil)) + ([data opts] + (let [opts (meta-merge {:coerce coerce-handler + :compile compile-handler} opts)] + (reitit/router data opts)))) diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index 12dddc68..3ef1a2e2 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -1,7 +1,8 @@ (ns reitit.ring-test (:require [clojure.test :refer [deftest testing is]] [reitit.middleware :as middleware] - [reitit.ring :as ring]) + [reitit.ring :as ring] + [clojure.set :as set]) #?(:clj (:import (clojure.lang ExceptionInfo)))) @@ -121,4 +122,38 @@ respond (partial reset! result), raise ::not-called] (app {:uri "/api/users" :request-method :post} respond raise) (is (= {:status 200, :body [:api :users :post :ok :post :users :api]} - @result))))))) + @result)))))) + + (testing "runtime extensions for meta-data" + (let [enforce-roles (fn [handler] + (fn [{:keys [::roles] :as request}] + (let [required (some-> request + (ring/get-match) + :meta + ::roles)] + (if (or (not (seq required)) + (set/intersection required roles)) + (handler request) + {:status 403 :body "forbidden"})))) + router (ring/router + [["/api" + ["/ping" handler] + ["/admin" {::roles #{:admin}} + ["/ping" handler]]]] + {:meta {:middleware [enforce-roles]}}) + app (ring/ring-handler router)] + + (testing "public handler" + (is (= {:status 200, :body [:ok]} + (app {:uri "/api/ping" :request-method :get})))) + + (testing "runtime-enforced handler" + (testing "without needed roles" + (is (= {:status 403 :body "forbidden"} + (app {:uri "/api/admin/ping" + :request-method :get})))) + (testing "with needed roles" + (is (= {:status 200, :body [:ok]} + (app {:uri "/api/admin/ping" + :request-method :get + ::roles #{:admin}}))))))))