TokenAccessHandlerTests.coffee 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. should = require('chai').should()
  2. SandboxedModule = require('sandboxed-module')
  3. assert = require('assert')
  4. path = require('path')
  5. sinon = require('sinon')
  6. modulePath = path.join __dirname, "../../../../app/js/Features/TokenAccess/TokenAccessHandler"
  7. expect = require("chai").expect
  8. ObjectId = require("mongojs").ObjectId
  9. describe "TokenAccessHandler", ->
  10. beforeEach ->
  11. @token = 'sometokenthing'
  12. @projectId = ObjectId()
  13. @project =
  14. _id: @projectId
  15. publicAccesLevel: 'tokenBased'
  16. @userId = ObjectId()
  17. @req = {}
  18. @TokenAccessHandler = SandboxedModule.require modulePath, requires:
  19. '../../models/Project': {Project: @Project = {}}
  20. 'settings-sharelatex': {}
  21. describe 'findProjectWithReadOnlyToken', ->
  22. beforeEach ->
  23. @Project.findOne = sinon.stub().callsArgWith(2, null, @project)
  24. it 'should call Project.findOne', (done) ->
  25. @TokenAccessHandler.findProjectWithReadOnlyToken @token, (err, project) =>
  26. expect(@Project.findOne.callCount).to.equal 1
  27. expect(@Project.findOne.calledWith({
  28. 'tokens.readOnly': @token,
  29. 'publicAccesLevel': 'tokenBased'
  30. })).to.equal true
  31. done()
  32. it 'should produce a project object with no error', (done) ->
  33. @TokenAccessHandler.findProjectWithReadOnlyToken @token, (err, project) =>
  34. expect(err).to.not.exist
  35. expect(project).to.exist
  36. expect(project).to.deep.equal @project
  37. done()
  38. describe 'when Project.findOne produces an error', ->
  39. beforeEach ->
  40. @Project.findOne = sinon.stub().callsArgWith(2, new Error('woops'))
  41. it 'should produce an error', (done) ->
  42. @TokenAccessHandler.findProjectWithReadOnlyToken @token, (err, project) =>
  43. expect(err).to.exist
  44. expect(project).to.not.exist
  45. expect(err).to.be.instanceof Error
  46. done()
  47. describe 'findProjectWithReadAndWriteToken', ->
  48. beforeEach ->
  49. @Project.findOne = sinon.stub().callsArgWith(2, null, @project)
  50. it 'should call Project.findOne', (done) ->
  51. @TokenAccessHandler.findProjectWithReadAndWriteToken @token, (err, project) =>
  52. expect(@Project.findOne.callCount).to.equal 1
  53. expect(@Project.findOne.calledWith({
  54. 'tokens.readAndWrite': @token,
  55. 'publicAccesLevel': 'tokenBased'
  56. })).to.equal true
  57. done()
  58. it 'should produce a project object with no error', (done) ->
  59. @TokenAccessHandler.findProjectWithReadAndWriteToken @token, (err, project) =>
  60. expect(err).to.not.exist
  61. expect(project).to.exist
  62. expect(project).to.deep.equal @project
  63. done()
  64. describe 'when Project.findOne produces an error', ->
  65. beforeEach ->
  66. @Project.findOne = sinon.stub().callsArgWith(2, new Error('woops'))
  67. it 'should produce an error', (done) ->
  68. @TokenAccessHandler.findProjectWithReadAndWriteToken @token, (err, project) =>
  69. expect(err).to.exist
  70. expect(project).to.not.exist
  71. expect(err).to.be.instanceof Error
  72. done()
  73. describe 'findPrivateOverleafProjectWithReadAndWriteToken', ->
  74. beforeEach ->
  75. @Project.findOne = sinon.stub().callsArgWith(2, null, @project)
  76. it 'should call Project.findOne', (done) ->
  77. @TokenAccessHandler.findPrivateOverleafProjectWithReadAndWriteToken @token, (err, project) =>
  78. expect(@Project.findOne.callCount).to.equal 1
  79. expect(@Project.findOne.calledWith({
  80. 'tokens.readAndWrite': @token,
  81. 'publicAccesLevel': 'private',
  82. 'overleaf.id': {$exists: true}
  83. })).to.equal true
  84. done()
  85. it 'should produce a project object with no error', (done) ->
  86. @TokenAccessHandler.findPrivateOverleafProjectWithReadAndWriteToken @token, (err, project) =>
  87. expect(err).to.not.exist
  88. expect(project).to.exist
  89. expect(project).to.deep.equal @project
  90. done()
  91. describe 'when Project.findOne produces an error', ->
  92. beforeEach ->
  93. @Project.findOne = sinon.stub().callsArgWith(2, new Error('woops'))
  94. it 'should produce an error', (done) ->
  95. @TokenAccessHandler.findPrivateOverleafProjectWithReadAndWriteToken @token, (err, project) =>
  96. expect(err).to.exist
  97. expect(project).to.not.exist
  98. expect(err).to.be.instanceof Error
  99. done()
  100. describe 'addReadOnlyUserToProject', ->
  101. beforeEach ->
  102. @Project.update = sinon.stub().callsArgWith(2, null)
  103. it 'should call Project.update', (done) ->
  104. @TokenAccessHandler.addReadOnlyUserToProject @userId, @projectId, (err) =>
  105. expect(@Project.update.callCount).to.equal 1
  106. expect(@Project.update.calledWith({
  107. _id: @projectId
  108. })).to.equal true
  109. expect(@Project.update.lastCall.args[1]['$addToSet'])
  110. .to.have.keys 'tokenAccessReadOnly_refs'
  111. done()
  112. it 'should not produce an error', (done) ->
  113. @TokenAccessHandler.addReadOnlyUserToProject @userId, @projectId, (err) =>
  114. expect(err).to.not.exist
  115. done()
  116. describe 'when Project.update produces an error', ->
  117. beforeEach ->
  118. @Project.update = sinon.stub().callsArgWith(2, new Error('woops'))
  119. it 'should produce an error', (done) ->
  120. @TokenAccessHandler.addReadOnlyUserToProject @userId, @projectId, (err) =>
  121. expect(err).to.exist
  122. done()
  123. describe 'addReadAndWriteUserToProject', ->
  124. beforeEach ->
  125. @Project.update = sinon.stub().callsArgWith(2, null)
  126. it 'should call Project.update', (done) ->
  127. @TokenAccessHandler.addReadAndWriteUserToProject @userId, @projectId, (err) =>
  128. expect(@Project.update.callCount).to.equal 1
  129. expect(@Project.update.calledWith({
  130. _id: @projectId
  131. })).to.equal true
  132. expect(@Project.update.lastCall.args[1]['$addToSet'])
  133. .to.have.keys 'tokenAccessReadAndWrite_refs'
  134. done()
  135. it 'should not produce an error', (done) ->
  136. @TokenAccessHandler.addReadAndWriteUserToProject @userId, @projectId, (err) =>
  137. expect(err).to.not.exist
  138. done()
  139. describe 'when Project.update produces an error', ->
  140. beforeEach ->
  141. @Project.update = sinon.stub().callsArgWith(2, new Error('woops'))
  142. it 'should produce an error', (done) ->
  143. @TokenAccessHandler.addReadAndWriteUserToProject @userId, @projectId, (err) =>
  144. expect(err).to.exist
  145. done()
  146. describe 'grantSessionTokenAccess', ->
  147. beforeEach ->
  148. @req = {session: {}, headers: {}}
  149. it 'should add the token to the session', (done) ->
  150. @TokenAccessHandler.grantSessionTokenAccess(@req, @projectId, @token)
  151. expect(@req.session.anonTokenAccess[@projectId.toString()])
  152. .to.equal @token
  153. done()
  154. describe 'isValidToken', ->
  155. describe 'when a read-only project is found', ->
  156. beforeEach ->
  157. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  158. .callsArgWith(1, null, null)
  159. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  160. .callsArgWith(1, null, @project)
  161. it 'should try to find projects with both kinds of token', (done) ->
  162. @TokenAccessHandler.isValidToken @projectId, @token, (err, allowed) =>
  163. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  164. .to.equal 1
  165. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  166. .to.equal 1
  167. done()
  168. it 'should allow read-only access', (done) ->
  169. @TokenAccessHandler.isValidToken @projectId, @token, (err, rw, ro) =>
  170. expect(err).to.not.exist
  171. expect(rw).to.equal false
  172. expect(ro).to.equal true
  173. done()
  174. describe 'when a read-and-write project is found', ->
  175. beforeEach ->
  176. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  177. .callsArgWith(1, null, @project)
  178. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  179. .callsArgWith(1, null, null)
  180. it 'should try to find projects with both kinds of token', (done) ->
  181. @TokenAccessHandler.isValidToken @projectId, @token, (err, allowed) =>
  182. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  183. .to.equal 1
  184. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  185. .to.equal 1
  186. done()
  187. it 'should allow read-and-write access', (done) ->
  188. @TokenAccessHandler.isValidToken @projectId, @token, (err, rw, ro) =>
  189. expect(err).to.not.exist
  190. expect(rw).to.equal true
  191. expect(ro).to.equal false
  192. done()
  193. describe 'when no project is found', ->
  194. beforeEach ->
  195. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  196. .callsArgWith(1, null, null)
  197. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  198. .callsArgWith(1, null, null)
  199. it 'should try to find projects with both kinds of token', (done) ->
  200. @TokenAccessHandler.isValidToken @projectId, @token, (err, allowed) =>
  201. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  202. .to.equal 1
  203. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  204. .to.equal 1
  205. done()
  206. it 'should not allow any access', (done) ->
  207. @TokenAccessHandler.isValidToken @projectId, @token, (err, rw, ro) =>
  208. expect(err).to.not.exist
  209. expect(rw).to.equal false
  210. expect(ro).to.equal false
  211. done()
  212. describe 'when findProject produces an error', ->
  213. beforeEach ->
  214. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  215. .callsArgWith(1, null, null)
  216. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  217. .callsArgWith(1, new Error('woops'))
  218. it 'should try to find projects with both kinds of token', (done) ->
  219. @TokenAccessHandler.isValidToken @projectId, @token, (err, allowed) =>
  220. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  221. .to.equal 1
  222. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  223. .to.equal 1
  224. done()
  225. it 'should produce an error and not allow access', (done) ->
  226. @TokenAccessHandler.isValidToken @projectId, @token, (err, rw, ro) =>
  227. expect(err).to.exist
  228. expect(err).to.be.instanceof Error
  229. expect(rw).to.equal undefined
  230. expect(ro).to.equal undefined
  231. done()
  232. describe 'when project is not set to token-based access', ->
  233. beforeEach ->
  234. @project.publicAccesLevel = 'private'
  235. describe 'for read-and-write project', ->
  236. beforeEach ->
  237. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  238. .callsArgWith(1, null, @project)
  239. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  240. .callsArgWith(1, null, null)
  241. it 'should try to find projects with both kinds of token', (done) ->
  242. @TokenAccessHandler.isValidToken @projectId, @token, (err, allowed) =>
  243. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  244. .to.equal 1
  245. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  246. .to.equal 1
  247. done()
  248. it 'should not allow any access', (done) ->
  249. @TokenAccessHandler.isValidToken @projectId, @token, (err, rw, ro) =>
  250. expect(err).to.not.exist
  251. expect(rw).to.equal false
  252. expect(ro).to.equal false
  253. done()
  254. describe 'for read-only project', ->
  255. beforeEach ->
  256. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  257. .callsArgWith(1, null, null)
  258. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  259. .callsArgWith(1, null, @project)
  260. it 'should try to find projects with both kinds of token', (done) ->
  261. @TokenAccessHandler.isValidToken @projectId, @token, (err, allowed) =>
  262. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  263. .to.equal 1
  264. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  265. .to.equal 1
  266. done()
  267. it 'should not allow any access', (done) ->
  268. @TokenAccessHandler.isValidToken @projectId, @token, (err, rw, ro) =>
  269. expect(err).to.not.exist
  270. expect(rw).to.equal false
  271. expect(ro).to.equal false
  272. done()
  273. describe 'with nothing', ->
  274. beforeEach ->
  275. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  276. .callsArgWith(1, null, @project)
  277. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  278. .callsArgWith(1, null, null)
  279. it 'should not call findProjectWithReadOnlyToken', (done) ->
  280. @TokenAccessHandler.isValidToken @projectId, null, (err, allowed) =>
  281. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  282. .to.equal 0
  283. done()
  284. it 'should try to find projects with both kinds of token', (done) ->
  285. @TokenAccessHandler.isValidToken @projectId, null, (err, allowed) =>
  286. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  287. .to.equal 0
  288. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  289. .to.equal 0
  290. done()
  291. it 'should not allow any access', (done) ->
  292. @TokenAccessHandler.isValidToken @projectId, null, (err, rw, ro) =>
  293. expect(err).to.not.exist
  294. expect(rw).to.equal false
  295. expect(ro).to.equal false
  296. done()