OneTimeTokenHandlerTests.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. no-unused-vars,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  12. */
  13. const should = require('chai').should()
  14. const SandboxedModule = require('sandboxed-module')
  15. const assert = require('assert')
  16. const path = require('path')
  17. const sinon = require('sinon')
  18. const modulePath = path.join(
  19. __dirname,
  20. '../../../../app/src/Features/Security/OneTimeTokenHandler'
  21. )
  22. const { expect } = require('chai')
  23. const Errors = require('../../../../app/src/Features/Errors/Errors')
  24. const tk = require('timekeeper')
  25. describe('OneTimeTokenHandler', function() {
  26. beforeEach(function() {
  27. tk.freeze(Date.now()) // freeze the time for these tests
  28. this.stubbedToken = 'mock-token'
  29. this.callback = sinon.stub()
  30. return (this.OneTimeTokenHandler = SandboxedModule.require(modulePath, {
  31. globals: {
  32. console: console
  33. },
  34. requires: {
  35. 'settings-sharelatex': this.settings,
  36. 'logger-sharelatex': {
  37. log() {}
  38. },
  39. crypto: {
  40. randomBytes: () => this.stubbedToken
  41. },
  42. '../Errors/Errors': Errors,
  43. '../../infrastructure/mongodb': {
  44. db: (this.db = { tokens: {} })
  45. }
  46. }
  47. }))
  48. })
  49. afterEach(function() {
  50. return tk.reset()
  51. })
  52. describe('getNewToken', function() {
  53. beforeEach(function() {
  54. return (this.db.tokens.insertOne = sinon.stub().yields())
  55. })
  56. describe('normally', function() {
  57. beforeEach(function() {
  58. return this.OneTimeTokenHandler.getNewToken(
  59. 'password',
  60. 'mock-data-to-store',
  61. this.callback
  62. )
  63. })
  64. it('should insert a generated token with a 1 hour expiry', function() {
  65. return this.db.tokens.insertOne
  66. .calledWith({
  67. use: 'password',
  68. token: this.stubbedToken,
  69. createdAt: new Date(),
  70. expiresAt: new Date(Date.now() + 60 * 60 * 1000),
  71. data: 'mock-data-to-store'
  72. })
  73. .should.equal(true)
  74. })
  75. it('should call the callback with the token', function() {
  76. return this.callback
  77. .calledWith(null, this.stubbedToken)
  78. .should.equal(true)
  79. })
  80. })
  81. describe('with an optional expiresIn parameter', function() {
  82. beforeEach(function() {
  83. return this.OneTimeTokenHandler.getNewToken(
  84. 'password',
  85. 'mock-data-to-store',
  86. { expiresIn: 42 },
  87. this.callback
  88. )
  89. })
  90. it('should insert a generated token with a custom expiry', function() {
  91. return this.db.tokens.insertOne
  92. .calledWith({
  93. use: 'password',
  94. token: this.stubbedToken,
  95. createdAt: new Date(),
  96. expiresAt: new Date(Date.now() + 42 * 1000),
  97. data: 'mock-data-to-store'
  98. })
  99. .should.equal(true)
  100. })
  101. it('should call the callback with the token', function() {
  102. return this.callback
  103. .calledWith(null, this.stubbedToken)
  104. .should.equal(true)
  105. })
  106. })
  107. })
  108. describe('getValueFromTokenAndExpire', function() {
  109. describe('successfully', function() {
  110. beforeEach(function() {
  111. this.db.tokens.findOneAndUpdate = sinon
  112. .stub()
  113. .yields(null, { value: { data: 'mock-data' } })
  114. return this.OneTimeTokenHandler.getValueFromTokenAndExpire(
  115. 'password',
  116. 'mock-token',
  117. this.callback
  118. )
  119. })
  120. it('should expire the token', function() {
  121. return this.db.tokens.findOneAndUpdate
  122. .calledWith(
  123. {
  124. use: 'password',
  125. token: 'mock-token',
  126. expiresAt: { $gt: new Date() },
  127. usedAt: { $exists: false }
  128. },
  129. {
  130. $set: { usedAt: new Date() }
  131. }
  132. )
  133. .should.equal(true)
  134. })
  135. it('should return the data', function() {
  136. return this.callback.calledWith(null, 'mock-data').should.equal(true)
  137. })
  138. })
  139. describe('when a valid token is not found', function() {
  140. beforeEach(function() {
  141. this.db.tokens.findOneAndUpdate = sinon
  142. .stub()
  143. .yields(null, { value: null })
  144. return this.OneTimeTokenHandler.getValueFromTokenAndExpire(
  145. 'password',
  146. 'mock-token',
  147. this.callback
  148. )
  149. })
  150. it('should return a NotFoundError', function() {
  151. return this.callback
  152. .calledWith(sinon.match.instanceOf(Errors.NotFoundError))
  153. .should.equal(true)
  154. })
  155. })
  156. })
  157. })