RegistrationTests.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. max-len,
  5. no-return-assign,
  6. no-unused-vars,
  7. */
  8. // TODO: This file was created by bulk-decaffeinate.
  9. // Fix any style issues and re-enable lint.
  10. /*
  11. * decaffeinate suggestions:
  12. * DS102: Remove unnecessary code created because of implicit returns
  13. * DS103: Rewrite code to no longer use __guard__
  14. * DS207: Consider shorter variations of null checks
  15. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  16. */
  17. const { expect } = require('chai')
  18. const { assert } = require('chai')
  19. const async = require('async')
  20. const User = require('./helpers/User')
  21. const request = require('./helpers/request')
  22. const settings = require('settings-sharelatex')
  23. const redis = require('./helpers/redis')
  24. const _ = require('lodash')
  25. // Currently this is testing registration via the 'public-registration' module,
  26. // whereas in production we're using the 'overleaf-integration' module.
  27. // Expectations
  28. const expectProjectAccess = function(user, projectId, callback) {
  29. // should have access to project
  30. if (callback == null) {
  31. callback = function(err, result) {}
  32. }
  33. return user.openProject(projectId, err => {
  34. expect(err).to.be.oneOf([null, undefined])
  35. return callback()
  36. })
  37. }
  38. const expectNoProjectAccess = function(user, projectId, callback) {
  39. // should not have access to project page
  40. if (callback == null) {
  41. callback = function(err, result) {}
  42. }
  43. return user.openProject(projectId, err => {
  44. expect(err).to.be.instanceof(Error)
  45. return callback()
  46. })
  47. }
  48. // Actions
  49. const tryLoginThroughRegistrationForm = function(
  50. user,
  51. email,
  52. password,
  53. callback
  54. ) {
  55. if (callback == null) {
  56. callback = function(err, response, body) {}
  57. }
  58. return user.getCsrfToken(function(err) {
  59. if (err != null) {
  60. return callback(err)
  61. }
  62. return user.request.post(
  63. {
  64. url: '/register',
  65. json: {
  66. email,
  67. password
  68. }
  69. },
  70. callback
  71. )
  72. })
  73. }
  74. describe('LoginRateLimit', function() {
  75. before(function() {
  76. this.user = new User()
  77. this.badEmail = 'bademail@example.com'
  78. return (this.badPassword = 'badpassword')
  79. })
  80. it('should rate limit login attempts after 10 within two minutes', function(done) {
  81. return this.user.request.get('/login', (err, res, body) => {
  82. return async.timesSeries(
  83. 15,
  84. (n, cb) => {
  85. return this.user.getCsrfToken(error => {
  86. if (error != null) {
  87. return cb(error)
  88. }
  89. return this.user.request.post(
  90. {
  91. url: '/login',
  92. json: {
  93. email: this.badEmail,
  94. password: this.badPassword
  95. }
  96. },
  97. (err, response, body) => {
  98. return cb(
  99. null,
  100. __guard__(
  101. body != null ? body.message : undefined,
  102. x => x.text
  103. )
  104. )
  105. }
  106. )
  107. })
  108. },
  109. (err, results) => {
  110. // ten incorrect-credentials messages, then five rate-limit messages
  111. expect(results.length).to.equal(15)
  112. assert.deepEqual(
  113. results,
  114. _.concat(
  115. _.fill(
  116. [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  117. 'Your email or password is incorrect. Please try again'
  118. ),
  119. _.fill(
  120. [1, 2, 3, 4, 5],
  121. 'This account has had too many login requests. Please wait 2 minutes before trying to log in again'
  122. )
  123. )
  124. )
  125. return done()
  126. }
  127. )
  128. })
  129. })
  130. })
  131. describe('CSRF protection', function() {
  132. beforeEach(function() {
  133. this.user = new User()
  134. this.email = `test+${Math.random()}@example.com`
  135. return (this.password = 'password11')
  136. })
  137. afterEach(function() {
  138. return this.user.full_delete_user(this.email)
  139. })
  140. it('should register with the csrf token', function(done) {
  141. return this.user.request.get('/login', (err, res, body) => {
  142. return this.user.getCsrfToken(error => {
  143. return this.user.request.post(
  144. {
  145. url: '/register',
  146. json: {
  147. email: this.email,
  148. password: this.password
  149. },
  150. headers: {
  151. 'x-csrf-token': this.user.csrfToken
  152. }
  153. },
  154. (error, response, body) => {
  155. expect(err != null).to.equal(false)
  156. expect(response.statusCode).to.equal(200)
  157. return done()
  158. }
  159. )
  160. })
  161. })
  162. })
  163. it('should fail with no csrf token', function(done) {
  164. return this.user.request.get('/login', (err, res, body) => {
  165. return this.user.getCsrfToken(error => {
  166. return this.user.request.post(
  167. {
  168. url: '/register',
  169. json: {
  170. email: this.email,
  171. password: this.password
  172. },
  173. headers: {
  174. 'x-csrf-token': ''
  175. }
  176. },
  177. (error, response, body) => {
  178. expect(response.statusCode).to.equal(403)
  179. return done()
  180. }
  181. )
  182. })
  183. })
  184. })
  185. it('should fail with a stale csrf token', function(done) {
  186. return this.user.request.get('/login', (err, res, body) => {
  187. return this.user.getCsrfToken(error => {
  188. const oldCsrfToken = this.user.csrfToken
  189. return this.user.logout(err => {
  190. return this.user.request.post(
  191. {
  192. url: '/register',
  193. json: {
  194. email: this.email,
  195. password: this.password
  196. },
  197. headers: {
  198. 'x-csrf-token': oldCsrfToken
  199. }
  200. },
  201. (error, response, body) => {
  202. expect(response.statusCode).to.equal(403)
  203. return done()
  204. }
  205. )
  206. })
  207. })
  208. })
  209. })
  210. })
  211. describe('Register', function() {
  212. before(function() {
  213. return (this.user = new User())
  214. })
  215. it('Set emails attribute', function(done) {
  216. return this.user.register((error, user) => {
  217. expect(error).to.not.exist
  218. user.email.should.equal(this.user.email)
  219. user.emails.should.exist
  220. user.emails.should.be.a('array')
  221. user.emails.length.should.equal(1)
  222. user.emails[0].email.should.equal(this.user.email)
  223. return done()
  224. })
  225. })
  226. })
  227. describe('Register with bonus referal id', function() {
  228. before(function(done) {
  229. this.user1 = new User()
  230. this.user2 = new User()
  231. return async.series(
  232. [
  233. cb => this.user1.register(cb),
  234. cb =>
  235. this.user2.registerWithQuery(
  236. `?r=${this.user1.referal_id}&rm=d&rs=b`,
  237. cb
  238. )
  239. ],
  240. done
  241. )
  242. })
  243. it('Adds a referal when an id is supplied and the referal source is "bonus"', function(done) {
  244. return this.user1.get((error, user) => {
  245. expect(error).to.not.exist
  246. user.refered_user_count.should.eql(1)
  247. return done()
  248. })
  249. })
  250. })
  251. describe('LoginViaRegistration', function() {
  252. before(function(done) {
  253. this.timeout(60000)
  254. this.user1 = new User()
  255. this.user2 = new User()
  256. async.series(
  257. [
  258. cb => this.user1.login(cb),
  259. cb => this.user1.logout(cb),
  260. cb => redis.clearUserSessions(this.user1, cb),
  261. cb => this.user2.login(cb),
  262. cb => this.user2.logout(cb),
  263. cb => redis.clearUserSessions(this.user2, cb)
  264. ],
  265. done
  266. )
  267. return (this.project_id = null)
  268. })
  269. describe('[Security] Trying to register/login as another user', function() {
  270. it('should not allow sign in with secondary email', function(done) {
  271. const secondaryEmail = 'acceptance-test-secondary@example.com'
  272. return this.user1.addEmail(secondaryEmail, err => {
  273. return this.user1.loginWith(secondaryEmail, err => {
  274. expect(err != null).to.equal(false)
  275. return this.user1.isLoggedIn(function(err, isLoggedIn) {
  276. expect(isLoggedIn).to.equal(false)
  277. return done()
  278. })
  279. })
  280. })
  281. })
  282. it('should have user1 login', function(done) {
  283. return this.user1.login(function(err) {
  284. expect(err != null).to.equal(false)
  285. return done()
  286. })
  287. })
  288. it('should have user1 create a project', function(done) {
  289. return this.user1.createProject('Private Project', (err, project_id) => {
  290. expect(err != null).to.equal(false)
  291. this.project_id = project_id
  292. return done()
  293. })
  294. })
  295. it('should ensure user1 can access their project', function(done) {
  296. return expectProjectAccess(this.user1, this.project_id, done)
  297. })
  298. it('should ensure user2 cannot access the project', function(done) {
  299. return expectNoProjectAccess(this.user2, this.project_id, done)
  300. })
  301. it('should prevent user2 from login/register with user1 email address', function(done) {
  302. return tryLoginThroughRegistrationForm(
  303. this.user2,
  304. this.user1.email,
  305. 'totally_not_the_right_password',
  306. (err, response, body) => {
  307. expect(body.redir != null).to.equal(false)
  308. expect(body.message != null).to.equal(true)
  309. expect(body.message).to.have.all.keys('type', 'text')
  310. expect(body.message.type).to.equal('error')
  311. return done()
  312. }
  313. )
  314. })
  315. it('should still ensure user2 cannot access the project', function(done) {
  316. return expectNoProjectAccess(this.user2, this.project_id, done)
  317. })
  318. })
  319. })
  320. function __guard__(value, transform) {
  321. return typeof value !== 'undefined' && value !== null
  322. ? transform(value)
  323. : undefined
  324. }