pyc-website
main website for pyc inc.
git clone https://9o.is/git/pyc-website.git
User.scala
(7446B)
1 package inc.pyc
2 package model
3
4 import lib._
5 import config._
6 import field._
7 import lib.RogueMetaRecord
8 import org.bson.types.ObjectId
9 import org.joda.time.DateTime
10 import net.liftweb._
11 import common._
12 import http.{StringField => _, BooleanField => _, _}
13 import mongodb.record.field._
14 import record.field._
15 import net.liftmodules.mongoauth._
16 import net.liftmodules.mongoauth.field._
17 import net.liftmodules.mongoauth.model._
18 import java.util.regex.Pattern
19 import scala.concurrent._
20 import ExecutionContext.Implicits.global
21
22 class User private () extends ProtoAuthUser[User] with ObjectIdPk[User] with USAUserVerification[User] {
23 def meta = User
24
25 def userIdAsString: String = id.toString
26
27 /**
28 * User's first name.
29 */
30 object fname extends StringField(this, 32) {
31 override def validations =
32 valMaxLen(32, "First Name must be 32 characters or less") _ ::
33 super.validations
34 }
35
36 /**
37 * User's last name.
38 */
39 object lname extends StringField(this, 32) {
40 override def validations =
41 valMaxLen(32, "Last Name must be 32 characters or less") _ ::
42 super.validations
43 }
44
45 /**
46 * User's phone number.
47 */
48 object phone extends OptionalStringField(this, 10) {
49 override def validations =
50 valRegex(Pattern.compile("[0-9]{10}"), "Phone number must be 10 digits long") _ ::
51 super.validations
52 }
53
54 /**
55 * User's driver's license.
56 */
57 object driversLicense extends OptionalStringField(this, 50)
58
59 /**
60 * User's date of birth.
61 */
62 object dob extends OptionalStringField(this, 6) {
63 override def validations =
64 valRegex(Pattern.compile("[0-9]{6}"), "Date of Birth should be 6 digits long (xx/xx/xx)") _ ::
65 super.validations
66 }
67
68 /**
69 * User's one-time password.
70 */
71 object verifypass extends StringField(this, 15) {
72
73 def reset = {
74 set(StringUtils.randomString(7))
75 owner
76 }
77
78 override def defaultValue = StringUtils.randomString(7)
79 }
80
81 /**
82 * When user's account was created.
83 */
84 def whenCreated: DateTime = new DateTime(id.get.getTime)
85
86 /**
87 * Country the user is located (primarily)
88 */
89 object country extends CountryField(this) {
90 override def defaultValue = Countries.USA
91 }
92
93 /**
94 * Bitcoin addresses the user has used to buy bitcoin.
95 */
96 object addresses extends MongoListField[User, String](this) with MongoListFieldExtra[User, String]
97 }
98
99 object User extends User with ProtoAuthUserMeta[User] with RogueMetaRecord[User] with Loggable {
100 import mongodb.BsonDSL._
101
102 override def collectionName = "user.users"
103
104 ensureIndex((email.name -> 1), ("unique" -> true))
105 ensureIndex((phone.name -> 1), ("unique" -> true) ~ ("sparse" -> true))
106
107 // look at https://jira.mongodb.org/browse/SERVER-3934
108 ensureIndex((addresses.name -> 1), ("unique" -> true) ~ ("sparse" -> true) ~ ("v" -> 0))
109
110 def findByEmail(in: String): Box[User] = find(email.name, in)
111
112 def findByAddress(in: String): Box[User] = find(addresses.name, in)
113
114 def findByPhone(in: String): Box[User] = find(phone.name, in)
115
116 def findByStringId(id: String): Box[User] =
117 if (ObjectId.isValid(id)) find(new ObjectId(id))
118 else Empty
119
120 override def onLogIn: List[User => Unit] = List(user => User.loginCredentials.remove())
121 override def onLogOut: List[Box[User] => Unit] = List(
122 x => logger.debug("User.onLogOut called."),
123 boxedUser => boxedUser.foreach { u =>
124 ExtSession.deleteExtCookie()
125 }
126 )
127
128 /*
129 * MongoAuth vars
130 */
131 private lazy val siteName = MongoAuth.siteName.vend
132 private lazy val sysUsername = MongoAuth.systemUsername.vend
133 private lazy val indexUrl = MongoAuth.indexUrl.vend
134 private lazy val registerUrl = MongoAuth.registerUrl.vend
135 private lazy val loginTokenAfterUrl = MongoAuth.loginTokenAfterUrl.vend
136
137 /*
138 * LoginToken
139 */
140 override def handleLoginToken: Box[LiftResponse] = {
141 val resp = S.param("token").flatMap(LoginToken.findByStringId) match {
142 case Full(at) if (at.expires.isExpired) => {
143 at.delete_!
144 RedirectWithState(indexUrl, RedirectState(() => { S.error("Login token has expired") }))
145 }
146 case Full(at) => find(at.userId.get).map(user => {
147 if (user.validate.length == 0) {
148 user.verified(true)
149 user.save()
150 logUserIn(user)
151 at.delete_!
152 RedirectResponse(loginTokenAfterUrl)
153 }
154 else {
155 at.delete_!
156 regUser(user)
157 RedirectWithState(registerUrl, RedirectState(() => { S.notice("Please complete the registration form") }))
158 }
159 }).openOr(RedirectWithState(indexUrl, RedirectState(() => { S.error("User not found") })))
160 case _ => RedirectWithState(indexUrl, RedirectState(() => { S.warning("Login token not provided") }))
161 }
162
163 Full(resp)
164 }
165
166 /**
167 * Sends an email to the user with a link for logging in.
168 */
169 def sendLoginToken(user: User): Future[Unit] = Future {
170 import net.liftweb.util.Mailer._
171
172 val token = LoginToken.createRecord.userId(user.id.get).save()
173 val url = "%s%s?token=%s".format(Site.host, MongoAuth.loginTokenUrl.vend, token.id.toString)
174 val title = "Account Login"
175 val btn = "Complete "+title
176 val msg = "Hello, someone requested a link to log in to your account."
177
178 HtmlEmail.createToken(false, "", msg, btn, url, false) map {
179 sendMail(
180 From(MongoAuth.systemFancyEmail),
181 Subject("%s: %s".format(siteName, title)),
182 To(user.fancyEmail),
183 _
184 )
185 }
186 }
187
188 /**
189 * Sends registration token.
190 */
191 def sendRegistrationToken(user: User): Future[Unit] = Future {
192 import net.liftweb.util.Mailer._
193
194 val token = LoginToken.createRecord.userId(user.id.get).save()
195 val url = "%s%s?token=%s".format(Site.host, MongoAuth.loginTokenUrl.vend, token.id.toString)
196 val title = "Account Registration"
197 val btn = "Complete "+title
198 val msg = "Thank you for using bitcoin. Please follow the link to complete your registration."
199
200 HtmlEmail.createToken(false, "", msg, btn, url, false) map {
201 sendMail(
202 From(MongoAuth.systemFancyEmail),
203 Subject("%s: %s".format(siteName, title)),
204 To(user.fancyEmail),
205 _
206 )
207 }
208 }
209
210 /*
211 * ExtSession
212 */
213 def createExtSession(uid: ObjectId) = ExtSession.createExtSessionBox(uid)
214
215 /*
216 * Test for active ExtSession.
217 */
218 def testForExtSession: Box[Req] => Unit = {
219 ignoredReq => {
220 if (currentUserId.isEmpty) {
221 ExtSession.handleExtSession match {
222 case Full(es) => find(es.userId.get).foreach { user => logUserIn(user, false) }
223 case Failure(msg, _, _) =>
224 logger.warn("Error logging user in with ExtSession: %s".format(msg))
225 case Empty =>
226 }
227 }
228 }
229 }
230
231 // used during login process
232 object loginCredentials extends SessionVar[LoginCredentials](LoginCredentials(""))
233 object regUser extends SessionVar[User](createRecord.email(loginCredentials.is.email))
234 }
235
236 case class LoginCredentials(email: String, isRememberMe: Boolean = false)
237
238 object SystemUser {
239 private val username = "pyc"
240 private val email = "noreply@pycbitcoin.com"
241
242 lazy val user: User = User.find("username", username) openOr {
243 User.createRecord
244 .fname("PYC")
245 .username(username)
246 .email(email)
247 .verified(true)
248 .password("abc123", true)
249 .save()
250 }
251 }