How to get logged in user's id from session in LuminusWeb - clojure

I've just saved the current logged-in user in session. Now how do I get user's id from session in Selmer templates?. Thanks
(defn login-form [{:keys [flash]}]
(layout/render
"login_form.html"
(merge (select-keys flash [:id :pass :errors]))))
(defn authenticate [id pass]
(when-let [user (db/get-user {:id id})]
(when (hashers/check pass (:pass user))
id)))
(defn login! [{:keys [params]}]
(if-let [id (authenticate (:id params) (:pass params))] ;TODO: Refactor
(do
>>>>>>>>> (update (response/found "/") :session {:id id}))
(layout/render "login_form.html" {:invalid-credentials? true})))
(defroutes auth-routes
(GET "/accounts/login/" request (login-form request))
(POST "/accounts/login/" request (login! request)))
Update
I've updated my login function. and it works.
(defn login! [{:keys [params session]}]
(if-let [id (authenticate (:id params) (:pass params))] ;TODO: Refactor
(-> (response/found "/")
(assoc :session
(assoc session :id id)))
(layout/render "login_form.html" {:invalid-credentials? true})))
To print the user's id, I've changed the home-routes.
(defn home-page [opts]
(layout/render "home.html" opts))
(defroutes home-routes
(GET "/" {:keys [flash session]} (home-page (or flash session))))
Now I can print user's id in home.html template. But If I use any other url then user's id stopped `displaying``.
Question > So do I need to pass {:keys [flash session]} in every routes?

You can make use of the wrap-identity middleware which Luminus provides by making sure it's in your wrap-base stack, then change :id to :identity in your session. You can now call (layout/render "home.html") (even without providing opts!) and you can access the identity using {{identity}}

Related

How to extract a path-param from a Reitit backend end route

How do you get a path-param out of a Reitit backend Clojure route? I am trying to get the val associated with :id in the following manner, but keep getting a 404 file not found error in the REPL.
["/foo/:id" {:get
(fn [{:keys [path-params ]}]
(some-ns/some-fn (:id path-params)))}]
I have tried using the documentation at https://luminusweb.com/docs/routes.html and https://github.com/metosin/reitit/blob/master/README.md.
Destructure the path params from the request, and then extract the id.
(defn project-routes
"SPA routing"
[]
["" {:middleware [middleware/wrap-base
middleware/wrap-formats]}
["/foo"
["/bar/:id"
{:get (fn [{:keys [path-params] :as _req}]
(http-response/ok
(core/READ some-table
(Integer/valueOf (:id path-params)))))}]]])
Documentation and examples obtained from https://github.com/luminus-framework/luminus-docs/blob/master/resources/md/sessions_cookies.md. Controllers were used, as documented by https://clojureverse.org/t/how-do-you-accomplish-spa-re-frame-teardown/5516/6.

How to save a user in noir session in a compojure web project?

I have to save a username in a session and I can't seem to make it work. This is my code (sesh is noir session):
(defn set-loggedin [username password]
(do
(sesh/put! :username (:user username))
(sesh/put! :password (:pass password))))
(defn login-handler [username password]
(let [user (datab/login username password)]
(if (empty? user)
(view/login-form "Wrong username/password")
(do
(set-loggedin username password)
(response/redirect "/main")))))
(defroutes app-routes
(GET "/" [] (view/login-form "" ))
(POST "/" [username password] (login-handler username password))
(GET "/main" [] (view/main-page))
(route/resources "/")
(route/not-found "Not Found"))
(def app
(sesh/wrap-noir-session
(wrap-defaults app-routes (assoc-in site-defaults [:security :anti-forgery]
false))))
This is the main page in hiccup and it doesn't show the user when I login:
(defn main-page []
(html[:head [:title "main"]] [:body
(sesh/get :username)] ))
Any help would be appreciated!
In your set-loggedin function, you are trying to get the :user key from 'username', which I suspect is a string and the :pass key from 'password', which is probably a string.
The do is also unnecessary in a function definition so try this:
(defn set-loggedin [username password]
(sesh/put! :username username)
(sesh/put! :password password)))

Getting the http request when using compojure.route/not-found in Clojure

I'm building a server using Cloujre's Compojure. The default route is compojure.route/not-found, is there a way of getting the request that reached this route? I'd like print all requests that end up there.
You can use this kinda approach:
(def handler (-> your-routes
wrap-my-request-middleware ;; it has to be in this order
...))
Let's log in here the uri
(defn wrap-my-request-middleware
[handler]
(fn [request]
(let [response (handler request)]
(when (= 404 (:status response))
;; do whatever you like in here
(log/info (str "Request path: " (:uri request))))
response)));; fn needs to return reponse...

Clojure - OpenID - Luminus - Steam Integration

I am new to Clojure and using Luminus to build a website, I have been trying to integrate OpenID to my site but I am failing so bad. I have this code example:
https://github.com/cemerick/friend-demo/blob/master/src/clj/cemerick/friend_demo/openid.clj
demo: http://friend-demo.herokuapp.com/openid/
I am trying to implement it to my site but I keep getting errors, in the end I just wanted to copy it exactly to see if its working on my localhost. So I have this code in my home.clj
But it just doesn't work, whenever I click Login button I get "Not Found" at localhost:3000/login
Does it mean I need to handle /login somewhat? but it is not documented in the example code above.
(def providers [{:name "Steam" :url "http://steamcommunity.com/openid"}
{:name "Yahoo" :url "http://me.yahoo.com/"}])
(defroutes home-routes
(GET "/" req
(h/html5
pretty-head
(pretty-body
; (github-link req)
[:h2 "Authenticating with various services using OpenID"]
[:h3 "Current Status " [:small "(this will change when you log in/out)"]]
(if-let [auth (friend/current-authentication req)]
[:p "Some information delivered by your OpenID provider:"
[:ul (for [[k v] auth
:let [[k v] (if (= :identity k)
["Your OpenID identity" (str (subs v 0 (* (count v) 2/3)) "…")]
[k v])]]
[:li [:strong (str (name k) ": ")] v])]]
[:div
[:h3 "Login with…"]
(for [{:keys [name url]} providers
:let [base-login-url (context-uri req (str "/login?identifier=" url))
dom-id (str (gensym))]]
[:form {:method "POST" :action (context-uri req "login")
:onsubmit (when (.contains ^String url "username")
(format "var input = document.getElementById(%s); input.value = input.value.replace('username', prompt('What is your %s username?')); return true;"
(str \' dom-id \') name))}
[:input {:type "hidden" :name "identifier" :value url :id dom-id}]
[:input {:type "submit" :class "button" :value name}]])
[:p "…or, with a user-provided OpenID URL:"]
[:form {:method "POST" :action (context-uri req "login")}
[:input {:type "text" :name "identifier" :style "width:250px;"}]
[:input {:type "submit" :class "button" :value "Login"}]]])
[:h3 "Logging out"]
[:p [:a {:href (context-uri req "logout")} "Click here to log out"] "."])))
(GET "/logout" req
(friend/logout* (resp/redirect (str (:context req) "/")))))
(def page (friend/authenticate
home-routes
{:allow-anon? true
:default-landing-uri "/"
:workflows [(openid/workflow
:openid-uri "/login"
:credential-fn identity)]}))
The /login callback is handled by openid-workflow which essentially becomes a wrapper around your home-routes. You are likely using home-routes as your request handler when you should use page instead.

Friend authentication empty param

I am trying to implement friend authentication on my web app but when i try to login i get this */login?&login_failed=Y&username=*...my param is empty and i cant login,what am i doing wrong?
these are my routes...
(defroutes routes
(GET "/" [] (index))
(GET "/indexMessage" [] (indexMessage))
(GET "/login" req (index))
(POST "/insert-user" {params :params}
(let [firstname (get params "firstname")
lastname (get params "lastname")
email (get params "email")
password (get params "password")
sex (get params "sex")
year (get params "year")
month (get params "month")
day (get params "day")]
(def date (str year"-"month"-"day))
(insert-user firstname lastname email password sex date)))
(route/resources "/public")
(route/not-found "Page not found")
)
i used all the middleware needed...
(def page (handler/site
(friend/authenticate
routes
{:allow-anon? true
:login-uri "/login"
:default-landing-uri "/"
:unauthorized-handler #(-> (html5 [:h2 "You do not have sufficient privileges to access " (:uri %)])
resp/response
(resp/status 401))
:credential-fn #(creds/bcrypt-credential-fn #users %)
:workflows [(workflows/interactive-form)]})
(wrap-keyword-params routes)
(wrap-nested-params routes)
(wrap-params routes)
(wrap-session routes)
))
and this is how i start up my jetty server...
(defn -main []
(run-jetty page {:port 8080 :join? false}))
users is a map like this...
{"username" {:username "username" :password "password"}}
is :roles a must in the map?maybe it's because of that?
I am pretty new to Friend as well but from the Friend source code I can say that the parameters name of your POST request matters. I guess you are following this example, if not, it's the best hint you can get actually. Notice the name of the form fields
https://github.com/cemerick/friend-demo/blob/master/src/clj/cemerick/friend_demo/interactive_form.clj#L22-l24
All credential functions take a single argument, a map containing the available credentials, so
as there is no explicit POST "/login" route, the Friend midleware is catching and using them as credentials for your credential-fn as shown here https://github.com/cemerick/friend/blob/master/src/cemerick/friend/workflows.clj#L76-78
So "username" and "password" should be the names of the parameters POSTed to the :login-uri
For newcomers that example is runnable here http://friend-demo.herokuapp.com/interactive-form/