-
Notifications
You must be signed in to change notification settings - Fork 228
/
login.clj
132 lines (121 loc) · 4.86 KB
/
login.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
(ns foreclojure.login
(:import org.jasypt.util.password.StrongPasswordEncryptor)
(:use hiccup.form-helpers
hiccup.page-helpers
[foreclojure utils config users]
compojure.core
[amalloy.utils :only [rand-in-range]]
somnium.congomongo)
(:require [sandbar.stateful-session :as session]
[ring.util.response :as response]))
(def-page my-login-page [location]
(when location
(session/session-put! :login-to location)
nil) ;; don't include this in HTML output
[:div.error
(session/flash-get :error)
(session/flash-get :message)]
(form-to [:post "/login"]
[:table
[:tr
[:td (label :user "Username")]
[:td (text-field :user)]]
[:tr
[:td (label :pwd "Password")]
[:td (password-field :pwd)]]
[:tr
[:td]
[:td [:button {:type "submit"} "Log In"]]]
[:tr
[:td]
[:td
[:a {:href "/login/reset"} "Forgot your password?"]]]]))
(defn do-login [user pwd]
(let [user (.toLowerCase user)
{db-pwd :pwd} (from-mongo (fetch-one :users :where {:user user}))
location (session/session-get :login-to)]
(if (and db-pwd (.checkPassword (StrongPasswordEncryptor.) pwd db-pwd))
(do (update! :users {:user user}
{:$set {:last-login (java.util.Date.)}}
:upsert false) ; never create new users accidentally
(session/session-put! :user user)
(session/session-delete-key! :login-to)
(response/redirect (or location "/problems")))
(flash-error "Error logging in." "/login"))))
(def-page update-password-page []
(with-user [{:keys [user] :as user-obj}]
[:div#account-settings
[:div#update-pwd
[:h2 "Change password for " user]
[:span.error (session/flash-get :error)]
[:table
(form-to [:post "/login/update"]
(map form-row
[[password-field :old-pwd "Current password"]
[password-field :pwd "New password"]
[password-field :repeat-pwd "Repeat password"]])
[:tr
[:td [:button {:type "submit"} "Reset now"]]])]]]))
(defn do-update-password! [old-pwd new-pwd repeat-pwd]
(with-user [{:keys [user pwd]}]
(let [encryptor (StrongPasswordEncryptor.)]
(assuming [(= new-pwd repeat-pwd)
"New password was not entered identically twice"
(.checkPassword encryptor old-pwd pwd)
"Old password incorrect"]
(let [new-pwd-hash (.encryptPassword encryptor new-pwd)]
(update! :users {:user user}
{:$set {:pwd new-pwd-hash}}
:upsert false)
(flash-msg (str "Password for " user " updated successfully")
"/problems"))
(flash-error why "/login/update")))))
(def-page reset-password-page []
[:div
[:div#reset-help
[:h3 "Forgot your password?"]
[:div "Enter your email address and we'll send you a new password."]
[:div
[:span.error (session/flash-get :error)]
(form-to [:post "/login/reset"]
(label :email "Email")
(text-field :email)
[:button {:type "submit"} "Reset!"])]]])
(def pw-chars "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXY1234567890")
(defn do-reset-password! [email]
(if-let [{id :_id, name :user} (fetch-one :users
:where {:email email}
:only [:_id :user])]
(let [pw (apply str
(repeatedly 10 #(rand-nth pw-chars)))
pw-hash (.encryptPassword (StrongPasswordEncryptor.) pw)]
(update! :users
{:_id id}
{:$set {:pwd pw-hash}})
(send-email
{:from "team@4clojure.com"
:to [email]
:subject "Password reset"
:body
(str "The password for your 4clojure.com account "
name " has been reset to " pw ". Make sure to change it"
" soon at https://4clojure.com/login/update - pick"
" something you'll remember!")})
(session/session-put! :login-to "/login/update")
(flash-msg "Your password has been reset! You should receive an email soon"
(login-url "/login/update")))
(flash-error "We don't know anyone with that email address!"
"/login/reset")))
(defroutes login-routes
(GET "/login" [location] (my-login-page location))
(POST "/login" {{:strs [user pwd]} :form-params}
(do-login user pwd))
(GET "/login/update" [] (update-password-page))
(POST "/login/update" {{:strs [old-pwd pwd repeat-pwd]} :form-params}
(do-update-password! old-pwd pwd repeat-pwd))
(GET "/login/reset" [] (reset-password-page))
(POST "/login/reset" [email]
(do-reset-password! email))
(GET "/logout" []
(do (session/session-delete-key! :user)
(response/redirect "/"))))