pedestal

simongray 2020-11-24T10:57:04.223Z

Question: can I access the interceptor chains of other routes from inside an interceptor (through some path in the context)?

simongray 2020-11-24T11:01:36.225800Z

I guess I can access the router interceptor, so could that be used to construct interceptor chains for named routes inside another interceptor?

orestis 2020-11-25T16:52:16.247200Z

I still don’t quite get it :) are you saying something like “the UI shouldn’t contain links that will be rejected anyway”? And then using your pedestal routes to figure that out?

orestis 2020-11-25T16:53:54.249400Z

I’m not 100% on the extra data that you can put in pedestals routes (I don’t heavily use them and for front end I use reitit) but I would probably move this info out to some other data structure and generate both interceptors and permission logic based on it...

simongray 2020-11-25T23:05:59.251100Z

I managed to make it work earlier. Explained here: https://github.com/kuhumcst/louis-hjelmslev/blob/development/pedestal-sp.md#bidirectional-authorisation

simongray 2020-11-25T23:09:15.251700Z

I’m still ruminating on the frontend part, specifically how to integrate this with a SPA :)

simongray 2020-11-25T23:11:40.251900Z

And sure you can always go back one level, but that introduces coupling, along with additional configuration and parameters to pass along to functions. The challenge is to get rid of those :)

orestis 2020-11-26T07:30:48.254600Z

We introduced a permissions namespace that speaks the domain language, and many resources (served by graphql, but should work in any case) expose flags about permitted operations...

orestis 2020-11-26T07:31:27.255700Z

So that the SPA can do data-driven UI. Still in the discovery phase though...

simongray 2020-11-26T08:34:44.255900Z

Yeah, something like that is definitely an option. Direct coupling with an API is quite convenient, but if I want this to be a in a library I’m going to need something slightly more generic. Maybe it’ll take a few walks before I figure out a good way 🙂

simongray 2020-11-24T12:16:30.226300Z

Ok, so I had a look at the Pedestal source code and this is what I came up with:

(defn- find-router*
  [stack]
  (loop [[ic & remaining] stack]
    (if (= (:name ic) ::route/router)
      ic
      (recur remaining))))

(def find-router
  (memoize find-router*))

(defn context->router
  [ctx]
  (find-router (::chain/stack ctx)))

;; Modified version of `io.pedestal.http.route/try-routing-for`
(defn routing-for
  [router query-string verb]
  (let [context {:request {:path-info query-string
                           :request-method verb}}
        context ((:enter router) context)]
    (:route context)))
And then inside the Interceptor you can do e.g.
(-> (context->router ctx)
    (routing-for "/" :get))
I’m not sure if it’s possible to resolve routes by route-names in this way, though.

simongray 2020-11-24T12:56:37.226500Z

It seems you can do this to get a URL from the context and a route-name:

(defn url-for
  [{:keys [bindings] :as ctx} & args]
  (let [*url-for* @(get bindings #'io.pedestal.http.route/*url-for*)]
    (apply *url-for* args)))

simongray 2020-11-24T12:58:48.226700Z

But it doesn’t seem possible to get a fully bidirectional variant, since you still need a verb and possibly some route-specific params to resolve to the correct routing.

orestis 2020-11-24T14:56:20.226900Z

What do you need to do?

2020-11-24T16:27:56.228100Z

if i'm running a pedestal server in a separate thread (for testing purposes), how can i clean it up, or kill it? i've tried (.interrupt thread-with-running-server) but it doesn't seem to help

2020-11-24T16:28:41.228200Z

we also tried putting the server in an atom and running (http/stop @server-atom) but since running the server blocks, it never gets swapped into the atom in the first place

2020-11-24T16:28:53.228400Z

at least, this is what we think is happening

2020-11-24T16:48:08.229Z

should this just be handled by setting :http/join? to false?

2020-11-24T18:00:44.229200Z

ok, figured this out. need to add a try catch in the function passed to the thread to allow it to be interrupt-able. then i can run whatever shutdown actions i need.

orestis 2020-11-24T18:02:47.229700Z

Join to false seems more robust to me

🙏 1
simongray 2020-11-24T18:55:58.230Z

I’m trying to make a function to be used when generating frontend content that can determine whether access to a route is possible or not based on the existence of certain interceptors so that I can e.g. disable/remove certain links dynamically, but I want to accomplish it by reading from the service context only.

simongray 2020-11-24T19:00:11.230700Z

So that you can do something like [(restrict-to ...) ....] in the interceptor chain of one route and then in a response content-generating interceptor of another route you call something like (allowed? ...) in some hiccup-generating logic to determine whether a route is authenticated and/or authorised for the SAML user, using this information to modify the HTML content being served in order to nudge the user away from clicking links that will end badly (e.g. 403 forbidden).

simongray 2020-11-24T19:01:14.230900Z

The trick is trying to do it in a decoupled way, only considering what’s in context.

simongray 2020-11-24T19:15:46.231400Z

I admit it's kind of an experimental approach, but I did have to solve something similar at my old work right before I left in their huge legacy system (in fact, I hear they're still trying to solve it) and I have this feeling that a fairly integrated system could be made from a few basic interceptors and some helper functions.

souenzzo 2020-11-24T20:16:29.232Z

Do pedestal (or some pedestal lib) do routing given the HTTP domain?

souenzzo 2020-11-24T20:32:14.232400Z

In ring/pedestal, the domain is the :host

hlship 2020-11-24T22:57:38.243100Z

So, let me tell you a story ... the other day, we had a network problem in our load balancers; this caused IO problems writing the response to the client (`io.pedestal.http.impl.servlet-interceptor/ring-response`); the exceptions thrown were caught by stylobate and logged. The exception occured inside io.pedestal.http.impl.servlet_interceptor/write-body. • Problem was, other interceptors had attached large maps to the :request ; logging that produced 800KB of output per exception which caused a cascade of other problems • When using io.pedestal.http/create-server you end up threading through io.pedestal.http.impl.servlet-interceptor/http-interceptor-service-fn where stylobate and ring-response interceptors are added • This is in internal implementation, and things are private; very hard to override stylobate's behavior here without forcing access to private functions So, I'm changing our interceptors to, on leave/error, remove the added keys, but that's not totally foolproof. I really just want to tweak stylobate's logging to not include the context (or, perhaps, carefully include only selected keys from the context and request). I'm thinking the best solution might be to add our own own top-level error reporting interceptor and put a ring-response directly after; this will do the expected response writing work, and then the Pedestal-added ring-response will see that the response has already been committed (and the :body removed) and do nothing. TL;DR; if you are putting large maps into the context and they percolate back up out of your code, and an exception is thrown when writing the response stream to the client, then you will experience logging grief, and there isn't an easy way to override Pedestal's behavior here.

👍 4
hlship 2020-11-27T16:57:41.267900Z

I'm trying a solution, an interceptor that sits early in the stack, and green-lists all the context and request keys (on enter) and uses select-keys on leave/error (obviously, also allowing :response).