mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 17:01:11 +00:00
Merge pull request #18 from metosin/EatYourOwn
Match is injected into request
This commit is contained in:
commit
1bd236f477
4 changed files with 108 additions and 14 deletions
59
README.md
59
README.md
|
|
@ -179,7 +179,7 @@ Path-based routing:
|
||||||
|
|
||||||
On match, route meta-data is returned and can interpreted by the application.
|
On match, route meta-data is returned and can interpreted by the application.
|
||||||
|
|
||||||
Routers also support meta-data compilation enabling things like fast [Ring](https://github.com/ring-clojure/ring) or [Pedestal](http://pedestal.io/) -style handlers. Compilation results are found under `:handler` in the match. See [configuring routers](configuring-routers) for details.
|
Routers also support meta-data compilation enabling things like fast [Ring](https://github.com/ring-clojure/ring) or [Pedestal](http://pedestal.io/) -style handlers. Compilation results are found under `:handler` in the match. See [configuring routers](#configuring-routers) for details.
|
||||||
|
|
||||||
## Ring
|
## Ring
|
||||||
|
|
||||||
|
|
@ -281,6 +281,59 @@ Nested middleware works too:
|
||||||
|
|
||||||
Ring-router supports also 3-arity [Async Ring](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html), so it can be used on [Node.js](https://nodejs.org/en/) too.
|
Ring-router supports also 3-arity [Async Ring](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html), so it can be used on [Node.js](https://nodejs.org/en/) too.
|
||||||
|
|
||||||
|
### Meta-data based extensions
|
||||||
|
|
||||||
|
The routing `Match` is injected into a request and can be extracted with `reitit.ring/get-match` helper. It can be used to build dynamic extensions to the system.
|
||||||
|
|
||||||
|
A middleware to guard routes:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(require '[clojure.set :as set])
|
||||||
|
|
||||||
|
(defn wrap-enforce-roles [handler]
|
||||||
|
(fn [{:keys [::roles] :as request}]
|
||||||
|
(let [required (some-> request (ring/get-match) :meta ::roles)]
|
||||||
|
(if (and (seq required) (not (set/intersection required roles)))
|
||||||
|
{:status 403, :body "forbidden"}
|
||||||
|
(handler request)))))
|
||||||
|
```
|
||||||
|
|
||||||
|
Mounted to an app via router meta-data (effecting all routes):
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(def handler (constantly {:status 200, :body "ok"}))
|
||||||
|
|
||||||
|
(def app
|
||||||
|
(ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
[["/api"
|
||||||
|
["/ping" handler]
|
||||||
|
["/admin" {::roles #{:admin}}
|
||||||
|
["/ping" handler]]]]
|
||||||
|
{:meta {:middleware [wrap-enforce-roles]}})))
|
||||||
|
```
|
||||||
|
|
||||||
|
Anonymous access to public route:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(app {:request-method :get, :uri "/api/ping"})
|
||||||
|
; {:status 200, :body "ok"}
|
||||||
|
```
|
||||||
|
|
||||||
|
Anonymous access to guarded route:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(app {:request-method :get, :uri "/api/admin/ping"})
|
||||||
|
; {:status 403, :body "forbidden"}
|
||||||
|
```
|
||||||
|
|
||||||
|
Authorized access to guarded route:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(app {:request-method :get, :uri "/api/admin/ping", ::roles #{:admin}})
|
||||||
|
; {:status 200, :body "ok"}
|
||||||
|
```
|
||||||
|
|
||||||
## Validating route-tree
|
## Validating route-tree
|
||||||
|
|
||||||
**TODO**
|
**TODO**
|
||||||
|
|
@ -297,10 +350,6 @@ Ring-router supports also 3-arity [Async Ring](https://www.booleanknot.com/blog/
|
||||||
|
|
||||||
**TODO**
|
**TODO**
|
||||||
|
|
||||||
## Custom extensions
|
|
||||||
|
|
||||||
**TODO**
|
|
||||||
|
|
||||||
## Configuring Routers
|
## Configuring Routers
|
||||||
|
|
||||||
Routers can be configured via options to do things like custom coercion and compilatin of meta-data. These can be used to do things like [`clojure.spec`](https://clojure.org/about/spec) validation of meta-data and fast, compiled [Ring](https://github.com/ring-clojure/ring/wiki/Concepts) or [Pedestal](http://pedestal.io/) -style handlers.
|
Routers can be configured via options to do things like custom coercion and compilatin of meta-data. These can be used to do things like [`clojure.spec`](https://clojure.org/about/spec) validation of meta-data and fast, compiled [Ring](https://github.com/ring-clojure/ring/wiki/Concepts) or [Pedestal](http://pedestal.io/) -style handlers.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
(ns reitit.middleware
|
(ns reitit.middleware
|
||||||
(:require [reitit.core :as reitit]))
|
(:require [meta-merge.core :refer [meta-merge]]
|
||||||
|
[reitit.core :as reitit]))
|
||||||
|
|
||||||
(defprotocol ExpandMiddleware
|
(defprotocol ExpandMiddleware
|
||||||
(expand-middleware [this]))
|
(expand-middleware [this]))
|
||||||
|
|
@ -40,5 +41,9 @@
|
||||||
(ensure-handler! path meta scope)
|
(ensure-handler! path meta scope)
|
||||||
((compose-middleware middleware) handler)))
|
((compose-middleware middleware) handler)))
|
||||||
|
|
||||||
(defn router [data]
|
(defn router
|
||||||
(reitit/router data {:compile compile-handler}))
|
([data]
|
||||||
|
(router data nil))
|
||||||
|
([data opts]
|
||||||
|
(let [opts (meta-merge {:compile compile-handler} opts)]
|
||||||
|
(reitit/router data opts))))
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,18 @@
|
||||||
(fn
|
(fn
|
||||||
([request]
|
([request]
|
||||||
(if-let [match (reitit/match-by-path router (:uri request))]
|
(if-let [match (reitit/match-by-path router (:uri request))]
|
||||||
((:handler match) request)))
|
((:handler match) (assoc request ::match match))))
|
||||||
([request respond raise]
|
([request respond raise]
|
||||||
(if-let [match (reitit/match-by-path router (:uri request))]
|
(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}))
|
{::router router}))
|
||||||
|
|
||||||
(defn get-router [handler]
|
(defn get-router [handler]
|
||||||
(some-> handler meta ::router))
|
(some-> handler meta ::router))
|
||||||
|
|
||||||
|
(defn get-match [request]
|
||||||
|
(::match request))
|
||||||
|
|
||||||
(defn coerce-handler [[path meta] {:keys [expand]}]
|
(defn coerce-handler [[path meta] {:keys [expand]}]
|
||||||
[path (reduce
|
[path (reduce
|
||||||
(fn [acc method]
|
(fn [acc method]
|
||||||
|
|
@ -53,6 +56,10 @@
|
||||||
(if-let [handler (resolved-handler (:request-method request))]
|
(if-let [handler (resolved-handler (:request-method request))]
|
||||||
(handler request respond raise))))))))
|
(handler request respond raise))))))))
|
||||||
|
|
||||||
(defn router [data]
|
(defn router
|
||||||
(reitit/router data {:coerce coerce-handler
|
([data]
|
||||||
:compile compile-handler}))
|
(router data nil))
|
||||||
|
([data opts]
|
||||||
|
(let [opts (meta-merge {:coerce coerce-handler
|
||||||
|
:compile compile-handler} opts)]
|
||||||
|
(reitit/router data opts))))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
(ns reitit.ring-test
|
(ns reitit.ring-test
|
||||||
(:require [clojure.test :refer [deftest testing is]]
|
(:require [clojure.test :refer [deftest testing is]]
|
||||||
[reitit.middleware :as middleware]
|
[reitit.middleware :as middleware]
|
||||||
[reitit.ring :as ring])
|
[reitit.ring :as ring]
|
||||||
|
[clojure.set :as set])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:import (clojure.lang ExceptionInfo))))
|
(:import (clojure.lang ExceptionInfo))))
|
||||||
|
|
||||||
|
|
@ -122,3 +123,35 @@
|
||||||
(app {:uri "/api/users" :request-method :post} respond raise)
|
(app {:uri "/api/users" :request-method :post} respond raise)
|
||||||
(is (= {:status 200, :body [:api :users :post :ok :post :users :api]}
|
(is (= {:status 200, :body [:api :users :post :ok :post :users :api]}
|
||||||
@result)))))))
|
@result)))))))
|
||||||
|
|
||||||
|
(defn wrap-enforce-roles [handler]
|
||||||
|
(fn [{:keys [::roles] :as request}]
|
||||||
|
(let [required (some-> request (ring/get-match) :meta ::roles)]
|
||||||
|
(if (and (seq required) (not (set/intersection required roles)))
|
||||||
|
{:status 403, :body "forbidden"}
|
||||||
|
(handler request)))))
|
||||||
|
|
||||||
|
(deftest enforcing-meta-data-rules-at-runtime-test
|
||||||
|
(let [handler (constantly {:status 200, :body "ok"})
|
||||||
|
app (ring/ring-handler
|
||||||
|
(ring/router
|
||||||
|
[["/api"
|
||||||
|
["/ping" handler]
|
||||||
|
["/admin" {::roles #{:admin}}
|
||||||
|
["/ping" handler]]]]
|
||||||
|
{:meta {:middleware [wrap-enforce-roles]}}))]
|
||||||
|
|
||||||
|
(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}})))))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue