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.
Related
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)))
currently i am playing with Yada as web lib. Now i want to execute some function before a route is hit.
Approaches I have tested:
- wrap the current resource as sub-resource, but then the swagger-doc doesn't find the resource
- using an prepend-interceptor, but the docu is not complete at this point at i got errors
My code:
(ns all-mighty.namespace
(:require [yada.yada :refer [handler listener resource as-resource]]
[yada.swagger :refer [swaggered]])
(defn resources []
[""
(swaggered
[""
[
(cool-route)
]]
{:info {:title "Hello You!"
:version "1.0"
:description "It's something"}
:basePath ""}
)])
(defn cool-route []
["/cool" (resource {
:description "Returns somethign cool"
:produces "application/json"
:methods {:get {:response cool-response}}}
)])
(defn cool-response [ctx]
(-> (:response ctx)
(assoc :status 200)
(assoc :body {:state :up}))
Yeah, I'll refactor the resources latter ;-)
Does someone has an idea?
The way I'm using append-interceptor:
(ns all-mighty.namespace
(:require
[yada.handler :refer [append-interceptor]]
[yada.interceptors :as i]
[yada.swagger :refer [swaggered]]
[yada.yada :refer [handler listener resource as-resource]]))
(defn cool-response [ctx]
{:state :up
:my/value (get ctx :my/value)})
(defn my-cool-interceptor [ctx]
(assoc ctx :my/value 10))
(defn my-cool-resource
[model]
(-> model
;; you have to provide an interception chain, here we use the default one
(assoc :interceptor-chain yada.yada/default-interceptor-chain)
resource
;; here we append our interceptor after the request body has been processed
(append-interceptor i/process-request-body my-cool-interceptor)))
(defn cool-route []
["/cool" (my-cool-resource {:description "Returns somethign cool"
:produces "application/json"
:methods {:get {:response cool-response}}})])
(defn routes []
[""
(swaggered
[""
[
(cool-route)
]]
{:info {:title "Hello You!"
:version "1.0"
:description "It's something"}
:basePath ""}
)])
(comment
(def l (listener (routes) {:port 1337}))
((:close l))
)
So for every resource under /cool you can use the my-cool-resource function to automatically add the desired interceptor.
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}}
I follow the example code of book web development with clojure, 2nd edition and I have a problem with the file upload with google closure.
I test the file upload with Swagger and it response me 200 ok, I think the error is from the upload-file! function.(see below).
But I look up the closure api doc, it seems I use the correct function.
So I was in a trouble, I don't know why it doesn't work...
I need someone help.Here is my code(I use semantic-ui for ui components):
(defn upload-file! [upload-form-id status]
(reset! status nil)
(let [io (IframeIo.)]
(gev/listen
io goog.net.EventType.SUCCESS
#(reset! status [c/success-message "file uploaded successfully"]))
(gev/listen
io goog.net.EventType.ERROR
#(reset! status [c/warning-message "failed to upload the file"]))
(.setErrorChecker io #(= "error" (.getResponseText io)))
(.sendFromForm io (.getElementById js/document upload-form-id) "/upload")))
(defn upload-form []
(let [status (atom nil)
form-id "upload-form"]
(fn []
[c/modal
[:div "Upload File"]
[:div
(when #status #status)
[:div.ui.form
{:id form-id
:enc-type "multipart/form-data"
:method "POST"}
[:label {:for "file"} "select an image for upload"]
[:input {:id "file"
:name "file"
:type "file"}]]]
[:div
[:button.ui.primary.button
{:on-click #(upload-file! form-id status)}
"upload"]
[:button.ui.red.button
{:on-click #(do
(.modal (js/$ ".ui.modal") "hide")
(reset! status nil))}
"Cancel"]]
"upload"])))
the components:
(defn modal [header content footer id]
[:div.ui.modal
{:id id}
[:div.header header]
[:div.content content]
[:div.actions footer]])
(defn success-message [content]
[:div.ui.green.message
[:div.header content]])
So I solved my question, I should type [:form:ui.form] rather than [:div.ui.form].
if you interest in the code, you can view this URL:
upload image code
The postal
https://github.com/drewr/postal
I want to send mail with clojure, but I don't know how to set sender name.
This my code.
(defn smtp []
{:host (env :mail-host)
:user (env :mail-user)
:pass (env :mail-pass)
:ssl :yes!!!11})
(defn mail [request]
{:from "demo#gmail.com"
:to "demo2#gmail.com"
:subject "subject"
:body "body"})
(defn send [request]
(p/send-message (smtp) (mail request)))
Thanks!
You need to change the address from demo#gmail.com to Your Name <demo#gmail.com>
(defn mail [request]
{:from "Tornado <demo#gmail.com>"
:to "Hurricane <demo2#gmail.com>"
:subject "subject"
:body "body"})