RegistrationTests.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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(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('Registration', function() {
  75. describe('LoginRateLimit', function() {
  76. beforeEach(function() {
  77. this.user = new User()
  78. this.badEmail = 'bademail@example.com'
  79. return (this.badPassword = 'badpassword')
  80. })
  81. it('should rate limit login attempts after 10 within two minutes', function(done) {
  82. return this.user.request.get('/login', (err, res, body) => {
  83. return async.timesSeries(
  84. 15,
  85. (n, cb) => {
  86. return this.user.getCsrfToken(error => {
  87. if (error != null) {
  88. return cb(error)
  89. }
  90. return this.user.request.post(
  91. {
  92. url: '/login',
  93. json: {
  94. email: this.badEmail,
  95. password: this.badPassword
  96. }
  97. },
  98. (err, response, body) => {
  99. return cb(
  100. null,
  101. __guard__(
  102. body != null ? body.message : undefined,
  103. x => x.text
  104. )
  105. )
  106. }
  107. )
  108. })
  109. },
  110. (err, results) => {
  111. // ten incorrect-credentials messages, then five rate-limit messages
  112. expect(results.length).to.equal(15)
  113. assert.deepEqual(
  114. results,
  115. _.concat(
  116. _.fill(
  117. [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  118. 'Your email or password is incorrect. Please try again'
  119. ),
  120. _.fill(
  121. [1, 2, 3, 4, 5],
  122. 'This account has had too many login requests. Please wait 2 minutes before trying to log in again'
  123. )
  124. )
  125. )
  126. return done()
  127. }
  128. )
  129. })
  130. })
  131. })
  132. describe('CSRF protection', function() {
  133. beforeEach(function() {
  134. this.user = new User()
  135. this.email = `test+${Math.random()}@example.com`
  136. return (this.password = 'password11')
  137. })
  138. afterEach(function(done) {
  139. return this.user.fullDeleteUser(this.email, done)
  140. })
  141. it('should register with the csrf token', function(done) {
  142. return this.user.request.get('/login', (err, res, body) => {
  143. return this.user.getCsrfToken(error => {
  144. return this.user.request.post(
  145. {
  146. url: '/register',
  147. json: {
  148. email: this.email,
  149. password: this.password
  150. },
  151. headers: {
  152. 'x-csrf-token': this.user.csrfToken
  153. }
  154. },
  155. (error, response, body) => {
  156. expect(err != null).to.equal(false)
  157. expect(response.statusCode).to.equal(200)
  158. return done()
  159. }
  160. )
  161. })
  162. })
  163. })
  164. it('should fail with no csrf token', function(done) {
  165. return this.user.request.get('/login', (err, res, body) => {
  166. return this.user.getCsrfToken(error => {
  167. return this.user.request.post(
  168. {
  169. url: '/register',
  170. json: {
  171. email: this.email,
  172. password: this.password
  173. },
  174. headers: {
  175. 'x-csrf-token': ''
  176. }
  177. },
  178. (error, response, body) => {
  179. expect(response.statusCode).to.equal(403)
  180. return done()
  181. }
  182. )
  183. })
  184. })
  185. })
  186. it('should fail with a stale csrf token', function(done) {
  187. return this.user.request.get('/login', (err, res, body) => {
  188. return this.user.getCsrfToken(error => {
  189. const oldCsrfToken = this.user.csrfToken
  190. return this.user.logout(err => {
  191. return this.user.request.post(
  192. {
  193. url: '/register',
  194. json: {
  195. email: this.email,
  196. password: this.password
  197. },
  198. headers: {
  199. 'x-csrf-token': oldCsrfToken
  200. }
  201. },
  202. (error, response, body) => {
  203. expect(response.statusCode).to.equal(403)
  204. return done()
  205. }
  206. )
  207. })
  208. })
  209. })
  210. })
  211. })
  212. describe('Register', function() {
  213. beforeEach(function() {
  214. return (this.user = new User())
  215. })
  216. it('Set emails attribute', function(done) {
  217. return this.user.register((error, user) => {
  218. expect(error).to.not.exist
  219. user.email.should.equal(this.user.email)
  220. user.emails.should.exist
  221. user.emails.should.be.a('array')
  222. user.emails.length.should.equal(1)
  223. user.emails[0].email.should.equal(this.user.email)
  224. return done()
  225. })
  226. })
  227. })
  228. describe('Register with bonus referal id', function() {
  229. beforeEach(function(done) {
  230. this.user1 = new User()
  231. this.user2 = new User()
  232. return async.series(
  233. [
  234. cb => this.user1.register(cb),
  235. cb =>
  236. this.user2.registerWithQuery(
  237. `?r=${this.user1.referal_id}&rm=d&rs=b`,
  238. cb
  239. )
  240. ],
  241. done
  242. )
  243. })
  244. it('Adds a referal when an id is supplied and the referal source is "bonus"', function(done) {
  245. return this.user1.get((error, user) => {
  246. expect(error).to.not.exist
  247. user.refered_user_count.should.eql(1)
  248. return done()
  249. })
  250. })
  251. })
  252. describe('LoginViaRegistration', function() {
  253. beforeEach(function(done) {
  254. this.timeout(60000)
  255. this.user1 = new User()
  256. this.user2 = new User()
  257. async.series(
  258. [
  259. cb => this.user1.login(cb),
  260. cb => this.user1.logout(cb),
  261. cb => redis.clearUserSessions(this.user1, cb),
  262. cb => this.user2.login(cb),
  263. cb => this.user2.logout(cb),
  264. cb => redis.clearUserSessions(this.user2, cb)
  265. ],
  266. done
  267. )
  268. return (this.project_id = null)
  269. })
  270. describe('[Security] Trying to register/login as another user', function() {
  271. it('should not allow sign in with secondary email', function(done) {
  272. const secondaryEmail = 'acceptance-test-secondary@example.com'
  273. return this.user1.addEmail(secondaryEmail, err => {
  274. return this.user1.loginWith(secondaryEmail, err => {
  275. expect(err != null).to.equal(false)
  276. return this.user1.isLoggedIn((err, isLoggedIn) => {
  277. expect(isLoggedIn).to.equal(false)
  278. return done()
  279. })
  280. })
  281. })
  282. })
  283. it('should have user1 login and create a project, which user2 cannot access', function(done) {
  284. let projectId
  285. async.series(
  286. [
  287. // user1 logs in and creates a project which only they can access
  288. cb => {
  289. this.user1.login(err => {
  290. expect(err).not.to.exist
  291. cb()
  292. })
  293. },
  294. cb => {
  295. this.user1.createProject('Private Project', (err, id) => {
  296. expect(err).not.to.exist
  297. projectId = id
  298. cb()
  299. })
  300. },
  301. cb => expectProjectAccess(this.user1, projectId, cb),
  302. cb => expectNoProjectAccess(this.user2, projectId, cb),
  303. // should prevent user2 from login/register with user1 email address
  304. cb => {
  305. tryLoginThroughRegistrationForm(
  306. this.user2,
  307. this.user1.email,
  308. 'totally_not_the_right_password',
  309. (err, response, body) => {
  310. expect(body.redir != null).to.equal(false)
  311. expect(body.message != null).to.equal(true)
  312. expect(body.message).to.have.all.keys('type', 'text')
  313. expect(body.message.type).to.equal('error')
  314. cb()
  315. }
  316. )
  317. },
  318. // check user still can't access the project
  319. cb => expectNoProjectAccess(this.user2, projectId, done)
  320. ],
  321. done
  322. )
  323. })
  324. })
  325. })
  326. })
  327. function __guard__(value, transform) {
  328. return typeof value !== 'undefined' && value !== null
  329. ? transform(value)
  330. : undefined
  331. }