pyc-website
main website for pyc inc.
git clone https://9o.is/git/pyc-website.git
UserSnip.scala
(10759B)
1 package inc.pyc
2 package snippet
3
4 import lib._
5 import rest._
6 import model._
7 import field._
8 import xml._
9 import net.liftweb._
10 import common._
11 import http._
12 import util._
13 import Helpers._
14 import json._
15 import JsonDSL._
16 import JsonAST.{JValue, JString, JBool}
17 import net.liftmodules.mongoauth.model.ExtSession
18 import net.liftmodules.extras.SnippetHelper
19 import inc.pyc.rest.IdVerificationFiles
20
21 /**
22 * Basic User information from a snippet.
23 */
24 sealed trait UserSnippet extends SnippetHelper with Logger {
25
26 protected def user: Box[User]
27
28 protected def serve(snip: User => NodeSeq): NodeSeq =
29 (for {
30 u <- user ?~ "User not found"
31 } yield {
32 snip(u)
33 }): NodeSeq
34
35 protected def serve(snip: User => CssSel): CssSel =
36 (for {
37 u <- user ?~ "User not found"
38 } yield {
39 snip(u)
40 }): CssSel
41
42 def username(in: NodeSeq): NodeSeq = serve { user =>
43 Text(user.username.get)
44 }
45
46 def name(in: NodeSeq): NodeSeq = serve { user =>
47 val name = user.fname.get + " " + user.lname.get
48 if (name.length > 1) Text(name)
49 else Text(user.username.get)
50 }
51
52 def limit(in: NodeSeq): NodeSeq = serve {
53 user => Text(user.purchaseLimit.get.toString)
54 }
55
56 def identifier(in: NodeSeq): NodeSeq = serve {
57 user =>
58 if(user.fname.get.isEmpty) Text(user.email.get)
59 else name(in)
60 }
61 }
62
63 /**
64 * Basic logged in user information from a snippet.
65 */
66 trait CurrentUser extends UserSnippet {
67 protected def user = User.currentUser
68 }
69
70 object CurrentUser extends CurrentUser
71
72 /**
73 * Angular snippet for accessing user information.
74 */
75 trait AngularUserSnippet extends AngularSnippet {
76
77 protected def user: Box[User]
78
79 protected def serve(snip: User => JValue): JValue =
80 (for {
81 u <- user ?~ "User not found"
82 } yield {
83 snip(u)
84 }): JValue
85
86 /** Validates and saves the currently signed in user. */
87 protected def validateAndSave(f: () => Unit): JValue = validateUser {
88 user =>
89 f()
90 user.save()
91 NgAlert.success
92 }
93
94 /** Validates and updates the currently signed in user. */
95 protected def validateAndUpdate(f: () => Unit): JValue = validateUser {
96 user =>
97 f()
98 user.update
99 NgAlert.success
100 }
101
102 protected def validateUser(f: User => JValue): JValue = serve {
103 user =>
104 user.validate match {
105 case Nil => f(user)
106 case errors =>
107 NgAlert.danger(S ? "Invalid_submission", errors)
108 }
109 }
110 }
111
112 /**
113 * Angular snippet for accessing logged in user information.
114 */
115 trait AngularCurrentUser extends AngularUserSnippet {
116 protected def user = User.currentUser
117 }
118
119 class UserLogin extends AngularSnippet {
120
121 def roundTrips: List[RoundTripInfo] = List("submit" -> submit _)
122
123 def submit(model: JValue): JValue = {
124 for {
125 JString(e) <- model \ "email"
126 JString(password) <- model \ "password"
127 JBool(remember) <- model \ "remember"
128 } yield {
129 val email = e.toLowerCase.trim
130 User.loginCredentials(LoginCredentials(email, remember))
131
132 User.findByEmail(email) match {
133 case Full(user) if !user.verified.get =>
134 NgAlert.danger(
135 <span>{S ? "unverified.title"}:</span> ++
136 <p>{S ? "unverified.message"}.</p>, Nil)
137
138 case Full(user) if (user.password.isMatch(password)) =>
139 User.logUserIn(user, remember)
140 if (remember) User.createExtSession(user.id.get)
141 else ExtSession.deleteExtCookie()
142 NgAlert.success
143
144 case _ =>
145 NgAlert.danger(
146 <span>{S ? "invalid.title"}:</span> ++
147 <p>{S ? "invalid.message"}.</p>, Nil)
148 }
149 }
150 }
151 }
152
153 class PasswordChange extends AngularCurrentUser {
154
155 /**
156 * Passwords must match this pattern.
157 * - makes sure there are no white-space characters
158 * - minimum length of 8
159 * - at least one non-alpha character
160 */
161 def passwordPattern: String = "^(?=.*[^a-zA-Z])\\S{8,}$"
162
163 def roundTrips: List[RoundTripInfo] = List("submit" -> submit _)
164
165 def submit(model: JValue): JValue = serve {
166 user =>
167 for {
168 JString(password) <- model \ "password"
169 } yield {
170
171 if(password matches passwordPattern) {
172
173 user.password(password)
174 user.password.hashIt
175 user.save()
176
177 NgAlert.success(
178 <span>{S ? "password_updated"}</span>)
179 } else {
180 NgAlert.danger(
181 <span>{S ? "password_req"}:</span> ++
182 <ul><li>{S ? "password_whitespace"}</li><li>{S ? "password_atleast8"}</li><li>{S ? "password_nonalpha"}</li></ul>, Nil)
183 }
184
185 }
186 }
187 }
188
189 class PasswordRecovery extends AngularSnippet {
190
191 def roundTrips: List[RoundTripInfo] = List("submit" -> submit _)
192
193 def submit(model: JValue): JValue = {
194 for {
195 JString(e) <- model \ "email"
196 } yield {
197 val email = e.toLowerCase.trim
198
199 def msg = NgAlert.success(
200 <span>{S ? "sent.title"}</span> ++
201 <p>{S ? "sent.message"}.</p>)
202
203 User.findByEmail(email) match {
204 case Full(user) =>
205 User.sendLoginToken(user)
206 User.loginCredentials.remove()
207 msg
208
209 case _ => msg
210 }
211 }
212 }
213 }
214
215 class UserRegistration extends AngularSnippet {
216
217 def roundTrips: List[RoundTripInfo] = List("submit" -> submit _)
218
219 def submit(model: JValue): JValue = {
220 for {
221 JString(e) <- model \ "email"
222 } yield {
223 val email = e.toLowerCase.trim
224 val user = User.createRecord
225 user.email(email)
226 user.username(Helpers.randomString(20))
227 user.password(Helpers.randomString(20))
228 user.password.hashIt
229
230 user.validate match {
231 case Nil =>
232 user.save()
233 User.sendRegistrationToken(user)
234
235 NgAlert.success(
236 <span>{S ? "thanks.title"}.</span> ++
237 <p>{S ? "thanks.message"}.</p>
238 )
239 case errors =>
240 NgAlert.danger(
241 Text(S ? "Invalid_submission"),
242 errors
243 )
244 }
245 }
246 }
247 }
248
249 class UserSettings extends AngularSnippet {
250 import com.foursquare._
251 import rogue.LiftRogue._
252
253 def roundTrips: List[RoundTripInfo] = List(
254 "submit" -> submit _)
255
256 def submit(model: JValue): JValue =
257 for {
258 JString(id) <- model \ "id"
259 JString(fname) <- model \ "fname"
260 JString(lname) <- model \ "lname"
261 JString(dob) <- model \ "dob"
262 JString(license) <- model \ "driversLicense"
263 } yield {
264
265 val exists = User.
266 where(_.fname eqs fname).
267 and(_.lname eqs lname).
268 and(_.dob eqs dob).
269 and(_.driversLicense eqs license).
270 exists()
271
272 if(exists) {
273 NgAlert.danger(S ? "verify_already_exists", Nil)
274 } else {
275 User.findByStringId(id).map {
276 user =>
277 user.
278 fname(fname).
279 lname(lname).
280 dob(dob).
281 driversLicense(license).
282 save()
283
284 NgAlert.success("updated")
285
286 } openOr JNull
287 }
288 }
289 }
290
291 class UserSettingsEmail extends AngularCurrentUser {
292
293 def roundTrips: List[RoundTripInfo] = List(
294 "submit" -> submit _,
295 "init" -> init _)
296
297 def submit(model: JValue): JValue = serve {
298 user =>
299 for {
300 JString(e) <- model \ "email"
301 } yield {
302 val email = e.toLowerCase.trim
303 EmailResetToken.sendToken(user, email)
304
305 NgAlert.success(
306 Text(S ? "verify_email_msg")
307 )
308 }
309 }
310
311 def init(model: JValue): JValue = serve {
312 user =>
313 ("email" -> user.email.get)
314 }
315 }
316
317
318 class UserVerifyPassword extends AngularCurrentUser with UserSnippet {
319
320 def roundTrips: List[RoundTripInfo] = List(
321 "init" -> init _)
322
323 def init(ignore: JValue): JValue = serve {
324 user: User =>
325 user.verifypass.reset.update // set a new password
326
327 ("password" -> user.verifypass.get) ~
328 ("limit" -> user.userLimitAsString): JValue
329 }
330
331 def show(in: NodeSeq): NodeSeq = serve {
332 user =>
333 if(user.limit1k) NodeSeq.Empty else in
334 }
335 }
336
337
338 class VerificationSteps extends CurrentUser {
339 def render = serve {
340 user: User =>
341 "wizard [current-step]" #> s"'${user.userLimitAsString}'"
342 }
343 }
344
345
346 class PhoneVerification extends AngularCurrentUser with Logger {
347
348 lazy val smscode = (100000 + new scala.util.Random().nextInt(900000)).toString
349 var phone = ""
350
351 def roundTrips: List[RoundTripInfo] = List(
352 "sendsms" -> sendsms _,
353 "verifyphone" -> verifyphone _,
354 "init" -> init _)
355
356
357 def sendsms(model: JValue): JValue = serve {
358 user =>
359 for {
360 JString(newPhone) <- model \ "phone"
361 } yield {
362 val msg = S ? "sms_code_msg" format (user.fname.get, smscode)
363
364 if (phone == newPhone) {
365 NgAlert.success
366 } else if (Twilio.sms(newPhone, msg)) {
367 phone = newPhone
368 NgAlert.success
369 } else {
370 NgAlert.danger(
371 Text(S ? "technical_difficulties"), Nil)
372 }
373
374 }
375 }
376
377 def verifyphone(model: JValue): JValue = serve {
378 user =>
379 for {
380 JString(smscode) <- model \ "smscode"
381 } yield
382
383 if(this.smscode == smscode) {
384 user.
385 purchaseLimit(USAPurchaseLimit.D3k).
386 phone(phone)
387 validateAndUpdate(() => {})
388
389 NgAlert.success(("purchaseLimit" -> user.purchaseLimit.get.toString))
390 } else {
391 NgAlert.danger(
392 Text(S ? "sms_verify_fail"), Nil)
393 }
394
395 }
396
397 def init(model: JValue): JValue = serve {
398 user =>
399 ("phone" -> user.phone.get)
400 }
401 }
402
403
404 class IdVerification extends AngularCurrentUser {
405
406 def roundTrips: List[RoundTripInfo] = List(
407 "submit" -> submit _)
408
409 def submit(model: JValue): JValue = serve {
410 user =>
411
412 import IdVerificationHelper._
413 val service = s3(createS3Folder(user))
414 val success = upload(service)
415
416 if (success) {
417 user.purchaseLimit(USAPurchaseLimit.D10k_?)
418 user.update
419 notifyIdentityRequest(user)
420 NgAlert.success(
421 <span>{S ? "file_received"}:</span> ++
422 <p>{S ? "file_review"}.</p>,
423 ("purchaseLimit" -> user.userLimitAsString))
424 } else {
425 NgAlert.danger(Text(S ? "file_upload_fail"), Nil)
426 }
427 }
428 }
429
430
431 class PurchaseLimit extends AngularCurrentUser {
432
433 def roundTrips: List[RoundTripInfo] = List("init" -> init _)
434
435 def init(model: JValue): JValue = serve {
436 user =>
437 ("purchaseLimit" -> user.userLimitAsString)
438 }
439 }