| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- import { vi } from 'vitest'
- import sinon from 'sinon'
- const modulePath =
- '../../../../app/src/Features/Security/RateLimiterMiddleware.mjs'
- describe('RateLimiterMiddleware', function () {
- beforeEach(async function (ctx) {
- ctx.SessionManager = {
- getLoggedInUserId: () => ctx.req.session?.user?._id,
- }
- vi.doMock('@overleaf/settings', () => ({
- default: (ctx.settings = {}),
- }))
- vi.doMock('../../../../app/src/Features/Security/LoginRateLimiter', () => ({
- default: {},
- }))
- vi.doMock(
- '../../../../app/src/Features/Authentication/SessionManager',
- () => ({
- default: ctx.SessionManager,
- })
- )
- ctx.RateLimiterMiddleware = (await import(modulePath)).default
- ctx.req = { params: {} }
- ctx.res = {
- status: sinon.stub(),
- write: sinon.stub(),
- end: sinon.stub(),
- }
- ctx.next = sinon.stub()
- })
- describe('rateLimit', function () {
- beforeEach(function (ctx) {
- ctx.projectId = 'project-id'
- ctx.docId = 'doc-id'
- ctx.rateLimiter = {
- consume: sinon.stub().resolves({ remainingPoints: 2 }),
- }
- ctx.middleware = ctx.RateLimiterMiddleware.rateLimit(ctx.rateLimiter, {
- params: ['projectId', 'docId'],
- })
- ctx.req.params = { projectId: ctx.projectId, docId: ctx.docId }
- })
- describe('when there is no session', function () {
- beforeEach(async function (ctx) {
- await new Promise(resolve => {
- ctx.req.ip = ctx.ip = '1.2.3.4'
- ctx.middleware(ctx.req, ctx.res, () => {
- resolve()
- })
- })
- })
- it('should call the rate limiter with the ip address', function (ctx) {
- ctx.rateLimiter.consume.should.have.been.calledWith(
- `${ctx.projectId}:${ctx.docId}:${ctx.ip}`
- )
- })
- })
- describe('when smoke test user', function () {
- beforeEach(async function (ctx) {
- await new Promise(resolve => {
- ctx.userId = 'smoke-test-user-id'
- ctx.req.session = {
- user: { _id: ctx.userId },
- }
- ctx.settings.smokeTest = { userId: ctx.userId }
- ctx.middleware(ctx.req, ctx.res, () => {
- resolve()
- })
- })
- })
- it('should not call the rate limiter', function (ctx) {
- ctx.rateLimiter.consume.should.not.have.been.called
- })
- })
- describe('when under the rate limit with logged in user', function () {
- beforeEach(async function (ctx) {
- await new Promise(resolve => {
- ctx.userId = 'user-id'
- ctx.req.session = {
- user: { _id: ctx.userId },
- }
- ctx.middleware(ctx.req, ctx.res, () => {
- resolve()
- })
- })
- })
- it('should call the rate limiter backend with the userId', function (ctx) {
- ctx.rateLimiter.consume.should.have.been.calledWith(
- `${ctx.projectId}:${ctx.docId}:${ctx.userId}`
- )
- })
- })
- describe('when under the rate limit with anonymous user', function () {
- beforeEach(async function (ctx) {
- await new Promise(resolve => {
- ctx.req.ip = '1.2.3.4'
- ctx.middleware(ctx.req, ctx.res, () => {
- resolve()
- })
- })
- })
- it('should call the rate limiter backend with the ip address', function (ctx) {
- ctx.rateLimiter.consume.should.have.been.calledWith(
- `${ctx.projectId}:${ctx.docId}:${ctx.req.ip}`
- )
- })
- })
- describe('when over the rate limit', function () {
- beforeEach(async function (ctx) {
- await new Promise(resolve => {
- ctx.userId = 'user-id'
- ctx.req.session = {
- user: { _id: ctx.userId },
- }
- ctx.res.end.callsFake(() => {
- resolve()
- })
- ctx.rateLimiter.consume.rejects({ remainingPoints: 0 })
- ctx.middleware(ctx.req, ctx.res, ctx.next)
- })
- })
- it('should return a 429', function (ctx) {
- ctx.res.status.should.have.been.calledWith(429)
- })
- it('should not continue', function (ctx) {
- ctx.next.should.not.have.been.called
- })
- })
- })
- })
|