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.js 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. 'use strict'
  2. require('dotenv').config()
  3. const Util = require('util')
  4. const Jwt = require('@hapi/jwt')
  5. const Schmervice = require('@hapipal/schmervice')
  6. const SecurePassword = require('secure-password')
  7. // Configuration for Brevo
  8. const SibApiV3Sdk = require('sib-api-v3-sdk')
  9. const defaultClient = SibApiV3Sdk.ApiClient.instance
  10. const apiKey = defaultClient.authentications['api-key']
  11. apiKey.apiKey = process.env.BREVO_KEY
  12. const apiInstance = new SibApiV3Sdk.TransactionalEmailsApi()
  13. const hasher = async (pwd, steak) => {
  14. const hash = await pwd.hash(steak)
  15. const result = await pwd.verify(steak, hash)
  16. let squirtle = null
  17. switch (result) {
  18. case SecurePassword.INVALID_UNRECOGNIZED_HASH:
  19. return console.error(
  20. 'This hash was not made with secure-password. Attempt legacy algorithm',
  21. )
  22. case SecurePassword.INVALID:
  23. return console.log('Invalid password')
  24. case SecurePassword.VALID:
  25. return result
  26. case SecurePassword.VALID_NEEDS_REHASH:
  27. console.log('Yay you made it, wait for us to improve your safety')
  28. try {
  29. squirtle = await pwd.hash(steak)
  30. // console.log('improvedHash', squirtle)
  31. // const saveHash = Auth.insert({user_email: matchingEmails}, ).into('token')
  32. return squirtle
  33. } catch (err) {
  34. console.error(
  35. 'You are authenticated, but we could not improve your safety this time around',
  36. )
  37. }
  38. break
  39. }
  40. }
  41. /** Class for methods used in the User plugin */
  42. module.exports = class UserService extends Schmervice.Service {
  43. /**
  44. * Unsure of what our constructor does
  45. * @param {...any} args
  46. */
  47. constructor(...args) {
  48. super(...args)
  49. const pwd = new SecurePassword()
  50. this.pwd = {
  51. hash: Util.promisify(pwd.hash.bind(pwd)),
  52. verify: Util.promisify(pwd.verify.bind(pwd)),
  53. }
  54. }
  55. /**
  56. * Use knex to find users with id column
  57. * @param {number} id
  58. * @param {*} txn
  59. * @returns
  60. */
  61. async findById(id, txn) {
  62. const { User } = this.server.models()
  63. return await User.query(txn)
  64. .throwIfNotFound()
  65. .first()
  66. .where({ user_id: id })
  67. }
  68. /**
  69. * Use knew to find first user with username
  70. * @param {*} username
  71. * @param {*} txn
  72. * @returns
  73. */
  74. async findByUsername(username, txn) {
  75. const { User } = this.server.models()
  76. return await User.query(txn)
  77. .throwIfNotFound()
  78. .first()
  79. .where({ user_name: username })
  80. }
  81. /**
  82. * Signup function
  83. * @param {*} param0
  84. * @param {*} txn
  85. * @returns
  86. */
  87. async signup({ password, userInfo, created_at }, txn) {
  88. const { User, Auth } = this.server.models()
  89. const matchingEmails = await User.query().where(
  90. 'user_email',
  91. userInfo.user_email,
  92. )
  93. if (matchingEmails.length > 0) {
  94. throw `User ${userInfo.user_email} already exists: Cannot create a user without a unique email`
  95. }
  96. // Insert User Info to User table
  97. const insertUser = await User.query().insert(userInfo)
  98. // insert a row with blank password to be updated by changePassword()
  99. await Auth.query().insert({
  100. user_email: insertUser.user_email,
  101. created_at: created_at,
  102. token: null,
  103. })
  104. // update null token with hashed password
  105. await this.changePassword(insertUser.user_email, password, txn)
  106. return {
  107. user_id: insertUser.id,
  108. user_name: insertUser.user_name,
  109. user_email: insertUser.user_email,
  110. is_poster: insertUser.is_poster,
  111. is_admin: insertUser.is_admin,
  112. is_verified: insertUser.is_verified,
  113. }
  114. }
  115. /**
  116. * Updates user's info
  117. * @param {number} id
  118. * @param {*} param1
  119. * @param {*} txn
  120. * @returns
  121. */
  122. async update(id, { password, ...userInfo }, txn) {
  123. const { User } = this.server.models()
  124. if (Object.keys(userInfo).length > 0) {
  125. await User.query(txn)
  126. .throwIfNotFound()
  127. .where({ id })
  128. .patch(userInfo)
  129. }
  130. if (password) {
  131. await this.changePassword(id, password, txn)
  132. }
  133. return id
  134. }
  135. /**
  136. * Self explanatory
  137. * @param {*} param0
  138. * @param {*} txn
  139. * @returns
  140. */
  141. async login({ email, password }, txn) {
  142. const { User, Auth } = this.server.models()
  143. const user = await Auth.query(txn)
  144. .throwIfNotFound()
  145. .first()
  146. .where({ user_email: email })
  147. const bufferPepper = Buffer.from(process.env.PEPPER + password)
  148. /** Uncomment to run password check using SecurePassword */
  149. const passwordCheck = await this.pwd.verify(bufferPepper, user.token)
  150. if (passwordCheck === SecurePassword.VALID_NEEDS_REHASH) {
  151. await this.changePassword(user.user_email, password, txn)
  152. } else if (passwordCheck !== SecurePassword.VALID) {
  153. throw User.createNotFoundError()
  154. }
  155. return user
  156. }
  157. /**
  158. * Create a token to be sent in request headers
  159. * @param {User} user
  160. * @returns {Token}
  161. */
  162. createToken(user) {
  163. const key = this.server.registrations['main-app-plugin'].options.jwtKey
  164. return Jwt.token.generate(
  165. {
  166. aud: 'urn:audience:test',
  167. iss: 'urn:issuer:test',
  168. email: user.user_email,
  169. },
  170. {
  171. key: key,
  172. algorithm: 'HS256',
  173. },
  174. {
  175. ttlSec: 4 * 60 * 60, // 7 days
  176. },
  177. )
  178. }
  179. /**
  180. * Use knex to try to change password entry
  181. * @param {number} id
  182. * @param {string} password
  183. * @param {*} txn
  184. * @returns {number}
  185. */
  186. async changePassword(email, password, txn) {
  187. const { Auth } = this.server.models()
  188. const hashed = await this.pwd.hash(
  189. Buffer.from(process.env.PEPPER + password),
  190. )
  191. await Auth.query(txn)
  192. .throwIfNotFound()
  193. .where({ user_email: email })
  194. .patch({
  195. // user_email: email,
  196. token: hashed,
  197. })
  198. return email
  199. }
  200. async getPassword(email, txn) {
  201. const { Auth } = this.server.models()
  202. const passwordRow = await Auth.query(txn)
  203. .where('user_email', email)
  204. .first()
  205. return passwordRow ? passwordRow.token : null
  206. }
  207. /**
  208. * Sends a Transactional Email via Brevo
  209. * @ returns {Object}
  210. */
  211. async emailSent(userEmail) {
  212. const sendSmtpEmail = {
  213. to: [
  214. {
  215. email: userEmail,
  216. },
  217. ],
  218. templateId: 1,
  219. params: {
  220. // TODO: create basic hashing email above and cache hash...
  221. email: 'tobehashedemail',
  222. },
  223. }
  224. await apiInstance.sendTransacEmail(sendSmtpEmail).then(
  225. data => {
  226. console.log('data from sendTransacEmail :=>', data)
  227. return {
  228. wasSuccessfull: true,
  229. data: data,
  230. }
  231. },
  232. error => {
  233. console.error('ERROR :=>', error)
  234. return {
  235. wasSuccessfull: false,
  236. error: error,
  237. }
  238. },
  239. )
  240. }
  241. }