How to specify a time-out for http.agent? - clojure

I am working on the following in Clojure:
(defn call-soap-function-for-url [url function-name body]
(http/buffer-bytes
(http/http-agent (str url "/soap.php")
:connect-timeout 2000
:read-timeout 2000
:method "POST"
:body body
)))
The problem is that if the url doesn't exist (for instance), the http-agent blocks the current execution indefinitely, ignoring the timeout I have specified.
What am I please doing wrong?
Thank you for your help - I really appreciate it.

Related

How do I obtain all of plain POST form parameters in compojure.api.sweet?

I have a route for which I have to get all of parameters from body.
I have no choice because it will be called from a third party system so I have to work with simple tools. I also don't have complete control of the data that is passed and I need everything anyway. I need every field but I don't know what those are.
One of the solutions would be to cherry-pick all fields that can be submitted and use optional parameters, but that's not ideal because I'll have to test that by hand for all request types.
And as I figured out there are at least 30 different submitted fields in my request which are needed to be concatenated for HMAC request validation. You can guess how my handler looks like... (Hint: {Field :- s/Str ""})
And I have one more request that is going to be at least this big but different. And I plan more in the future. Oof.
So: How do I get all of the submitted body parameters without any deserialization?
HTTP Body string, parameter map or InputStream are completely acceptable solutions. At this point anything is better than specifying all of that by hand. (VIM macros -- thanks for all your help)
My code:
(ns api.compojure-api
(:require [compojure.api.sweet :refer :all]
[schema.core :as s]
[ring.util.http-response :refer :all]
[ring.middleware.params]
[muuntaja.middleware]))
(def app
(api
(context "/something" []
(POST "/api/:variable" req
:middleware [;;ring.middleware.params/wrap-params ;; This does nothing
;;muuntaja.middleware/wrap-params ;; This does nothing
(fn [handler]
(fn
([request]
(println "request" request) ;; This prints the request but params are not there, they are not deserialized
(handler request))
([request respond raise]
(println "request" request)
(handler request respond raise))))]
;; :body-params [some-body-param :- s/Str] ;; This works if I know the fields. But I don't want to know or specify.
;; :coercion nil ;; stops coercing the output, but I need to not coerce the input into String or Object too.
(println (:params req)) ;; this prints only value from path (`:variable`), but not from body.
(ok))
)))
I've tried multiple things but nothing worked so far.
I can't copy the methods from compojure examples that wrap whole routes because compojure.api.sweet is different from plain compojure.
Versions:
[org.clojure/clojure "1.10.1"]
[metosin/compojure-api "1.1.13"]
[ring/ring-core "1.8.0"]
[metosin/muuntaja "0.6.6"]
[metosin/ring-http-response "0.9.1"]
All of these solutions return nil:
How to get all the params of a POST request with Compojure
muuntaja.middleware/wrap-format middleware (and together with wrap-params in both orders) also doesn't do anything.
I also tried to print (slurp (:body req)) and ring.util.request/body-string but it's empty. In fact this is the output of (println (:body req)):
#object[org.eclipse.jetty.server.HttpInputOverHTTP 0x12335807 HttpInputOverHTTP#12335807[c=0,q=0,[0]=null,s=EOF]]
Which says that parameter s=EOF. But I've set the content type to form and added some data to the body.

Implementing a login system with re-frame

