You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

user_mail.go 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. // Copyright 2016 The Gogs Authors. All rights reserved.
  2. // Copyright 2020 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package models
  6. import (
  7. "errors"
  8. "fmt"
  9. "net/mail"
  10. "strings"
  11. "code.gitea.io/gitea/modules/log"
  12. "code.gitea.io/gitea/modules/setting"
  13. "code.gitea.io/gitea/modules/util"
  14. "xorm.io/builder"
  15. )
  16. var (
  17. // ErrEmailAddressNotExist email address not exist
  18. ErrEmailAddressNotExist = errors.New("Email address does not exist")
  19. )
  20. // EmailAddress is the list of all email addresses of a user. Can contain the
  21. // primary email address, but is not obligatory.
  22. type EmailAddress struct {
  23. ID int64 `xorm:"pk autoincr"`
  24. UID int64 `xorm:"INDEX NOT NULL"`
  25. Email string `xorm:"UNIQUE NOT NULL"`
  26. IsActivated bool
  27. IsPrimary bool `xorm:"-"`
  28. }
  29. // ValidateEmail check if email is a allowed address
  30. func ValidateEmail(email string) error {
  31. if len(email) == 0 {
  32. return nil
  33. }
  34. if _, err := mail.ParseAddress(email); err != nil {
  35. return ErrEmailInvalid{email}
  36. }
  37. // TODO: add an email allow/block list
  38. return nil
  39. }
  40. // GetEmailAddresses returns all email addresses belongs to given user.
  41. func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
  42. emails := make([]*EmailAddress, 0, 5)
  43. if err := x.
  44. Where("uid=?", uid).
  45. Find(&emails); err != nil {
  46. return nil, err
  47. }
  48. u, err := GetUserByID(uid)
  49. if err != nil {
  50. return nil, err
  51. }
  52. isPrimaryFound := false
  53. for _, email := range emails {
  54. if email.Email == u.Email {
  55. isPrimaryFound = true
  56. email.IsPrimary = true
  57. } else {
  58. email.IsPrimary = false
  59. }
  60. }
  61. // We always want the primary email address displayed, even if it's not in
  62. // the email address table (yet).
  63. if !isPrimaryFound {
  64. emails = append(emails, &EmailAddress{
  65. Email: u.Email,
  66. IsActivated: u.IsActive,
  67. IsPrimary: true,
  68. })
  69. }
  70. return emails, nil
  71. }
  72. // GetEmailAddressByID gets a user's email address by ID
  73. func GetEmailAddressByID(uid, id int64) (*EmailAddress, error) {
  74. // User ID is required for security reasons
  75. email := &EmailAddress{UID: uid}
  76. if has, err := x.ID(id).Get(email); err != nil {
  77. return nil, err
  78. } else if !has {
  79. return nil, nil
  80. }
  81. return email, nil
  82. }
  83. func isEmailActive(e Engine, email string, userID, emailID int64) (bool, error) {
  84. if len(email) == 0 {
  85. return true, nil
  86. }
  87. // Can't filter by boolean field unless it's explicit
  88. cond := builder.NewCond()
  89. cond = cond.And(builder.Eq{"email": email}, builder.Neq{"id": emailID})
  90. if setting.Service.RegisterEmailConfirm {
  91. // Inactive (unvalidated) addresses don't count as active if email validation is required
  92. cond = cond.And(builder.Eq{"is_activated": true})
  93. }
  94. em := EmailAddress{}
  95. if has, err := e.Where(cond).Get(&em); has || err != nil {
  96. if has {
  97. log.Info("isEmailActive('%s',%d,%d) found duplicate in email ID %d", email, userID, emailID, em.ID)
  98. }
  99. return has, err
  100. }
  101. // Can't filter by boolean field unless it's explicit
  102. cond = builder.NewCond()
  103. cond = cond.And(builder.Eq{"email": email}, builder.Neq{"id": userID})
  104. if setting.Service.RegisterEmailConfirm {
  105. cond = cond.And(builder.Eq{"is_active": true})
  106. }
  107. us := User{}
  108. if has, err := e.Where(cond).Get(&us); has || err != nil {
  109. if has {
  110. log.Info("isEmailActive('%s',%d,%d) found duplicate in user ID %d", email, userID, emailID, us.ID)
  111. }
  112. return has, err
  113. }
  114. return false, nil
  115. }
  116. func isEmailUsed(e Engine, email string) (bool, error) {
  117. if len(email) == 0 {
  118. return true, nil
  119. }
  120. return e.Where("email=?", email).Get(&EmailAddress{})
  121. }
  122. // IsEmailUsed returns true if the email has been used.
  123. func IsEmailUsed(email string) (bool, error) {
  124. return isEmailUsed(x, email)
  125. }
  126. func addEmailAddress(e Engine, email *EmailAddress) error {
  127. email.Email = strings.ToLower(strings.TrimSpace(email.Email))
  128. used, err := isEmailUsed(e, email.Email)
  129. if err != nil {
  130. return err
  131. } else if used {
  132. return ErrEmailAlreadyUsed{email.Email}
  133. }
  134. if err = ValidateEmail(email.Email); err != nil {
  135. return err
  136. }
  137. _, err = e.Insert(email)
  138. return err
  139. }
  140. // AddEmailAddress adds an email address to given user.
  141. func AddEmailAddress(email *EmailAddress) error {
  142. return addEmailAddress(x, email)
  143. }
  144. // AddEmailAddresses adds an email address to given user.
  145. func AddEmailAddresses(emails []*EmailAddress) error {
  146. if len(emails) == 0 {
  147. return nil
  148. }
  149. // Check if any of them has been used
  150. for i := range emails {
  151. emails[i].Email = strings.ToLower(strings.TrimSpace(emails[i].Email))
  152. used, err := IsEmailUsed(emails[i].Email)
  153. if err != nil {
  154. return err
  155. } else if used {
  156. return ErrEmailAlreadyUsed{emails[i].Email}
  157. }
  158. if err = ValidateEmail(emails[i].Email); err != nil {
  159. return err
  160. }
  161. }
  162. if _, err := x.Insert(emails); err != nil {
  163. return fmt.Errorf("Insert: %v", err)
  164. }
  165. return nil
  166. }
  167. // Activate activates the email address to given user.
  168. func (email *EmailAddress) Activate() error {
  169. sess := x.NewSession()
  170. defer sess.Close()
  171. if err := sess.Begin(); err != nil {
  172. return err
  173. }
  174. if err := email.updateActivation(sess, true); err != nil {
  175. return err
  176. }
  177. return sess.Commit()
  178. }
  179. func (email *EmailAddress) updateActivation(e Engine, activate bool) error {
  180. user, err := getUserByID(e, email.UID)
  181. if err != nil {
  182. return err
  183. }
  184. if user.Rands, err = GetUserSalt(); err != nil {
  185. return err
  186. }
  187. email.IsActivated = activate
  188. if _, err := e.ID(email.ID).Cols("is_activated").Update(email); err != nil {
  189. return err
  190. }
  191. return updateUserCols(e, user, "rands")
  192. }
  193. // DeleteEmailAddress deletes an email address of given user.
  194. func DeleteEmailAddress(email *EmailAddress) (err error) {
  195. var deleted int64
  196. // ask to check UID
  197. var address = EmailAddress{
  198. UID: email.UID,
  199. }
  200. if email.ID > 0 {
  201. deleted, err = x.ID(email.ID).Delete(&address)
  202. } else {
  203. deleted, err = x.
  204. Where("email=?", email.Email).
  205. Delete(&address)
  206. }
  207. if err != nil {
  208. return err
  209. } else if deleted != 1 {
  210. return ErrEmailAddressNotExist
  211. }
  212. return nil
  213. }
  214. // DeleteEmailAddresses deletes multiple email addresses
  215. func DeleteEmailAddresses(emails []*EmailAddress) (err error) {
  216. for i := range emails {
  217. if err = DeleteEmailAddress(emails[i]); err != nil {
  218. return err
  219. }
  220. }
  221. return nil
  222. }
  223. // MakeEmailPrimary sets primary email address of given user.
  224. func MakeEmailPrimary(email *EmailAddress) error {
  225. has, err := x.Get(email)
  226. if err != nil {
  227. return err
  228. } else if !has {
  229. return ErrEmailNotExist
  230. }
  231. if !email.IsActivated {
  232. return ErrEmailNotActivated
  233. }
  234. user := &User{}
  235. has, err = x.ID(email.UID).Get(user)
  236. if err != nil {
  237. return err
  238. } else if !has {
  239. return ErrUserNotExist{email.UID, "", 0}
  240. }
  241. // Make sure the former primary email doesn't disappear.
  242. formerPrimaryEmail := &EmailAddress{UID: user.ID, Email: user.Email}
  243. has, err = x.Get(formerPrimaryEmail)
  244. if err != nil {
  245. return err
  246. }
  247. sess := x.NewSession()
  248. defer sess.Close()
  249. if err = sess.Begin(); err != nil {
  250. return err
  251. }
  252. if !has {
  253. formerPrimaryEmail.UID = user.ID
  254. formerPrimaryEmail.IsActivated = user.IsActive
  255. if _, err = sess.Insert(formerPrimaryEmail); err != nil {
  256. return err
  257. }
  258. }
  259. user.Email = email.Email
  260. if _, err = sess.ID(user.ID).Cols("email").Update(user); err != nil {
  261. return err
  262. }
  263. return sess.Commit()
  264. }
  265. // SearchEmailOrderBy is used to sort the results from SearchEmails()
  266. type SearchEmailOrderBy string
  267. func (s SearchEmailOrderBy) String() string {
  268. return string(s)
  269. }
  270. // Strings for sorting result
  271. const (
  272. SearchEmailOrderByEmail SearchEmailOrderBy = "emails.email ASC, is_primary DESC, sortid ASC"
  273. SearchEmailOrderByEmailReverse SearchEmailOrderBy = "emails.email DESC, is_primary ASC, sortid DESC"
  274. SearchEmailOrderByName SearchEmailOrderBy = "`user`.lower_name ASC, is_primary DESC, sortid ASC"
  275. SearchEmailOrderByNameReverse SearchEmailOrderBy = "`user`.lower_name DESC, is_primary ASC, sortid DESC"
  276. )
  277. // SearchEmailOptions are options to search e-mail addresses for the admin panel
  278. type SearchEmailOptions struct {
  279. ListOptions
  280. Keyword string
  281. SortType SearchEmailOrderBy
  282. IsPrimary util.OptionalBool
  283. IsActivated util.OptionalBool
  284. }
  285. // SearchEmailResult is an e-mail address found in the user or email_address table
  286. type SearchEmailResult struct {
  287. UID int64
  288. Email string
  289. IsActivated bool
  290. IsPrimary bool
  291. // From User
  292. Name string
  293. FullName string
  294. }
  295. // SearchEmails takes options i.e. keyword and part of email name to search,
  296. // it returns results in given range and number of total results.
  297. func SearchEmails(opts *SearchEmailOptions) ([]*SearchEmailResult, int64, error) {
  298. // Unfortunately, UNION support for SQLite in xorm is currently broken, so we must
  299. // build the SQL ourselves.
  300. where := make([]string, 0, 5)
  301. args := make([]interface{}, 0, 5)
  302. emailsSQL := "(SELECT id as sortid, uid, email, is_activated, 0 as is_primary " +
  303. "FROM email_address " +
  304. "UNION ALL " +
  305. "SELECT id as sortid, id AS uid, email, is_active AS is_activated, 1 as is_primary " +
  306. "FROM `user` " +
  307. "WHERE type = ?) AS emails"
  308. args = append(args, UserTypeIndividual)
  309. if len(opts.Keyword) > 0 {
  310. // Note: % can be injected in the Keyword parameter, but it won't do any harm.
  311. where = append(where, "(lower(`user`.full_name) LIKE ? OR `user`.lower_name LIKE ? OR emails.email LIKE ?)")
  312. likeStr := "%" + strings.ToLower(opts.Keyword) + "%"
  313. args = append(args, likeStr)
  314. args = append(args, likeStr)
  315. args = append(args, likeStr)
  316. }
  317. switch {
  318. case opts.IsPrimary.IsTrue():
  319. where = append(where, "emails.is_primary = ?")
  320. args = append(args, true)
  321. case opts.IsPrimary.IsFalse():
  322. where = append(where, "emails.is_primary = ?")
  323. args = append(args, false)
  324. }
  325. switch {
  326. case opts.IsActivated.IsTrue():
  327. where = append(where, "emails.is_activated = ?")
  328. args = append(args, true)
  329. case opts.IsActivated.IsFalse():
  330. where = append(where, "emails.is_activated = ?")
  331. args = append(args, false)
  332. }
  333. var whereStr string
  334. if len(where) > 0 {
  335. whereStr = "WHERE " + strings.Join(where, " AND ")
  336. }
  337. joinSQL := "FROM " + emailsSQL + " INNER JOIN `user` ON `user`.id = emails.uid " + whereStr
  338. count, err := x.SQL("SELECT count(*) "+joinSQL, args...).Count()
  339. if err != nil {
  340. return nil, 0, fmt.Errorf("Count: %v", err)
  341. }
  342. orderby := opts.SortType.String()
  343. if orderby == "" {
  344. orderby = SearchEmailOrderByEmail.String()
  345. }
  346. querySQL := "SELECT emails.uid, emails.email, emails.is_activated, emails.is_primary, " +
  347. "`user`.name, `user`.full_name " + joinSQL + " ORDER BY " + orderby
  348. opts.setDefaultValues()
  349. rows, err := x.SQL(querySQL, args...).Rows(new(SearchEmailResult))
  350. if err != nil {
  351. return nil, 0, fmt.Errorf("Emails: %v", err)
  352. }
  353. // Page manually because xorm can't handle Limit() with raw SQL
  354. defer rows.Close()
  355. emails := make([]*SearchEmailResult, 0, opts.PageSize)
  356. skip := (opts.Page - 1) * opts.PageSize
  357. for rows.Next() {
  358. var email SearchEmailResult
  359. if err := rows.Scan(&email); err != nil {
  360. return nil, 0, err
  361. }
  362. if skip > 0 {
  363. skip--
  364. continue
  365. }
  366. emails = append(emails, &email)
  367. if len(emails) == opts.PageSize {
  368. break
  369. }
  370. }
  371. return emails, count, err
  372. }
  373. // ActivateUserEmail will change the activated state of an email address,
  374. // either primary (in the user table) or secondary (in the email_address table)
  375. func ActivateUserEmail(userID int64, email string, primary, activate bool) (err error) {
  376. sess := x.NewSession()
  377. defer sess.Close()
  378. if err = sess.Begin(); err != nil {
  379. return err
  380. }
  381. if primary {
  382. // Activate/deactivate a user's primary email address
  383. user := User{ID: userID, Email: email}
  384. if has, err := sess.Get(&user); err != nil {
  385. return err
  386. } else if !has {
  387. return fmt.Errorf("no such user: %d (%s)", userID, email)
  388. }
  389. if user.IsActive == activate {
  390. // Already in the desired state; no action
  391. return nil
  392. }
  393. if activate {
  394. if used, err := isEmailActive(sess, email, userID, 0); err != nil {
  395. return fmt.Errorf("isEmailActive(): %v", err)
  396. } else if used {
  397. return ErrEmailAlreadyUsed{Email: email}
  398. }
  399. }
  400. user.IsActive = activate
  401. if user.Rands, err = GetUserSalt(); err != nil {
  402. return fmt.Errorf("generate salt: %v", err)
  403. }
  404. if err = updateUserCols(sess, &user, "is_active", "rands"); err != nil {
  405. return fmt.Errorf("updateUserCols(): %v", err)
  406. }
  407. } else {
  408. // Activate/deactivate a user's secondary email address
  409. // First check if there's another user active with the same address
  410. addr := EmailAddress{UID: userID, Email: email}
  411. if has, err := sess.Get(&addr); err != nil {
  412. return err
  413. } else if !has {
  414. return fmt.Errorf("no such email: %d (%s)", userID, email)
  415. }
  416. if addr.IsActivated == activate {
  417. // Already in the desired state; no action
  418. return nil
  419. }
  420. if activate {
  421. if used, err := isEmailActive(sess, email, 0, addr.ID); err != nil {
  422. return fmt.Errorf("isEmailActive(): %v", err)
  423. } else if used {
  424. return ErrEmailAlreadyUsed{Email: email}
  425. }
  426. }
  427. if err = addr.updateActivation(sess, activate); err != nil {
  428. return fmt.Errorf("updateActivation(): %v", err)
  429. }
  430. }
  431. return sess.Commit()
  432. }