From 379788a8a62d328dcd212e0468de9ed046c37914 Mon Sep 17 00:00:00 2001 From: Antoine Clausse Date: Fri, 16 Jan 2026 11:58:30 +0100 Subject: [PATCH] [web] Promisify RegistrationTests and SessionTests (#30646) * Promisify RegistrationTests.mjs * Re-add `await this.user.request.get('/login')` and remove comment * Promisify SessionTests.mjs * Remove callback versions of RedisHelper * Re-add `await this.user.request.get('/login')` GitOrigin-RevId: 9d0e21336f348c6aff99cb262738163e611f5712 --- .../test/acceptance/src/RegistrationTests.mjs | 302 +++----- .../web/test/acceptance/src/SessionTests.mjs | 650 +++++------------- .../web/test/acceptance/src/helpers/redis.mjs | 65 +- 3 files changed, 323 insertions(+), 694 deletions(-) diff --git a/services/web/test/acceptance/src/RegistrationTests.mjs b/services/web/test/acceptance/src/RegistrationTests.mjs index c5fd66def7..49f90026c8 100644 --- a/services/web/test/acceptance/src/RegistrationTests.mjs +++ b/services/web/test/acceptance/src/RegistrationTests.mjs @@ -1,5 +1,4 @@ import { expect } from 'chai' -import async from 'async' import metrics from './helpers/metrics.mjs' import User from './helpers/User.mjs' import redis from './helpers/redis.mjs' @@ -8,43 +7,28 @@ import Features from '../../../app/src/infrastructure/Features.mjs' const UserPromises = User.promises // Expectations -const expectProjectAccess = function (user, projectId, callback) { +async function expectProjectAccess(user, projectId) { // should have access to project - user.openProject(projectId, err => { - expect(err).to.be.oneOf([null, undefined]) - return callback() - }) + await user.openProject(projectId) } -const expectNoProjectAccess = function (user, projectId, callback) { +async function expectNoProjectAccess(user, projectId) { // should not have access to project page - user.openProject(projectId, err => { - expect(err).to.be.instanceof(Error) - return callback() - }) + let error = null + try { + await user.openProject(projectId) + } catch (err) { + error = err + } + expect(error).to.be.instanceof(Error) } // Actions -const tryLoginThroughRegistrationForm = function ( - user, - email, - password, - callback -) { - user.getCsrfToken(err => { - if (err != null) { - return callback(err) - } - user.request.post( - { - url: '/register', - json: { - email, - password, - }, - }, - callback - ) +async function tryLoginThroughRegistrationForm(user, email, password) { + await user.getCsrfToken() + return await user.doRequest('POST', { + url: '/register', + json: { email, password }, }) } @@ -204,95 +188,63 @@ describe('Registration', function () { }) beforeEach(function () { - this.user = new User() + this.user = new UserPromises() this.email = `test+${Math.random()}@example.com` this.password = 'password11' }) - afterEach(function (done) { - this.user.fullDeleteUser(this.email, done) + afterEach(async function () { + await this.user.fullDeleteUser(this.email) }) - it('should register with the csrf token', function (done) { - this.user.request.get('/login', (err, res, body) => { - expect(err).to.not.exist - this.user.getCsrfToken(error => { - expect(error).to.not.exist - this.user.request.post( - { - url: '/register', - json: { - email: this.email, - password: this.password, - }, - headers: { - 'x-csrf-token': this.user.csrfToken, - }, - }, - (error, response, body) => { - expect(error).to.not.exist - expect(response.statusCode).to.equal(200) - return done() - } - ) - }) + it('should register with the csrf token', async function () { + await this.user.request.get('/login') + await this.user.getCsrfToken() + const { response } = await this.user.doRequest('POST', { + url: '/register', + json: { + email: this.email, + password: this.password, + }, + headers: { + 'x-csrf-token': this.user.csrfToken, + }, }) + expect(response.statusCode).to.equal(200) }) - it('should fail with no csrf token', function (done) { - this.user.request.get('/login', (err, res, body) => { - expect(err).to.not.exist - this.user.getCsrfToken(error => { - expect(error).to.not.exist - this.user.request.post( - { - url: '/register', - json: { - email: this.email, - password: this.password, - }, - headers: { - 'x-csrf-token': '', - }, - }, - (error, response, body) => { - expect(error).to.not.exist - expect(response.statusCode).to.equal(403) - return done() - } - ) - }) + it('should fail with no csrf token', async function () { + await this.user.request.get('/login') + await this.user.getCsrfToken() + const { response } = await this.user.doRequest('POST', { + url: '/register', + json: { + email: this.email, + password: this.password, + }, + headers: { + 'x-csrf-token': '', + }, }) + expect(response.statusCode).to.equal(403) }) - it('should fail with a stale csrf token', function (done) { - this.user.request.get('/login', (err, res, body) => { - expect(err).to.not.exist - this.user.getCsrfToken(error => { - expect(error).to.not.exist - const oldCsrfToken = this.user.csrfToken - this.user.logout(err => { - expect(err).to.not.exist - this.user.request.post( - { - url: '/register', - json: { - email: this.email, - password: this.password, - }, - headers: { - 'x-csrf-token': oldCsrfToken, - }, - }, - (error, response, body) => { - expect(error).to.not.exist - expect(response.statusCode).to.equal(403) - return done() - } - ) - }) - }) + it('should fail with a stale csrf token', async function () { + await this.user.request.get('/login') + await this.user.getCsrfToken() + const oldCsrfToken = this.user.csrfToken + await this.user.logout() + const { response } = await this.user.doRequest('POST', { + url: '/register', + json: { + email: this.email, + password: this.password, + }, + headers: { + 'x-csrf-token': oldCsrfToken, + }, }) + expect(response.statusCode).to.equal(403) }) }) @@ -304,38 +256,32 @@ describe('Registration', function () { }) beforeEach(function () { - this.user = new User() + this.user = new UserPromises() }) - it('Set emails attribute', function (done) { - this.user.register((error, user) => { - expect(error).to.not.exist - user.email.should.equal(this.user.email) - user.emails.should.exist - user.emails.should.be.a('array') - user.emails.length.should.equal(1) - user.emails[0].email.should.equal(this.user.email) - return done() - }) + it('Set emails attribute', async function () { + const user = await this.user.register() + expect(user.email).to.equal(this.user.email) + expect(user.emails).to.exist + expect(user.emails).to.be.a('array') + expect(user.emails.length).to.equal(1) + expect(user.emails[0].email).to.equal(this.user.email) }) }) describe('LoginViaRegistration', function () { - beforeEach(function (done) { + beforeEach(async function () { this.timeout(60000) - this.user1 = new User() - this.user2 = new User() - async.series( - [ - cb => this.user1.login(cb), - cb => this.user1.logout(cb), - cb => redis.clearUserSessions(this.user1, cb), - cb => this.user2.login(cb), - cb => this.user2.logout(cb), - cb => redis.clearUserSessions(this.user2, cb), - ], - done - ) + this.user1 = new UserPromises() + this.user2 = new UserPromises() + + await this.user1.login() + await this.user1.logout() + await redis.clearUserSessions(this.user1) + await this.user2.login() + await this.user2.logout() + await redis.clearUserSessions(this.user2) + this.project_id = null }) @@ -346,68 +292,46 @@ describe('Registration', function () { } }) - it('should not allow sign in with secondary email', function (done) { + it('should not allow sign in with secondary email', async function () { const secondaryEmail = 'acceptance-test-secondary@example.com' - this.user1.addEmail(secondaryEmail, err => { - expect(err).to.not.exist - this.user1.loginWith(secondaryEmail, err => { - expect(err).to.match(/login failed: status=401/) - expect(err.info.body).to.deep.equal({ - message: { - type: 'error', - key: 'invalid-password-retry-or-reset', - }, - }) - this.user1.isLoggedIn((err, isLoggedIn) => { - expect(err).to.not.exist - expect(isLoggedIn).to.equal(false) - return done() - }) - }) + await this.user1.addEmail(secondaryEmail) + let error = null + try { + await this.user1.loginWith(secondaryEmail) + } catch (err) { + error = err + } + expect(error).to.be.instanceOf(Error) + expect(error.message).to.match(/login failed: status=401/) + expect(error.info.body).to.deep.equal({ + message: { + type: 'error', + key: 'invalid-password-retry-or-reset', + }, }) + + const isLoggedIn = await this.user1.isLoggedIn() + expect(isLoggedIn).to.equal(false) }) - it('should have user1 login and create a project, which user2 cannot access', function (done) { - let projectId - async.series( - [ - // user1 logs in and creates a project which only they can access - cb => { - this.user1.login(err => { - expect(err).not.to.exist - cb() - }) - }, - cb => { - this.user1.createProject('Private Project', (err, id) => { - expect(err).not.to.exist - projectId = id - cb() - }) - }, - cb => expectProjectAccess(this.user1, projectId, cb), - cb => expectNoProjectAccess(this.user2, projectId, cb), - // should prevent user2 from login/register with user1 email address - cb => { - tryLoginThroughRegistrationForm( - this.user2, - this.user1.email, - 'totally_not_the_right_password', - (err, response, body) => { - expect(err).to.not.exist - expect(body.redir != null).to.equal(false) - expect(body.message != null).to.equal(true) - expect(body.message).to.have.all.keys('type', 'text') - expect(body.message.type).to.equal('error') - cb() - } - ) - }, - // check user still can't access the project - cb => expectNoProjectAccess(this.user2, projectId, done), - ], - done + it('should have user1 login and create a project, which user2 cannot access', async function () { + // user1 logs in and creates a project which only they can access + await this.user1.login() + const projectId = await this.user1.createProject('Private Project') + await expectProjectAccess(this.user1, projectId) + await expectNoProjectAccess(this.user2, projectId) + // should prevent user2 from login/register with user1 email address + const { body } = await tryLoginThroughRegistrationForm( + this.user2, + this.user1.email, + 'totally_not_the_right_password' ) + expect(body.redir != null).to.equal(false) + expect(body.message != null).to.equal(true) + expect(body.message).to.have.all.keys('type', 'text') + expect(body.message.type).to.equal('error') + // check user still can't access the project + await expectNoProjectAccess(this.user2, projectId) }) }) }) diff --git a/services/web/test/acceptance/src/SessionTests.mjs b/services/web/test/acceptance/src/SessionTests.mjs index a106a14368..17a7d82e89 100644 --- a/services/web/test/acceptance/src/SessionTests.mjs +++ b/services/web/test/acceptance/src/SessionTests.mjs @@ -1,558 +1,286 @@ import { expect } from 'chai' -import async from 'async' +import { setTimeout } from 'node:timers/promises' import UserHelper from './helpers/User.mjs' import redis from './helpers/redis.mjs' import UserSessionsRedis from '../../../app/src/Features/User/UserSessionsRedis.mjs' + const rclient = UserSessionsRedis.client() +const UserPromises = UserHelper.promises + describe('Sessions', function () { - beforeEach(function (done) { + beforeEach(async function () { this.timeout(20000) - this.user1 = new UserHelper() - this.site_admin = new UserHelper({ email: 'admin@example.com' }) - async.series( - [cb => this.user1.login(cb), cb => this.user1.logout(cb)], - done - ) + this.user1 = new UserPromises() + this.site_admin = new UserPromises({ email: 'admin@example.com' }) + await this.user1.login() + await this.user1.logout() }) describe('one session', function () { - it('should have one session in UserSessions set', function (done) { - async.series( - [ - next => { - redis.clearUserSessions(this.user1, next) - }, + it('should have one session in UserSessions set', async function () { + await redis.clearUserSessions(this.user1) - // login, should add session to set - next => { - this.user1.login(err => next(err)) - }, + // login, should add session to set + await this.user1.login() - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(1) - expect(sessions[0].slice(0, 5)).to.equal('sess:') - next() - }) - }, + const sessions = await redis.getUserSessions(this.user1) + expect(sessions.length).to.equal(1) + expect(sessions[0].slice(0, 5)).to.equal('sess:') - // should be able to access project list page - next => { - this.user1.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(200) - next() - }) - }, + // should be able to access project list page + const statusCode = await this.user1.getProjectListPage() + expect(statusCode).to.equal(200) - // logout, should remove session from set - next => { - this.user1.logout(err => next(err)) - }, - - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(0) - next() - }) - }, - ], - (err, result) => { - if (err) { - throw err - } - done() - } - ) + // logout, should remove session from set + await this.user1.logout() + const sessionsAfterLogout = await redis.getUserSessions(this.user1) + expect(sessionsAfterLogout.length).to.equal(0) }) - it('should update audit log on logout', function (done) { - async.series( - [ - next => { - redis.clearUserSessions(this.user1, next) - }, + it('should update audit log on logout', async function () { + await redis.clearUserSessions(this.user1) - // login - next => { - this.user1.login(err => next(err)) - }, + // login + await this.user1.login() - // logout, should add logout audit log entry (happens in background) - next => { - this.user1.logout(err => next(err)) - }, + // logout, should add logout audit log entry (happens in background) + await this.user1.logout() - // poll for audit log entry since it's written in the background - next => { - let attempts = 0 - const checkAuditLog = () => { - this.user1.getAuditLogWithoutNoise((error, auditLog) => { - if (error) return next(error) + // poll for audit log entry since it's written in the background + const findAuditLogEntry = async () => { + for (let attempts = 0; attempts < 10; attempts++) { + const auditLog = await this.user1.getAuditLogWithoutNoise() - const logoutEntries = auditLog.filter( - entry => entry.operation === 'logout' - ) + const logoutEntries = auditLog.filter( + entry => entry.operation === 'logout' + ) - // If we found the logout entry, we're done - if (logoutEntries.length > 0) { - expect(logoutEntries.length).to.be.greaterThan(0) - const lastLogout = logoutEntries[logoutEntries.length - 1] - expect(lastLogout.operation).to.equal('logout') - expect(lastLogout.ipAddress).to.exist - expect(lastLogout.initiatorId).to.exist - expect(lastLogout.timestamp).to.exist - return next() - } - - // Otherwise retry up to 10 times - attempts++ - if (attempts >= 10) { - return next( - new Error( - 'Logout audit log entry not found after 10 attempts' - ) - ) - } - - setTimeout(checkAuditLog, 25) - }) - } - checkAuditLog() - }, - ], - (err, result) => { - if (err) { - throw err + // If we found the logout entry, we're done + if (logoutEntries.length > 0) { + return logoutEntries } - done() + + await setTimeout(25) } - ) + + throw new Error('Logout audit log entry not found after 10 attempts') + } + + const logoutEntries = await findAuditLogEntry() + expect(logoutEntries.length).to.be.greaterThan(0) + const lastLogout = logoutEntries[logoutEntries.length - 1] + expect(lastLogout.operation).to.equal('logout') + expect(lastLogout.ipAddress).to.exist + expect(lastLogout.initiatorId).to.exist + expect(lastLogout.timestamp).to.exist }) }) describe('two sessions', function () { beforeEach(function () { // set up second session for this user - this.user2 = new UserHelper() + this.user2 = new UserPromises() this.user2.email = this.user1.email this.user2.emails = this.user1.emails this.user2.password = this.user1.password }) - it('should have two sessions in UserSessions set', function (done) { - async.series( - [ - next => { - redis.clearUserSessions(this.user1, next) - }, + it('should have two sessions in UserSessions set', async function () { + await redis.clearUserSessions(this.user1) - // login, should add session to set - next => { - this.user1.login(err => next(err)) - }, + // login, should add session to set + await this.user1.login() - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(1) - expect(sessions[0].slice(0, 5)).to.equal('sess:') - next() - }) - }, + const sessions1 = await redis.getUserSessions(this.user1) + expect(sessions1.length).to.equal(1) + expect(sessions1[0].slice(0, 5)).to.equal('sess:') - // login again, should add the second session to set - next => { - this.user2.login(err => next(err)) - }, + // login again, should add the second session to set + await this.user2.login() - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(2) - expect(sessions[0].slice(0, 5)).to.equal('sess:') - expect(sessions[1].slice(0, 5)).to.equal('sess:') - next() - }) - }, + const sessions2 = await redis.getUserSessions(this.user1) + expect(sessions2.length).to.equal(2) + expect(sessions2[0].slice(0, 5)).to.equal('sess:') + expect(sessions2[1].slice(0, 5)).to.equal('sess:') - // both should be able to access project list page - next => { - this.user1.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(200) - next() - }) - }, + // both should be able to access project list page + const statusCode1 = await this.user1.getProjectListPage() + expect(statusCode1).to.equal(200) + const statusCode2 = await this.user2.getProjectListPage() + expect(statusCode2).to.equal(200) - next => { - this.user2.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(200) - next() - }) - }, + // logout first session, should remove session from set + await this.user1.logout() - // logout first session, should remove session from set - next => { - this.user1.logout(err => next(err)) - }, + const sessions3 = await redis.getUserSessions(this.user1) + expect(sessions3.length).to.equal(1) - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(1) - next() - }) - }, + // first session should not have access to project list page + const statusCode3 = await this.user1.getProjectListPage() + expect(statusCode3).to.equal(302) - // first session should not have access to project list page - next => { - this.user1.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(302) - next() - }) - }, + // second session should still have access to settings + const statusCode4 = await this.user2.getProjectListPage() + expect(statusCode4).to.equal(200) - // second session should still have access to settings - next => { - this.user2.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(200) - next() - }) - }, + // logout second session, should remove last session from set + await this.user2.logout() - // logout second session, should remove last session from set - next => { - this.user2.logout(err => next(err)) - }, + const sessions4 = await redis.getUserSessions(this.user1) + expect(sessions4.length).to.equal(0) - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(0) - next() - }) - }, - - // second session should not have access to project list page - next => { - this.user2.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(302) - next() - }) - }, - ], - (err, result) => { - if (err) { - throw err - } - done() - } - ) + // second session should not have access to project list page + const statusCode5 = await this.user2.getProjectListPage() + expect(statusCode5).to.equal(302) }) }) describe('three sessions, password reset', function () { beforeEach(function () { // set up second session for this user - this.user2 = new UserHelper() + this.user2 = new UserPromises() this.user2.email = this.user1.email this.user2.emails = this.user1.emails this.user2.password = this.user1.password - this.user3 = new UserHelper() + this.user3 = new UserPromises() this.user3.email = this.user1.email this.user3.emails = this.user1.emails this.user3.password = this.user1.password }) - it('should erase both sessions when password is reset', function (done) { - async.series( - [ - next => { - redis.clearUserSessions(this.user1, next) - }, + it('should erase both sessions when password is reset', async function () { + await redis.clearUserSessions(this.user1) - // login, should add session to set - next => { - this.user1.login(err => next(err)) - }, + // login, should add session to set + await this.user1.login() - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(1) - expect(sessions[0].slice(0, 5)).to.equal('sess:') - next() - }) - }, + const sessions1 = await redis.getUserSessions(this.user1) + expect(sessions1.length).to.equal(1) + expect(sessions1[0].slice(0, 5)).to.equal('sess:') - // login again, should add the second session to set - next => { - this.user2.login(err => next(err)) - }, + // login again, should add the second session to set + await this.user2.login() - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(2) - expect(sessions[0].slice(0, 5)).to.equal('sess:') - expect(sessions[1].slice(0, 5)).to.equal('sess:') - next() - }) - }, + const sessions2 = await redis.getUserSessions(this.user1) + expect(sessions2.length).to.equal(2) + expect(sessions2[0].slice(0, 5)).to.equal('sess:') + expect(sessions2[1].slice(0, 5)).to.equal('sess:') - // login third session, should add the second session to set - next => { - this.user3.login(err => next(err)) - }, + // login third session, should add the second session to set + await this.user3.login() - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(3) - expect(sessions[0].slice(0, 5)).to.equal('sess:') - expect(sessions[1].slice(0, 5)).to.equal('sess:') - next() - }) - }, + const sessions3 = await redis.getUserSessions(this.user1) + expect(sessions3.length).to.equal(3) + expect(sessions3[0].slice(0, 5)).to.equal('sess:') + expect(sessions3[1].slice(0, 5)).to.equal('sess:') - // password reset from second session, should erase two of the three sessions - next => { - this.user2.changePassword(`password${Date.now()}`, err => next(err)) - }, + // password reset from second session, should erase two of the three sessions + await this.user2.changePassword(`password${Date.now()}`) - next => { - redis.getUserSessions(this.user2, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(1) - next() - }) - }, + const sessions4 = await redis.getUserSessions(this.user2) + expect(sessions4.length).to.equal(1) - // users one and three should not be able to access project list page - next => { - this.user1.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(302) - next() - }) - }, + // users one and three should not be able to access project list page + const statusCode1 = await this.user1.getProjectListPage() + expect(statusCode1).to.equal(302) + const statusCode3 = await this.user3.getProjectListPage() + expect(statusCode3).to.equal(302) - next => { - this.user3.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(302) - next() - }) - }, + // user two should still be logged in, and able to access project list page + const statusCode2 = await this.user2.getProjectListPage() + expect(statusCode2).to.equal(200) - // user two should still be logged in, and able to access project list page - next => { - this.user2.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(200) - next() - }) - }, + // logout second session, should remove last session from set + await this.user2.logout() - // logout second session, should remove last session from set - next => { - this.user2.logout(err => next(err)) - }, - - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(0) - next() - }) - }, - ], - (err, result) => { - if (err) { - throw err - } - done() - } - ) + const sessions5 = await redis.getUserSessions(this.user1) + expect(sessions5.length).to.equal(0) }) }) describe('three sessions, sessions page', function () { - beforeEach(function (done) { + beforeEach(async function () { // set up second session for this user - this.user2 = new UserHelper() + this.user2 = new UserPromises() this.user2.email = this.user1.email this.user2.emails = this.user1.emails this.user2.password = this.user1.password - this.user3 = new UserHelper() + this.user3 = new UserPromises() this.user3.email = this.user1.email this.user3.emails = this.user1.emails this.user3.password = this.user1.password - async.series([this.user2.login.bind(this.user2)], done) + await this.user2.login() }) - it('should allow the user to erase the other two sessions', function (done) { - async.series( - [ - next => { - redis.clearUserSessions(this.user1, next) - }, + it('should allow the user to erase the other two sessions', async function () { + await redis.clearUserSessions(this.user1) - // login, should add session to set - next => { - this.user1.login(err => next(err)) - }, + // login, should add session to set + await this.user1.login() - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(1) - expect(sessions[0].slice(0, 5)).to.equal('sess:') - next() - }) - }, + const sessions1 = await redis.getUserSessions(this.user1) + expect(sessions1.length).to.equal(1) + expect(sessions1[0].slice(0, 5)).to.equal('sess:') - // login again, should add the second session to set - next => { - this.user2.login(err => next(err)) - }, + // login again, should add the second session to set + await this.user2.login() - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(2) - expect(sessions[0].slice(0, 5)).to.equal('sess:') - expect(sessions[1].slice(0, 5)).to.equal('sess:') - next() - }) - }, + const sessions2 = await redis.getUserSessions(this.user1) + expect(sessions2.length).to.equal(2) + expect(sessions2[0].slice(0, 5)).to.equal('sess:') + expect(sessions2[1].slice(0, 5)).to.equal('sess:') - // login third session, should add the second session to set - next => { - this.user3.login(err => next(err)) - }, + // login third session, should add the second session to set + await this.user3.login() - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(3) - expect(sessions[0].slice(0, 5)).to.equal('sess:') - expect(sessions[1].slice(0, 5)).to.equal('sess:') - next() - }) - }, + const sessions3 = await redis.getUserSessions(this.user1) + expect(sessions3.length).to.equal(3) + expect(sessions3[0].slice(0, 5)).to.equal('sess:') + expect(sessions3[1].slice(0, 5)).to.equal('sess:') - // check the sessions page - next => { - this.user2.request.get( - { - uri: '/user/sessions', - }, - (err, response, body) => { - expect(err).to.be.oneOf([null, undefined]) - expect(response.statusCode).to.equal(200) - next() - } - ) - }, - - // clear sessions from second session, should erase two of the three sessions - next => { - this.user2.getCsrfToken(err => { - expect(err).to.be.oneOf([null, undefined]) - this.user2.request.post( - { - uri: '/user/sessions/clear', - }, - err => next(err) - ) - }) - }, - - next => { - redis.getUserSessions(this.user2, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(1) - next() - }) - }, - - // users one and three should not be able to access project list page - next => { - this.user1.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(302) - next() - }) - }, - - next => { - this.user3.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(302) - next() - }) - }, - - // user two should still be logged in, and able to access project list page - next => { - this.user2.getProjectListPage((err, statusCode) => { - expect(err).to.equal(null) - expect(statusCode).to.equal(200) - next() - }) - }, - - // logout second session, should remove last session from set - next => { - this.user2.logout(err => next(err)) - }, - - next => { - redis.getUserSessions(this.user1, (err, sessions) => { - expect(err).to.not.exist - expect(sessions.length).to.equal(0) - next() - }) - }, - - // the user audit log should have been updated - next => { - this.user1.getAuditLogWithoutNoise((error, auditLog) => { - expect(error).not.to.exist - expect(auditLog).to.exist - - // find the clear-sessions entry - const clearSessionsEntries = auditLog.filter( - entry => entry.operation === 'clear-sessions' - ) - expect(clearSessionsEntries.length).to.equal(1) - expect(clearSessionsEntries[0].operation).to.equal( - 'clear-sessions' - ) - expect(clearSessionsEntries[0].ipAddress).to.exist - expect(clearSessionsEntries[0].initiatorId).to.exist - expect(clearSessionsEntries[0].timestamp).to.exist - expect(clearSessionsEntries[0].info.sessions.length).to.equal(2) - next() - }) - }, - ], - (err, result) => { - if (err) { - throw err - } - done() - } + // check the sessions page + const { response: sessionsPageResponse } = await this.user2.doRequest( + 'GET', + '/user/sessions' ) + expect(sessionsPageResponse.statusCode).to.equal(200) + + // clear sessions from second session, should erase two of the three sessions + await this.user2.getCsrfToken() + await this.user2.doRequest('POST', '/user/sessions/clear') + const sessions4 = await redis.getUserSessions(this.user2) + expect(sessions4.length).to.equal(1) + + // users one and three should not be able to access project list page + const statusCode1 = await this.user1.getProjectListPage() + expect(statusCode1).to.equal(302) + const statusCode3 = await this.user3.getProjectListPage() + expect(statusCode3).to.equal(302) + + // user two should still be logged in, and able to access project list page + const statusCode2 = await this.user2.getProjectListPage() + expect(statusCode2).to.equal(200) + + // logout second session, should remove last session from set + await this.user2.logout() + const sessions5 = await redis.getUserSessions(this.user1) + expect(sessions5.length).to.equal(0) + + // the user audit log should have been updated + const auditLog = await this.user1.getAuditLogWithoutNoise() + expect(auditLog).to.exist + + // find the clear-sessions entry + const clearSessionsEntries = auditLog.filter( + entry => entry.operation === 'clear-sessions' + ) + expect(clearSessionsEntries.length).to.equal(1) + expect(clearSessionsEntries[0].operation).to.equal('clear-sessions') + expect(clearSessionsEntries[0].ipAddress).to.exist + expect(clearSessionsEntries[0].initiatorId).to.exist + expect(clearSessionsEntries[0].timestamp).to.exist + expect(clearSessionsEntries[0].info.sessions.length).to.equal(2) }) }) diff --git a/services/web/test/acceptance/src/helpers/redis.mjs b/services/web/test/acceptance/src/helpers/redis.mjs index 3070aceaed..17c70702be 100644 --- a/services/web/test/acceptance/src/helpers/redis.mjs +++ b/services/web/test/acceptance/src/helpers/redis.mjs @@ -1,49 +1,26 @@ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -import Async from 'async' import UserSessionsRedis from '../../../../app/src/Features/User/UserSessionsRedis.mjs' -// rclient = redis.createClient(Settings.redis.web) const rclient = UserSessionsRedis.client() -export default { - getUserSessions(user, callback) { - if (callback == null) { - callback = function () {} - } - return rclient.smembers( - UserSessionsRedis.sessionSetKey(user), - (err, result) => callback(err, result) - ) - }, - - clearUserSessions(user, callback) { - if (callback == null) { - callback = function () {} - } - const sessionSetKey = UserSessionsRedis.sessionSetKey(user) - return rclient.smembers(sessionSetKey, (err, sessionKeys) => { - if (err) { - return callback(err) - } - if (sessionKeys.length === 0) { - return callback(null) - } - const actions = sessionKeys.map(k => cb => rclient.del(k, err => cb(err))) - return Async.series(actions, () => - rclient.srem(sessionSetKey, sessionKeys, err => { - if (err) { - return callback(err) - } - return callback(null) - }) - ) - }) - }, +async function getUserSessions(user) { + return await rclient.smembers(UserSessionsRedis.sessionSetKey(user)) } + +async function clearUserSessions(user) { + const sessionSetKey = UserSessionsRedis.sessionSetKey(user) + const sessionKeys = await rclient.smembers(sessionSetKey) + if (sessionKeys.length === 0) { + return + } + for (const k of sessionKeys) { + await rclient.del(k) + } + await rclient.srem(sessionSetKey, sessionKeys) +} + +const RedisHelper = { + getUserSessions, + clearUserSessions, +} + +export default RedisHelper