I am new to re-frame and not quite sure how to build a user authentication/authorization system with it.
From what I gathered I should create an auth interceptor and place my auth logic inside :before section then inject the interceptor into every events reg-event-db and reg-event-fx that I want to protect.
Am I on the right track?
Not sure if my solution is particularly idiomatic, but I used something like the following in one of my projects. Consider it a Works For Me.
Create a map for the ajax request with a special value for error cases (ignore the context-uri function):
(defn xhrio-map [method path format success-event]
{:method method
:uri (context-uri path)
:timeout 5000
:response-format format
:on-success [success-event]
:on-failure [::ajax-failure]})
Then I use an fx handler for the failure (this is a bit more complicated as it also handles a loading indicator):
(rf/reg-event-fx
::ajax-failure
(fn [{:keys [db]} [_ http-result]]
(if (= 403 (:status http-result))
{:db (assoc db :loading-indicator nil)
:dispatch [::logout]}
{:db (assoc db :loading-indicator nil)
:dispatch
[::error-msg (str "Error fetching from " (:uri http-result)
": " (:response http-result))]})))
The ::logout events sets the document location. This also triggers the logout in the backend.
(rf/reg-event-fx
::logout
(fn [coefx [ev]]
{::location "./logout"}))
Finally, the loading of resources works like this:
(defn load-with-indicator [db xhrio-data]
{:db (assoc db :loading-indicator true)
:http-xhrio xhrio-data})
(rf/reg-event-fx
::load-documentation
(fn [{:keys [db]} _]
(load-with-indicator
db
(xhrio-map :get "documentation/"
(ajax/json-response-format {:keywords? true})
::received-documentation))))
The :received-documentation is handled by some code which invokes the correct display functions.
This uses the day8.re-frame/http-fx and ajax.core
On the backend, I use something similar to the demo code I published over at https://github.com/ska2342/ring-routes-demo.
Hope that helps.
License of the code in this post
In addition to the default license of the StackOverflow site, I also publish these lines under the Eclipse Public License either version 1.0 or (at your option) any later version.

Finding Matched Path in Clojure Web Service

