kekkonen

ikitommi 2016-12-19T07:18:42.000005Z

@metametadata there is also a simple router in kekkonen.ring, see https://github.com/metosin/kekkonen/blob/e52031da4a07a8f219475d113a77202d8bf86084/src/kekkonen/ring.clj#L257-L274

ikitommi 2016-12-19T07:20:43.000007Z

if you have static assets, you can do

(require '[kekkonen.ring :as r])

(def my-api (api …))

(def app
   (r/routes
     my-api
     (r/match “/” #{:get} my-index-page)
     (r/match “/public” my-resources)))

metametadata 2016-12-19T13:44:11.000008Z

cool, thanks!

metametadata 2016-12-19T14:36:06.000009Z

While playing with the lib I also struggled to understand the difference between using interceptors vs custom :user metadata fields in handlers - they both only modify the context, right?.. E.g. auth is implemented as user metadata function:

(defnk ^:command reset-counter!
  "reset the counter. Just for admins."
  {::roles #{:admin}} ; <-------- meta
  [counter]
  (success (reset! counter 0)))
but adding file upload support is implemented using an interceptor:
(defnk ^:command upload
  "Upload a file to a server"
  {:interceptors [[upload/multipart-params]]} ; <-------- interceptor
  [[:state file]
   [:request [:multipart-params upload :- upload/TempFileUpload]]]
  (reset! file upload)
  (success (dissoc upload :tempfile)))
It was also confusing why returning empty context from the user meta function results in not showing the handler in Swagger UI and (less surprising but still not obvious) returning 404 on xhr request to the unauthorised action:
(defn require-roles [required]
  (fn [context]
    (let [roles (-> context :user :roles)]
      (if (seq (set/intersection roles required))  ;  <-- can return nil context
        context))))

ikitommi 2016-12-19T15:09:15.000010Z

in the end, there are only interceptors & handlers. The user-meta is an extra layer on top of interceptors.

ikitommi 2016-12-19T15:11:54.000011Z

at dispatcher creation time, all keys in the handler/namespace meta are transformed into interceptors. So, to use ::roles, one needs to register a meta-hander, which takes the value #{:admin} and returns an interceptor for it.

ikitommi 2016-12-19T15:13:04.000012Z

so all the user-meta keys need a key => value => Interceptor function registered into the dispatcher. otherwise, a creation-time error is given.

ikitommi 2016-12-19T15:13:38.000013Z

Kekkonen eats it’s own dogfood: the :interceptors is just a pre-registered user-meta-key.

ikitommi 2016-12-19T15:16:21.000016Z

That gives nice creation-time guarantees. For example, we have been using a require-role interceptor, and if there is a predefined set of roles, we can enforce those at creation-time.

ikitommi 2016-12-19T15:17:04.000017Z

so, you can't write ::roles #{:bogus} if there is no such role. Same for feature flags etc. Fail early.

metametadata 2016-12-19T15:21:26.000019Z

thank you! I think I'm starting to get it now so the handler can be actually rewritten as this

(defnk ^:command reset-counter!
  "reset the counter. Just for admins."
  {:interceptors [security/api-key-authenticator
                                   (security/require-roles #{:admin})]
   }
  [counter]
  (success (reset! counter 0)))

metametadata 2016-12-19T15:22:39.000020Z

but I guess it makes the handler less "portable" now because security/api-key-authenticator assumes there's a Ring layer

metametadata 2016-12-19T15:26:18.000021Z

this is originally a snippet from https://github.com/metosin/kekkonen-sample btw

ikitommi 2016-12-19T15:26:30.000024Z

yes, Kekkonen doesn’t make it hard to mix the pure-clojure handlers from ring-ones. But one can push all the ring-spesific stuff into the ring-handler.

ikitommi 2016-12-19T15:27:02.000026Z

so, read from the request there and push results into context under some other key.

metametadata 2016-12-19T15:27:10.000027Z

right

ikitommi 2016-12-19T15:28:16.000028Z

for example, we use :user key to store info of the user. The handlers (or namespaces) use interceptors that just assert that it has right data. Might be a good idea to remote the :request from the normal handlers totally. would make it easier to keep ‘em ring-free.

ikitommi 2016-12-19T15:28:58.000029Z

(defnk ^:command reset-counter!
  "reset the counter. Just for admins."
  {:interceptors [security/api-key-authenticator
                  [security/require-roles #{:admin}]}}}
  [counter]
  (success (reset! counter 0)))

ikitommi 2016-12-19T15:29:37.000032Z

that :interceptor-form is most data-oriented.

metametadata 2016-12-19T15:31:09.000036Z

that's nice, I think I saw smt like this in compojure-api middlewares

ikitommi 2016-12-19T15:32:28.000039Z

yes, the syntax is from Duct.

metametadata 2016-12-19T15:40:19.000040Z

so it looks like that api.admin/reset-counter! handler's meta is calculated on forming Swagger UI as well as on the GET /kekkonen/handlers but somehow not on GET /kekkonen/handler for the api.admin/reset-counter!

metametadata 2016-12-19T15:41:02.000041Z

(I've put logs into securoty interceptor functions)

ikitommi 2016-12-19T16:03:59.000042Z

seems so. Hmm. Issue or PR welcome!

ikitommi 2016-12-19T16:05:32.000043Z

About the nil contexts… could be changed if there is a better way. Thought it was safe not to see any apis that are not meant to be seen, but that’s bad in development.

ikitommi 2016-12-19T16:06:37.000044Z

there is the kekkonen.interceptor/terminate that can be called on the context to kill the request processing (the pedestal way).

ikitommi 2016-12-19T16:06:41.000045Z

ideas welcome.

metametadata 2016-12-19T16:33:30.000046Z

Got it, I'll prob add an issue

metametadata 2016-12-19T16:34:58.000047Z

kekkonen.interceptor/terminate works almost the same as failure! - i.e. the action is visible in Swagger UI but throws an error status code (404 and 400 respectively)

metametadata 2016-12-19T16:36:15.000048Z

yeah, I'm not sure I see much point in hiding the handlers in Swagger - it's like the security through obscurity 🙂

metametadata 2016-12-19T16:39:32.000050Z

on the other hand, if API's Swagger UI is available to the public on purpose then it makes sense to hide some stuff..