I hope I can explain this in such a way that it makes sense!
I'm using Liberator to prototype some web services that I need to expose to clients and have route(s) defined like so:
(defroutes fish
(context "/fish"
[]
(ANY "/cod/:id/count"
[id]
(cod-fish id))))
(def handler
(-> fish
wrap-params
path-wrapper))
The intention of path-wrapper is to output some information about the matched path. It currently looks like so:
(defn path-wrapper
[handler]
(fn [request]
(println "in" (:request-method request) (:uri request))
(let [response (handler request)]
(println "out")
response)))
This prints out what you'd expect:
in :get /fish/cod/123/count
out
However, what I'd like it to print out is:
in :get /fish/cod/:id/count
out
That is, the path that matched rather than the URI that matched it.
I'm almost certain that the answer is in Clout somewhere but I don't seem able to find it! :(
Any advice?
Cheers,
Peter
In cases like this I'm fond of putting in a debugging statement like:
(let [response .... ]
(log/errorf "in: request was: %s"
(with-out-str (clojure.pprint/pprint request))
....
and look for the data you want in the output (then remove the statement) or if you have a working and modern emacs+cider environment you can add debugging to the function with C-uC-cC-c and catch the value of request that way. If the data you wan't is available it will likely be in that output. If you are not using a logging framework then remove the log and with-out-str parts and just call pprint directly.
Sorry if i'm misunderstanding or perhaps is's a typo in the question though:
(let [response handler]
(println "out")
response)
looks like it's returning the handler itself rather than the result of calling that handler, should it be :
(let [response (handler request)]
(println "out")
response)

Faking friend credential function using Midje

I'm trying to test my routing in isolation using Midje. For some routes that hit the database I have no trouble using (provided ...) to isolate the route from a real db call. I've introduced Friend for authentication and I've been unable to fake the call to the credential function.
My credential function looks like this (It's implemented like this because I don't want it getting called just yet):
(defn cred-fn
[creds]
(println (str "hey look I got called with " creds))
(throw (Exception.)))
The middleware for the routes then look like this:
(def app
(-> app-routes
(wrap-json-body {:keywords? true :bigdecimals? true})
wrap-json-response
(wrap-defaults defaults)
(friend/authenticate
{:unauthorized-handler json-auth/login-failed
:workflows [(json-auth/json-login
:login-uri "/login"
:login-failure-handler json-auth/login-failed
:credential-fn auth/cred-fn)]})
(ring-session/wrap-session)))
I've also tried without using the auth-json-workflow, the implementation for the routes looks almost identical and I can add that if it helps but I get the same result.
And then my tests look like this (using ring-mock):
(defn post [url body]
(-> (mock/request :post url body)
(mock/content-type "application/json")
app))
(fact "login with incorrect username and password returns unauthenticated"
(:status (post "/login" invalid-auth-account-json)) => 401
(provided
(auth/cred-fn anything) => nil))
(fact "login with correct username and password returns success"
(:status (post "/login" auth-account-json)) => 200
(provided
(auth/cred-fn anything) => {:identity "root"}))
I then get the following output running the tests:
hey look I got called with {:password "admin_password", :username "not-a-user"}
FAIL at (handler.clj:66)
These calls were not made the right number of times:
(auth/cred-fn anything) [expected at least once, actually never called]
FAIL "routes - authenticated routes - login with incorrect username and password returns unauthenticated" at (handler.clj:64)
Expected: 401
Actual: java.lang.Exception
clojure_api_seed.authentication$cred_fn.invoke(authentication.clj:23)
hey look I got called with {:password "admin_password", :username "root"}
FAIL at (handler.clj:70)
These calls were not made the right number of times:
(auth/cred-fn anything) [expected at least once, actually never called]
FAIL "routes - authenticated routes - login with correct username and password returns success" at (handler.clj:68)
Expected: 200
Actual: java.lang.Exception
clojure_api_seed.authentication$cred_fn.invoke(authentication.clj:23)
So from what I can see the provided statement is not taking effect, and I'm not sure why. Any ideas?
I recently ran into a similar issue, and after some digging I
think I understand why this is happening. Let's take a look at how
the bindings for auth/cred-fn change over time.
(clojure.pprint/pprint (macroexpand '(defn cred-fn
[creds]
(println (str "hey look I got called with " creds))
(throw (Exception.)))))
(def
cred-fn
(clojure.core/fn
([creds]
(println (str "hey look I got called with " creds))
(throw (Exception.)))))
As you can see above, the defn macro interns the symbol cred-fn in the current namespace and binds it to a Var referencing
your dummy function.
(def app
(-> app-routes
(wrap-json-body {:keywords? true :bigdecimals? true})
wrap-json-response
(wrap-defaults defaults)
(friend/authenticate
{:unauthorized-handler json-auth/login-failed
:workflows [(json-auth/json-login
:login-uri "/login"
:login-failure-handler json-auth/login-failed
:credential-fn auth/cred-fn)]})
(ring-session/wrap-session)))
Here's the important piece. At compile time, we thread
app-routes through a series of functions. One of these functions
is friend/authenticate, which takes a map with key :workflows.
The value of :workflows is a vector populated with the results of
a call to json-auth/json-login, which receives auth/credential-fn
as a parameter. Remember, we are inside a def, so this is all
happening at compile time. We look up the symbol cred-fn in the
auth namespace, and pass in the Var which the symbol is bound
to. At this point, that's still the dummy implementation.
Presumably, json-auth/json-login captures this implementation
and sets up a request handler which invokes it.
(fact "login with incorrect username and password returns unauthenticated"
(:status (post "/login" invalid-auth-account-json)) => 401
(provided
(auth/cred-fn anything) => nil))
Now we're at runtime. In our precondition, Midje rebinds the
symbol auth/cred-fn to a Var that references the mock. But the
value of auth/cred-fn has already been captured, when we def'd
app at compile time.
So how come the workaround you posted works? (This was actually the clue that led me to
Eureka moment - thanks for that.)
(defn another-fn []
(println (str "hey look I got called"))
(throw (Exception.)))
(defn cred-fn [creds]
(another-fn))
And in your tests...
(fact "login with incorrect username and password returns unauthenticated"
(:status (post "/login" invalid-auth-account-json)) => 401
(provided
(auth/another-fn) => nil))
(fact "login with correct username and password returns success"
(:status (post "/login" auth-account-json)) => 200
(provided
(auth/another-fn) => {:identity "root"}))
This works because, at compile time, the value of auth/cred-fn that
gets captured is a function that simply delegates to
auth/another-fn. Note that auth/another-fn has not been
evaluated yet. Now in our tests, Midje rebinds auth/another-fn
to reference the mock. Then it executes the post, and somewhere
in the middle-ware, auth/cred-fn gets invoked. Inside
auth/cred-fn, we look up the Var bound to auth/another-fn (which
is our mock), and invoke it. And now of course, the behavior is
exactly as you expected the first time.
The moral of this story is, be careful with the def in Clojure
I'm still not sure why this is happening but I have a work around. If I replace my credential-fn with:
(defn another-fn
[]
(println (str "hey look I got called"))
(throw (Exception.)))
(defn cred-fn
[creds]
(another-fn))
And then create a fake for the new function in the test, like this:
(fact "login with incorrect username and password returns unauthenticated"
(:status (post "/login" invalid-auth-account-json)) => 401
(provided
(auth/another-fn) => nil))
(fact "login with correct username and password returns success"
(:status (post "/login" auth-account-json)) => 200
(provided
(auth/another-fn) => {:identity "root"}))
I get the result I was expecting. cred-fn still gets called but another-fn doesn't get called due to the provided.
If anyone knows why this is the case I'd be interested in knowing. It might be due to the way that the credential function gets called? - https://github.com/marianoguerra/friend-json-workflow/blob/master/src/marianoguerra/friend_json_workflow.clj#L46

How to write a login function in luminus or use friend?

I am beginning to use luminus framework to develop a web app, and I am trying to use friend for auth, I am stacked here, I don't know how to use that like using gem in rails app.
I don't know where should I put the code in luminus, is there anyone can show me a demo. Or tell me what to do next?
Well, you can also tell me how to write a log in function in luminus.
The login sort of works like it is posted in the Luminus Docs. Not sure if you managed to read that part, but I'll show you a simplified version of the code I use. I want to mention that I removed quite a bit of code to make everything a bit easier to understand, so this may not work as-is since I only deleted code and extra parens. Since it is from actual working code, it will work with a bit of tweeking:
The first part is getting the login form:
(defn login-page []
(html5
[:h3 "Login"]
[:form {:method "POST" :action "login"}
[:div "Username:"
[:input {:type "text" :name "username" :required "required"}]]
[:div "Password:"
[:input {:type "password" :name "password" :required "required"}]]
[:div
[:input {:type "submit" :value "Log In"}]]]]))
Notice that there is a "POST" method? In order to get the routes to work, you have to have a "POST" route, but you will also need a "GET" route. This is the simplified version of the "GET" "POST" loop that I have:
(defroutes app-routes
(GET "/login" []
(log/login-page))
(POST "/login" [username password]
(do-login username password)))
The (do-login) function is where I authenticate the user / password combo, which then sets the session, which is shown downn below.
Notice that the POST route needs arguments. The arguments must match the "name" parameters in the form.
Finally, to get it all to work, you have to hook up some sessions. I personally use lib-noir.sessions:
(ns myapp.handler
(:require [noir.session :as sesh])
Then you have to create a map to hold the session, which I'm wrapping in a function here (note that the :key must match whatever you have in your database:
(defn set-user [username]
(sesh/put! :handle username))
And finally, you have to tell clojure that you want to allow sessions to be handled via middleware:
(def app
(sesh/wrap-noir-session
(handler/site
app-routes)))
Hopefully that gives you a bit of a headstart. I did not include how to connect to a database or how to use a map, but the above should be enough to get you on your way. I also did not touch on authorization and security (please don't skip this!). Using a database, map, or friend isn't a huge quantum leap from here. Just wanted to offer just enough to get you started.
Here's an example from when I did a luminus+friend combination, granted, they've changed the template several times, so this is from an older version, but the concepts the same, I hope it helps.
(def all-routes
[home-routes cljs-routes test-routes app-routes])
(def app
(-> all-routes middleware/app-handler ))
(def secured-app
(handler/site
(friend/authenticate app{
:login-uri "/login"
:unauthorized-redirect-uri "/login"
:credential-fn (partial creds/bcrypt-credential-fn users)
:workflows [(workflows/interactive-form)]})))
(def war-handler
(middleware/war-handler secured-app))